MySQL锁总结

231 阅读3分钟

61286ea374f9469ba9b6bb9cf448b4a1.jpg 图片来源

MDL

MDL(Meta Data Lock), 元数据锁。

引入MDL主要是为了解决或者保证DDL操作与DML操作之间的一致性。在引入MDL之前,因为DDL和DML的冲突问题会出现下面数据不一致的情况:

  • 事务隔离问题,比如在可重复隔离级别下,会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致,无法满足可重复读的要求
  • 数据复制问题,比如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。

具体可以看一下掘金的这篇文章

MDL读锁共享,读写互斥,写写互斥。因为MDL锁冲突的问题,线上经常出现因为加字段导致服务崩溃的问题。在会话A之后有会话B,会话B之后有会话C,会话A获得的是MDL读锁,会话B获得的是MDL写锁,会话C获得的是MDL读锁。因为会话A是一个大事务,持续时间非常长。这个时候会话B和C都会阻塞,如果会话C有超时重试功能,并且流量非常大,就有可能将数据库连接池打满,出现服务异常的情况。

因为上面这种情况,引出另外一个话题:online DDL。 具体大家可以看一下MySQL的官方文档。总结一下就是将原来的copy-replace分成多个阶段,让阻塞的MDL只出现在少数阶段,并持续非常短的时间。

表锁

表锁的语法是

lock tables ... read/write;
...
unlock table;

还有一种方式是

select * from t lock in share mode

行锁

表锁会锁住整张表,对业务的影响非常大。所以InnoDB推出行锁。InnoDB的行锁是通过锁住索引项来实现的。

这里引申出另外一个概念- 两阶段锁协议: 在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

间隙锁

间隙锁就是锁住数据之间的间隙,在锁没有释放之前,别的事务是无法插入这些间隙的。这样就能保证不出现幻读。

Next-Key-Lock

行锁+间隙锁就组成了Next-Key-Lock,它的加锁规则在极客时间专栏《MySQL实战45讲》林晓斌老师总结为以下规律:

两个“原则”、两个“优化”和一个“bug”。
原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。
原则 2:查找过程中访问到的对象才会加锁。
优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

意向锁

意向锁其实也是一种表级别的锁。意向锁之间也是读写互斥,写写互斥,读读不互斥。可以看一下这篇文章

死锁

死锁就是会话之间持有彼此想要的锁,然后互相等待。