多个 SELECT ... FOR UPDATE with a delayed INSERT INTO
Multiple SELECT ... FOR UPDATE with a delayed INSERT INTO
我遇到了 SELECT 的问题 ... FOR UPDATE 和 INSERT INTO 语句在单独的连接上死锁。
给定一个空 table tblFoo
和主键 id
,考虑以下伪代码:
function locate(array values) {
BEGIN TRANSACTION;
rows = SELECT * FROM tblFoo WHERE id IN values FOR UPDATE;
if (rows is empty) {
sleep(10); // i.e., do some stuff
rows = INSERT INTO tblFoo (id) VALUES values;
}
COMMIT;
return rows;
}
在进程 A @ t=0 上:return locate([1,2,3]);
在进程 B @ t=1 上:return locate([1]);
我的期望是进程 1 将与 id
s 1、2、3 锁定行,这会在 SELECT ... FOR UPDATE
处阻塞进程 B,直到提交进程 A 的事务。提交后,进程 B 将解除阻塞,并且 returns 刚被进程 A 插入的带有 id
1 的行。
观察到的行为是遇到死锁,导致进程A回滚,进程B插入一行id
1.
谁能帮我理解为什么 MySQL 会这样?
我正在使用带有 MySQL 5.5 版的 innoDB。
编辑:下面是table结构
CREATE TABLE `tblFoo` (
`id` INT(11) NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
;
编辑 2:以下是详细说明死锁的 innoDB 状态
------------------------
LATEST DETECTED DEADLOCK
------------------------
161205 15:55:50
*** (1) TRANSACTION:
TRANSACTION 32A3E743A, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 12243323, OS thread handle 0x7fd7dd47f700, query id 4713227035 localhost root update
INSERT INTO test.tblFoo (id) VALUES (1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 1556528 n bits 72 index `PRIMARY` of table `test`.`tblFoo` trx id 32A3E743A lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00032a3e5f6b; asc *>_k;;
2: len 7; hex b30017d06b0110; asc k ;;
*** (2) TRANSACTION:
TRANSACTION 32A3E5FD3, ACTIVE 5 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1248, 5 row lock(s)
MySQL thread id 12243319, OS thread handle 0x7fd7f0097700, query id 4713230393 localhost root update
INSERT INTO test.tblFoo (id) VALUES (1),(2),(3)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 1556528 n bits 72 index `PRIMARY` of table `test`.`tblFoo` trx id 32A3E5FD3 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00032a38e424; asc *8 $;;
2: len 7; hex cc001c166a0110; asc j ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00032a3e5f6b; asc *>_k;;
2: len 7; hex b30017d06b0110; asc k ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 1556528 n bits 72 index `PRIMARY` of table `test`.`tblFoo` trx id 32A3E5FD3 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00032a3e5f6b; asc *>_k;;
2: len 7; hex b30017d06b0110; asc k ;;
*** WE ROLL BACK TRANSACTION (2)
我认为发生的事情是,您开始了 2 笔交易。两者都在同一个 table 上获得了 "select ... for update"。您会期望第二个事务在更新之前等待。但是从文档 https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html 看来它不会(这就是您看到的行为)。所以两者 "selects ... for update" 互相阻塞,让你陷入僵局。
我遇到了 SELECT 的问题 ... FOR UPDATE 和 INSERT INTO 语句在单独的连接上死锁。
给定一个空 table tblFoo
和主键 id
,考虑以下伪代码:
function locate(array values) {
BEGIN TRANSACTION;
rows = SELECT * FROM tblFoo WHERE id IN values FOR UPDATE;
if (rows is empty) {
sleep(10); // i.e., do some stuff
rows = INSERT INTO tblFoo (id) VALUES values;
}
COMMIT;
return rows;
}
在进程 A @ t=0 上:return locate([1,2,3]);
在进程 B @ t=1 上:return locate([1]);
我的期望是进程 1 将与 id
s 1、2、3 锁定行,这会在 SELECT ... FOR UPDATE
处阻塞进程 B,直到提交进程 A 的事务。提交后,进程 B 将解除阻塞,并且 returns 刚被进程 A 插入的带有 id
1 的行。
观察到的行为是遇到死锁,导致进程A回滚,进程B插入一行id
1.
谁能帮我理解为什么 MySQL 会这样?
我正在使用带有 MySQL 5.5 版的 innoDB。
编辑:下面是table结构
CREATE TABLE `tblFoo` (
`id` INT(11) NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
;
编辑 2:以下是详细说明死锁的 innoDB 状态
------------------------
LATEST DETECTED DEADLOCK
------------------------
161205 15:55:50
*** (1) TRANSACTION:
TRANSACTION 32A3E743A, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 12243323, OS thread handle 0x7fd7dd47f700, query id 4713227035 localhost root update
INSERT INTO test.tblFoo (id) VALUES (1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 1556528 n bits 72 index `PRIMARY` of table `test`.`tblFoo` trx id 32A3E743A lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00032a3e5f6b; asc *>_k;;
2: len 7; hex b30017d06b0110; asc k ;;
*** (2) TRANSACTION:
TRANSACTION 32A3E5FD3, ACTIVE 5 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1248, 5 row lock(s)
MySQL thread id 12243319, OS thread handle 0x7fd7f0097700, query id 4713230393 localhost root update
INSERT INTO test.tblFoo (id) VALUES (1),(2),(3)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 1556528 n bits 72 index `PRIMARY` of table `test`.`tblFoo` trx id 32A3E5FD3 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00032a38e424; asc *8 $;;
2: len 7; hex cc001c166a0110; asc j ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00032a3e5f6b; asc *>_k;;
2: len 7; hex b30017d06b0110; asc k ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 1556528 n bits 72 index `PRIMARY` of table `test`.`tblFoo` trx id 32A3E5FD3 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00032a3e5f6b; asc *>_k;;
2: len 7; hex b30017d06b0110; asc k ;;
*** WE ROLL BACK TRANSACTION (2)
我认为发生的事情是,您开始了 2 笔交易。两者都在同一个 table 上获得了 "select ... for update"。您会期望第二个事务在更新之前等待。但是从文档 https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html 看来它不会(这就是您看到的行为)。所以两者 "selects ... for update" 互相阻塞,让你陷入僵局。