暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

运维还是研发的锅?MySQL的锁策略如何引发线上争端?

运维路书 2024-11-11
68


线上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

要实现以上两种锁定读,需要在事务中实现,当事务提交了,锁就会被释放,需要在语句执行前使用beginstart 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 时,是会被阻塞的


预告

下期会通过实验来证明以上结论,敬请期待!


【以上仅为个人观点,如有不同意见,欢迎留言讨论!】

点击蓝字 关注我们



文章转载自运维路书,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论