mysql间隙锁加锁11个规则间隙锁是在可重复读隔离级别下才会生效的:next-keylock实际上是由间隙锁加行锁实现的,如果切换到读提交隔离级别(read-committed)的话,就好理解了,过程中去掉间隙锁的部分,也就是只剩下 ...
mysql 间隙锁加锁11个规则间隙锁是在可重复读隔离级别下才会生效的: next-key lock 实际上是由间隙锁加行锁实现的,如果切换到读提交隔离级别 (read-committed) 的话,就好理解了,过程中去掉间隙锁的部分,也就是只剩下行锁的部分。而在读提交隔离级别下间隙锁就没有了,为了解决可能出现的数据和日志不一致问题,需要把binlog 格式设置为 row 。也就是说,许多公司的配置为:读提交隔离级别加 binlog_format=row。业务不需要可重复读的保证,这样考虑到读提交下操作数据的锁范围更小(没有间隙锁),这个选择是合理 next-key lock的加锁规则 总结的加锁规则里面,包含了两个 “ “ 原则 ” ” 、两个 “ “ 优化 ” ” 和一个 “bug” 。 案例分析我们以表test作为例子,建表语句和初始化语句如下:其中id为主键索引 CREATE TABLE `test` ( id` int(11) NOT NULL, col1` int(11) DEFAULT NULL, col2` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `c` (`col1`) ) ENGINE=InnoDB; insert into test values(0,0,0),(5,5,5), (10,10,10),(15,15,15),(20,20,20),(25,25,25); 登录后复制 案例一:唯一索引等值查询间隙锁由于表 test 中没有 id=7 的记录 根据原则 1 ,加锁单位是 next-key lock , session A 加锁范围就是 (5,10] ; 同时根据优化 2 ,这是一个等值查询 (id=7) ,而 id=10 不满足查询条件, next-key lock 退化成间隙锁,因此最终加锁的范围是 (5,10) 案例二:非唯一索引等值查询锁这里 session A 要给索引 col1 上 col1=5 的这一行加上读锁. 根据原则 1 ,加锁单位是 next-key lock ,左开右闭,5是闭上的,因此会给 (0,5]加上 next-key lock 要注意 c 是普通索引,因此仅访问 c=5 这一条记录是不能马上停下来的(可能有col1=5的其他记录), 需要向右遍历,查到c=10 才放弃。根据第二个原则,需要对所有访问进行加锁,因此需要给区间(5,10]加上next-key lock。 但是同时这个符合优化 2 :等值判断,向右遍历,最后一个值不满足 col1=5 这个等值条件,因此退化成间隙锁 (5,10) 。 根据原则 2 , 只有访问到的对象才会加锁,这个查询使用覆盖索引,并不需要访问主键索引, 因此,在主键索引上没有添加任何锁,这解释了为什么会话B的更新语句可以执行成功。 但 session C 要插入一个 (7,7,7) 的记录,就会被 session A 的间隙锁 (5,10) 锁住 这个例子说明,锁是加在索引上的。 执行 for update 时,系统会认为你接下来要更新数据,因此会顺便给主键索引上满足条件的行加上行锁。 如果你要用 lock in share mode来给行加读锁避免数据被更新的话,就必须得绕过覆盖索引的优化,因为覆盖索引不会访问主键索引,不会给主键索引上加锁 案例三:主键索引范围查询锁 开始执行的时候,要找到第一个 id=10 的行,因此本该是 next-key lock(5,10] 。 根据优化 1 ,主键 案例四:非唯一索引范围查询锁 在第一次用 col1=10 定位记录的时候,索引 c 上加了 (5,10] 这个 next-key lock 后,由于索引 col1 是非唯 案例五:唯一索引范围查询锁 bug session A 是一个范围查询,按照原则 1 的话,应该是索引 id 上只加 (10,15] 这个 next-key lock ,并且因 案例六:非唯一索引上存在 " " 等值 " " 的例子这里,我给表 t 插入一条新记录:insert into t values(30,10,30);也就是说,现在表里面有两个c=10的行 这次我们用 delete 语句来验证。注意, delete 语句加锁的逻辑,其实跟 select ... for update 是类似的, 这个 delete 语句在索引 c 上的加锁范围,就是上面图中蓝色区域覆盖的部分。这个蓝色区域左右两边都 案例七: limit 语句加锁 session A 的 delete 语句加了 limit 2 。你知道表 t 里 c=10 的记录其实只有两条,因此加不加 limit 2 ,删 这个例子对我们实践的指导意义就是, 在删除数据的时候尽量加 limit 。 案例八:一个死锁的例子 session A 启动事务后执行查询语句加 lock in share mode ,在索引 col1 上加了 next-keylock(5,10] 和 案例九:order by索引排序的间隙锁1如下面一条语句 首先这个查询语句的语义是 order by id desc ,要拿到满足条件的所有行,优化器必须先找到 “ 第 案例十:order by索引排序的间隙锁2由于是 order by col1 desc ,第一个要定位的是索引 col1 上 “ 最右边的 ”col1=20 的行。这是一个非唯一索引的等值查询: 首先在左侧开区间加入 next-key 锁定,形成 (15,20] 区间。 向右遍历,col1=25不满足条件,退化为间隙锁 所以会加上间隙锁(20,25) 和 next-key lock (15,20] 。 遍历到 col1=10 时停止向左扫描索引。下一个键锁定(next-keylock)会被应用于右开左闭区间 (5,10) 这正是阻塞session B 的 insert 语句的原因。在扫描过程中, col1=20 、 col1=15 、 col1=10 这三行都存在值,由于是 select * ,所以会在主键 id 上加三个行锁。 因此, session A 的 select 语句锁的范围就是: 案例十一:update修改数据的例子-先插入后删除 注意:根据 col1>5 查到的第一个记录是 col1=10 ,因此不会加 (0,5] 这个 next-key lock 。 好,接下来 session B 要执行 update t set col1 = 5 where col1 = 1 这个语句了,一样地可以拆成两步: 以上就是mysql间隙锁加锁的规则有哪些的详细内容,更多请关注模板之家(www.mb5.com.cn)其它相关文章! |