线上MySQL经常会出现锁引起的故障,研发和运维经常互相甩锅

分两期探讨一下MySQL中非常关键的行锁如何加锁
本期:讲结论和理论基础
下期:通过实验来证明结论
核心要点

本期要点:
1. 结论
2. 如何加行锁?
3. 行锁的分类
下期预告:实验证明
1. 读提交
有索引
无索引
2. 可重复读
唯一索引
非唯一索引
无索引
老规矩,先把结论给出,后面实验证明
结论
不同的隔离级别,有无索引,最终的加锁结果不一样。
读提交(RC)
1
有索引
锁类型为记录锁,只对符合查询条件的记录加记录锁。
2
无索引
select ... for update,先对全表数据顺序加锁,再判断是否符合查询要求,符合的锁就保留,不符合的就释放,相当于表锁。
update,先查找出符合要求的记录再加锁更新
可重复读(RR)
可重复读的隔离级别下,行级锁加锁的基本单位是next-key lock。但是,在不同的索引类型以及是否使用索引的不同情况下,next-key lock会相应的退化成记录锁或间隙锁
1
唯一索引
等值查询
查询记录【存在】,索引上的next-key lock退化成记录锁
查询记录【不存在】,扫描到第一条大于该查询条件的记录后,将该记录的next-key lock退化成间隙锁
范围查询
大于等于(>=): 记录【存在】,主键索引会退化成记录锁
小于或小于等于(<|<=): 记录【不存在】,终止扫描行的记录上的主键索引会退化成间隙锁。
小于(<): 记录【存在】,该记录的主键索引上的锁会退化成间隙锁
2
非唯一索引
等值查询
查询记录【存在】,由于不是唯一索引,因此可能存在多个相同索引值的情况。于是,非唯一索引等值查询是一个扫描过程,直到扫描到第一个不满足查询条件的记录才停止扫描。对于扫描到的二级索引记录加next-key lock, 对于扫描到的第一个不满足查询条件的记录,二级索引会退化成间隙锁。同时,对于符合查询条件的记录的主键索引加记录锁。
查询记录【不存在】,扫描到第一条不符合查询条件的记录后,停止扫描。同时,将扫描到的记录二级索引的next-key lock退化成间隙锁。因为不存在符合查询条件的记录,所以不会对主键加记录锁。
范围查询
所有扫描到的数据都会加 next_key lock ,不存在锁退化的情况。同时,所有符合查询条件的记录还会在主键索引上加记录锁。
3
无索引
如果查询条件没有用到索引列或者索引失效,会导致全表扫描。在每条记录的主键索引上,加一把next-key lock,这就相当于锁住了全表。其他事务,对该表的增、删、改 都会被阻塞。
如何加行锁?
MySQL两种常用的存储引擎(MyISAM和InnoDB)
MyISAM不支持行锁,
行锁都是用于InnoDB引擎。
普通的select语句属于快照读,是不会对记录加锁的,MVCC(事务多版本控制)主要是针对快照读。(之后再专门写一篇关于MVCC的)
锁定读有两种:
对读取的记录加共享锁(S型锁)select ... lock in share mode
对读取的记录加独占锁(X型锁)select ... for update
要实现以上两种锁定读,需要在事务中实现,当事务提交了,锁就会被释放,需要在语句执行前使用begin 或 start transaction 开启事务。
update 和 delete 操作也都会加行级锁,并且都是独占锁(X型锁)
共享锁(S锁)读读共享,读写互斥;
独占锁(X锁)写写互斥,读写互斥;

行锁的分类
行锁分三类
Record Lock,记录锁,只锁定一条记录
Gap Lock,间隙锁,锁定一个范围,但不包含记录本身
Next-Key Lock,Record Lock+Gap Lock的组合,锁定一个范围,同时也锁定记录本身。
Record Lock
记录锁,只锁住一条记录。同时,记录锁也分为S锁和X锁:
当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型记录锁(S 型与 X 锁不兼容)。
当一个事务对一条记录加了 X 型记录锁后,其他事务既不可以对该记录加 S 型记录锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型记录锁(X 型与 X 锁不兼容)。
Gap Lock
间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。
假设,表中有一个 id 范围为(1,5)的间隙锁,那么其他事务就无法插入id=2,3,4这三条记录,这样就有效防止了幻读现象的发生。

间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是,还是有区别,间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻影记录而提出的。
Next-Key Lock
临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,同时锁定记录本身。
临键锁是一个左开右闭的格式
假设,表中字段id的Next-Key Lock为(1,5]

其他事务即不能插入 id=2,3,4 记录
也不能修改和删除 id = 5 这条记录
Next-Key Lock 既能保护该记录,又能阻止其他事务将新记录插入到被保护记录前面的间隙中。
Next-Key Lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的。
预告
下期会通过实验来证明以上结论,敬请期待!
【以上仅为个人观点,如有不同意见,欢迎留言讨论!】


点击蓝字 关注我们





