此文为极客时间:MySQL实战45讲的元数据锁相关部分问题的思考总结
一、读写锁堵塞
前面在锁这块介绍了元数据锁(MDL 锁)。元数据锁是一种读写锁,他的读读不互斥,读写和写写是互斥的。因此我们参考下面的时序图:
现在等待队列中有四个 session 要获取锁:
- 由于 sessionA 和 sessionB 都是读操作,读读不互斥,因此两个 session 都持有读锁;
- sessionA 和 sessionB 还未释放,此时 sessionC 要获取写锁,由于读写互斥,此时 sessionC 发生堵塞;
- 由于写锁的获取优先级高于读锁,sessionC 的写锁没获取到,后面其他 session 的读锁也无法获取到。此时 sessionD 堵塞;
上面是一个很典型的读写锁堵塞的场景。但是实际测试的时候,会出现 sessionC 和 sessionD 都堵塞了,但是将 sessionA 和 sessionB 提交以后,sessionC 仍然保持堵塞状态,必须等 sessionD 也提交以后,才会继续执行。也就说,sessionD 好像发生了一个插队的现象。
二、锁降级
我们知道 mysql 有一个 online ddl,即 执行 ddl 的时候不会直接锁表导致无法执行其他查询的操作。其实这涉及到一个锁降级的概念。即原本流程是:
- 拿写锁
- 做 DDL
- 释放锁
其他查询语句必须等到 DDL完整的做完这三步以后才能拿到锁,引入锁降级以后,流程变成:
- 先只拿读锁;
- 自己先申请内存执行 DDL,先把一些处理做好;
- 去拿写锁;
- 真正完成 DDL,替换表结构
- 释放锁
原本一开始就拿写锁,现在换成了先拿读锁,保证在这期间没有其他 DDL 先执行去修改表,然后自己先把一些读写的过程完成,最后再去拿写锁,把表结构替换上去。相当于原本大家排队买票,轮到了一个人,但是他电话来了,由于他堵着窗口,大家都得等他接完电话才能继续买票。后面大家跟他商量了一些,他接电话的时候先出去,让后面的先继续买票,等他打完电话在回来直接插到最前面。但是这样做的好处在于,避免了修改数据的过程长时间的占用写锁,导致其他 DML 被堵塞。