实践基于以下表结构,分析各个情况下加锁:
1 | CREATE TABLE `tablet` ( |
唯一主键加锁
先执行session1,query ok :
1 | use souche_study; |
再执行session2,query ok:
1 | use souche_study; |
再执行session3,query blocked:
1 | use souche_study; |
唯一主键等值查询不加gap间隙锁,只加对应值的行锁。
这里将session1换成:
1 | use souche_study; |
session2,query ok:
1 | use souche_study; |
session3,query blocked:
1 | use souche_study; |
session4,query blocked:
1 | use souche_study; |
这里如果我们将session1中id<15加上一个等号条件也就是id<=15,mysql后在id=20这行也加上锁,同时多了一个gap锁也就是多了一整个next key lock (15,20],这是相当奇怪的 ,因为扫描到15的时候可以确定不再往后进行扫描。这种貌似可以优化的场景但是并没有进行优化。
最终上面的test发现会加id为10记录的行锁+(10,15]的next key lock。总结下,mysql在唯一主键索引查询的条件为范围索引的条件下会默认向后多加一个next key lock,等值查询直加行锁。
lock in share mode 这种方式只会在索引上加锁,在不回表的情况下不会锁主键索引记录。for update 的方式不管是否回表都会锁主键记录。
唯一普通索引加范围锁
唯一普通索引有独立的树结构,在加锁上跟主键的索引是有区别的,防止根据主键更新造成不一致读会加行锁。
将c修改为唯一索引,执行下面的session1:
1 | use souche_study; |
然后执行session2,query blocked :
1 | use souche_study; |
唯一索引范围锁搜索到不符合预期范围的第一个值,以该值为最终值进行加锁,上面的语句如果改成c<15那么锁范围变成(10,15]。
非唯一普通索引加范围锁
类比上面的情况多了一个next key lock,执行session1:
1 | use souche_study; |
执行session2:
1 | use souche_study; |
非唯一索引范围锁搜索到不符合预期范围的第一个值,以该值为最终值进行加锁,上面的语句如果改成c<15那么锁范围变成(10,15]。这里并不会在右边多加间隙锁。
####
非唯一普通索引加范围锁倒序
倒序排序默认会从后往前扫描索引,mysql默认多加一个间隙锁。
1 | -- session1 |
1 | -- session2 |
总结
总结一下,分析加锁情况主要需要区分出唯一索引和非唯一索引。唯一索引是左半区或者右半区可能插入,非唯一索引是左右半区都可能插入。并且有几个原则和几个优化,原则:
1.加锁的基本单位是next key lock,前开后闭区间。
2.mysql扫描到的对象才会进行加锁。
优化:
1.索引的唯一等值查询,next key lock退化为行锁。(非唯一等值查询不退化)
2.等值查询或者范围查询向右遍历到最后一个不符合期望的值,即使是非唯一索引也不会在该值的右侧加一个间隙锁。