MDL( metadata lock)
1.1 当对一个表做增删改查的时候,该表会被加MDL读锁。(DML操作需要MDL读锁)
1.2 当对表做结构变更的时候,加MDL写锁。(DDL操作需要MDL写锁)
1.3 读读不互斥,读写,写写互斥。
1.4 一旦出现写锁等待,不但当前操作会被阻塞,同时还会阻塞后续该表的所有操作。事务一旦申请到MDL锁后,直到事务执行完才会将锁释放。可以理解为,当前的MDL写锁,如果事务未提交,会导致后续该表都不能进行操作。 —> 引申出一个问题,
MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。
引入MDL解决了什么问题?
- 事务隔离问题,即会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致(返回的结构不同),无法满足可重复读的要求。
- 数据复制问题,如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。
出现的问题case
- 在对表进行上述操作时,如果表上有活动事务(未提交或回滚),请求写入的会话会等待在Metadata lock wait 。
- 已经有MDL写锁时,后续的读事务也会阻塞。直到MDL写锁提交。
case来源: 1
- session A先启动,这时候会对表t加一个MDL读锁。由于session B需要的也是MDL读锁,因此可以正常执行。
- 之后session C会被blocked,是因为session A的MDL读锁还没有释放,而session C需要MDL写锁,因此只能被阻塞。
- 如果只有session C自己被阻塞还没什么关系,但是之后所有要在表t上新申请MDL读锁的请求(如sessionD)也会被session C阻塞。
如果某个表上的查询语句频繁,而且客户端有重试机制,也就是说超时后会再起一个新session再请求的话,这个库的线程很快就会爆满
如何避免
- 规范使用事务,及时提交事务,避免使用大事务。
- 设置参数lock_wait_timeout为较小值,使被阻塞端主动停止。
- DDL操作及备份操作放在业务低峰期执行。
- 少用工具开启事务进行查询,图形化工具要及时关闭。
MDL与行锁
问题: 表锁同一张表在同一时刻只能有一个更新。但是上节课讲的表级锁中的MDL锁,dml语句会产生MDL读锁,而MDL读锁不是互斥的,也就是说一张表可以同时有多个dml语句操作。感觉这两种说法有点矛盾?2
不矛盾,MDL锁和表锁是两个不同的结构。
比如:
你要在table 表上更新一行,那么会加MDL读锁和表的写锁;
然后同时另外一个线程要更新这个表上另外一行,也要加MDL读锁和表写锁。
第二个线程的MDL读锁是能成功加上的,但是被表写锁堵住了。从语句现象上看,就是第二个线程要等第一个线程执行完成。如果有多种锁,必须得“全部不互斥”才能并行,只要有一个互斥,就得等
参考资料
MDL锁导致的几个常见的 MySQL 问题分析 - 墨天轮
mysql45讲