Mysql next-key-lock锁定范围分析

1,133 阅读2分钟

建表

        CREATE TABLE `lock_test` (
            `id` bigint(20) NOT NULL,
            `value` varchar(255) NOT NULL,
            PRIMARY KEY (`id`),
            KEY `value_index` (`value`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁测试表'
id主键, value 二级索引

next-key-lock

表中数据

id value
10 b
20 f
30 g
40 j
50 m
60 p
70 r
80 y

主键索引结构

辅助索引结构

当前环境 where非聚集索引, 隔离级别RR

1. next-key-lock锁定范围测试

TranscationA TranscationB
begin begin
select * from lock_test where value = 'j' for update
insert into lock_test (id, value) VALUES (29, 'g')

当前情况下事务A锁定的是(g,30)~(40, j) ~ (50,m)

从辅助索引结构来看是

首先要理解辅助索引的排序规则,先按照索引字段排序,索引字段相同按照主键id排序, 因此从上面的insert来看可知(g, 29) 是插入到 (g, 30)前面,因此无需阻塞

对于下面事务

TranscationA TranscationB
begin begin
select * from lock_test where value = 'j' for update
insert into lock_test (id, value) VALUES (31, 'g')

事务B会阻塞,因为根据辅助索引排序规则,(g, 31) 需要插入到锁定范围内,因此会阻塞

insert into lock_test (id, value) VALUES (49, 'm')
insert into lock_test (id, value) VALUES (51, 'm')
这两条哪个可以执行,哪条阻塞大家可以自己分析下

总结 :next-key-lock锁住的是前后的一个范围,根据索引 向前找到第一个存在的值,向后找第一个存在的值 这个区间范围进行锁定,具体锁定范围需要根据主键配合确定


当前环境 where主键索引, 隔离级别RR

2.主键聚集索引测试
TranscationA TranscationB
begin begin
select * from lock_test where id = 40 for update
insert into lock_test (id, value) VALUES (31, 'g')

因为where条件是主键索引,不会重复,最多只能筛选出1条,因此只对id=40加一个行锁,非此行操作均不阻塞

TranscationA TranscationB
begin begin
insert into lock_test (id, value) VALUES (23, 'b')
select * from lock_test where value = 'b' for update
commit
select * from lock_test where value = 'b' for update
select * from lock_test where value = 'b'
mvcc读无法读到 事务B的提交数据