我不确定为什么会发生以下行为。我认为它必须与差距锁定/下一键锁定有关。文档中提到了它们,但解释不详细。
-- isolation level is Repeatable Read
-- create the table
create table t (id int primary key auto_increment, COL1 int, key idx_a(COL1));
insert into t (COL1) values(5), (10), (11), (13), (20);
select * from t;
---- ----
id | COL1
---- ----
1 | 5
2 | 10
3 | 11
4 | 13
5 | 20
-- in transaction 1
select * from t where COL1 = 13 for update;
-- in transaction 2
insert into t (COL1) values(10); -- success
insert into t (COL1) values(11); -- blocks
insert into t (COL1) values(12); -- blocks
.
.
insert into t (COL1) values(19); -- blocks
insert into t (COL1) values(20); -- success
-- in transaction 3
update t set COL1 = 11 where COL1 = 10; -- success
update t set COL1 = 12 where COL1 = 10; -- blocks
.
.
update t set COL1 = 20 where COL1 = 10; -- blocks
update t set a = 21 where a = 10; -- success
看来:
COL1的INSERT锁定,其值为[11,20)(11到20,不包括20)
COL1的更新锁定为(11,20]中的值(11到20,不包括11)
我想知道为什么MySQL这样表现?这种锁定的一般规则是什么?
答案 0 :(得分:0)
根据MySQL 5.7 InnoDB可重复读取
由于COL1具有非唯一索引,
select * from t where COL1 = 13 for update;
该语句锁定id = 4的集群索引B + Tree记录,还锁定具有COL1 = 10的记录的COL1索引B + Tree,并锁定(k = 11,id = 3的COL1索引B + Tree )到(k = 20,id = 5)并带有间隙锁定。
insert into t (COL1) values(10); equals to : insert into t (id, COL1) values(6,10); gap lock
insert into t (COL1) values(11); equals to : insert into t (id, COL1) values(7,11); gap lock
insert into t (COL1) values(12); equals to : insert into t (id, COL1) values(8,12); gap lock
insert into t (COL1) values(19); gap lock
insert into t (COL1) values(20); equals to : insert into t (id, COL1) values(9,20);
但请参阅以下条件:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_k` (`k`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
然后插入:
insert into t values(2,2),(6,6);
1, insert into t values (1,2); success
2, insert into t values (3,2); block
3, insert into t values (5,6); block
4, insert into t values (7,6); success
它们是否都可以在k'B + Tree中插入这些值的位置?
因此您可以看到(k = 2,id = 1)可以插入,因为它不在间隙中,还可以插入值(k = 6,id = 7),(k = 2,id = 3)和( k = 6,id = 5)无法插入,因为它们在间隙中。
答案 1 :(得分:0)
select * from t where COL1 = 13 for update;
此sql子句将锁定范围:
((11,3), (13,4)]
((13,4), (20,5)]
所以,我将在您的交易2和3中说明结果
insert into t (COL1) values(10); -- success because (10, 6) not in the gap range.
insert into t (COL1) values(11); -- block because (11, 7) in the gap range.
insert into t (COL1) values(12); -- block because (12, 8) in the gap range.
insert into t (COL1) values(19); -- block because (19, 9) in the gap range.
insert into t (COL1) values(20); -- success because (20, 10) not in the gap range.
update t set COL1 = 11 where COL1 = 10; --success because (11,2) not in the gap range.
update t set COL1 = 12 where COL1 = 10; -- blocks because (12,2) in the gap range.
update t set COL1 = 20 where COL1 = 10; -- blocks because (20,2) in the gap range.
update t set COL1 = 21 where COL1 = 10; -- success because (21,2) not in the gap range.
答案 2 :(得分:0)
执行后
create table t (id int primary key auto_increment, COL1 int, key idx_a(COL1));
insert into t (COL1) values(5), (10), (11), (13), (20);
-- transaction 1
start transaction;
select * from t where COL1 = 13 for update;
select * from performance_schema.data_locks
的输出是:
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| INNODB | 140043377180872:1075:140043381460688 | 2368 | 49 | 180 | test | t | NULL | NULL | NULL | 140043381460688 | TABLE | IX | GRANTED | NULL |
| INNODB | 140043377180872:14:5:5:140043381457776 | 2368 | 49 | 180 | test | t | NULL | NULL | idx_a | 140043381457776 | RECORD | X | GRANTED | 13, 4 |
| INNODB | 140043377180872:14:4:5:140043381458120 | 2368 | 49 | 180 | test | t | NULL | NULL | PRIMARY | 140043381458120 | RECORD | X,REC_NOT_GAP | GRANTED | 4 |
| INNODB | 140043377180872:14:5:6:140043381458464 | 2368 | 49 | 180 | test | t | NULL | NULL | idx_a | 140043381458464 | RECORD | X,GAP | GRANTED | 20, 5 |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
事务1持有下一键锁定((11,3),(13,4)]和间隙锁定((13,4),(20,5))。
insert into t (COL1) values(10)
和insert into t (COL1) values(20)
等于insert into t (COL1, id) values(10, ?)
,并且?
必须大于5,因此(10,?)和(20,?)都不在((11,3),(13,4)]或((13,4),(20,5)),这就是它们能够成功的原因。insert into t (COL1) values(11)
至insert into t (COL1) values(19)
,它们位于( (11,3),(13,4)]或((13,4),(20,5)),这就是它们被阻止的原因。
更新就像删除然后插入。 update t set COL1 = 11 where COL1 = 10
将插入((11,2),(11,2)不在((11,3),(13,4)]或((13,4),(20,5))中,即update t set COL1 = 12 where COL1 = 10
至update t set COL1 = 20 where COL1 = 10
将插入(?,2),而?
在[12,20]中,所以(?,2)在((11,3)中,(13,4)]或((13,4),(20,5)),这就是它们被阻止的原因。我认为update t set a = 21 where a = 10
应该为update t set COL1 = 21 where COL1 = 10
,它将插入(21,2 ),(21,2)不在((11,3),(13,4)]或((13,4),(20,5))中,这就是它成功的原因。