I have a java application, and it occurs dead lock exception on the sql below:
insert into voucher (
id,
order_id,
voucher_code
) SELECT
#{id},
#{orderId},
#{voucherCode}
FROM DUAL WHERE NOT EXISTS (SELECT id FROM voucher where order_id = #{orderId})
The order_id is unique key.
And I am sure that it will be deadlocked when the sql is executed on concurrency.
However, I dont have permission enough to execute show engine innodb status, so I fail to get information about the deadlock exception.
I try to reproduce the problem in a lab environment. table test is below:
Create Table: CREATE TABLE `test` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`info` varchar(128) NOT NULL DEFAULT '',
`order_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `uni_order_id` (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8
and I execute two sqls in two different sessions:
insert into test(info,order_id) select '12345',sleep(10) from dual where not exists (select info from test where info='12345');
insert into test(info,order_id) select '12345',234 from dual where not exists (select info from test where info='12345');
deadlock log is below:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-06-20 17:26:54 700000a83000
*** (1) TRANSACTION:
TRANSACTION 2321, ACTIVE 9 sec inserting
mysql tables in use 2, locked 2
LOCK WAIT 5 lock struct(s), heap size 1184, 2 row lock(s)
MySQL thread id 1, OS thread handle 0x700000a3f000, query id 29 localhost root executing
insert into test(info,order_id) select '12345',234 from dual where not exists (select info from test where info='12345')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 9 page no 3 n bits 72 index `PRIMARY` of table `test`.`test` trx id 2321 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 2320, ACTIVE 10 sec setting auto-inc lock
mysql tables in use 2, locked 2
3 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 2, OS thread handle 0x700000a83000, query id 28 localhost root User sleep
insert into test(info,order_id) select '12345',sleep(10) from dual where not exists (select info from test where info='12345')
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 9 page no 3 n bits 72 index `PRIMARY` of table `test`.`test` trx id 2320 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table `test`.`test` trx id 2320 lock mode AUTO-INC waiting
*** WE ROLL BACK TRANSACTION (2)
I supposed that the subquery select info from test where info=‘12345’ may hold SLock, and insert into … select want XLock.
But I fail to find the offical document to support my point.
So my question is below:
1. is my reproduce design right?
2. is my supposal(the subquery select info from test where info=‘12345’ may hold SLock) right? Any offical document can support my supposal?
When you run INSERT ... SELECT, by default MySQL is locking all rows in SELECT.
If you change isolation level to READ-COMMITTED, then rows in SELECT is not locked. In your case this should solve deadlock problem.
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
PS:I suggest to read about isolation levels and understand differences between them.
http://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
INSERT INTO T SELECT ... FROM S WHERE ... sets an exclusive index record lock (without a gap lock) on each row inserted into T. If the transaction isolation level is READ COMMITTED, or innodb_locks_unsafe_for_binlog is enabled and the transaction isolation level is not SERIALIZABLE, InnoDB does the search on S as a consistent read (no locks). Otherwise, InnoDB sets shared next-key locks on rows from S. InnoDB has to set locks in the latter case: In roll-forward recovery from a backup, every SQL statement must be executed in exactly the same way it was done originally.
CREATE TABLE ... SELECT ... performs the SELECT with shared next-key locks or as a consistent read, as for INSERT ... SELECT.
When a SELECT is used in the constructs REPLACE INTO t SELECT ... FROM s WHERE ... or UPDATE t ... WHERE col IN (SELECT ... FROM s ...), InnoDB sets shared next-key locks on rows from table s.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With