快速定位业务死锁 -- 表MDL锁视图
什么是MDL锁
MDL锁,Metadata Lock,即元数据锁,在数据库中元数据即数据字典信息包括db, table, function, procedure, trigger, event等,首先,看看官方的说法:
MySQL uses metadata locking to manage concurrent access to database objects and to ensure data consistency. Metadata locking applies not just to tables, but also to schemas, stored programs (procedures, functions, triggers, scheduled events), tablespaces, user locks acquired with the GET_LOCK() function, and locks acquired with the locking service.
从上面的描述可以看到,MDL锁主要为了保证元数据的一致性,用于处理不同线程操作同一元数据对象的同步与互斥问题,会在各个业务场景下十分频繁地使用到。
MySQL在5.5.3版本引入了MDL锁。其实5.5也有类似元数据保护的机制,但没有明确提出MDL的概念。5.5之前版本(比如5.1)与5.5之后版本在元数据保护上的不同点是,5.1对于元数据的保护是语句级别的,5.5对于Metadata的保护是事务级别的。所谓语句级别,即语句执行完成后,无论事务是否提交或回滚,其表结构可以被其他会话更新;而事务级别则是在事务结束后才释放MDL。
引入MDL锁主要是为了解决两个问题:
Ø 事务隔离问题:比如在可重复隔离级别下,会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致,无法满足可重复读的要求。
Ø 数据复制问题:比如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。
什么是MDL锁视图
社区版MySQL无法获取表元数据锁(MDL)的详细信息,当客户遇到类似“Waiting for metadata lock”的问题而阻塞DML或DDL后,由于无法确定各session之间的关联关系,往往无从下手,复杂情况下,只能重启实例,解决问题的成本非常高,会对业务产生较大影响。且在业务场景较复杂的情况下,一旦涉及对数据库元数据的互斥操作(如DDL、LOCK Table等),此类问题便会频繁发生,给一线运维和客户带来很大的困扰。
针对以上痛点,华为云数据库MySQL在充分调研内核的基础上,推出了MDL锁视图特性,可以查看数据库各session持有和等待的元数据锁信息,一目了然,方便现网运维进行问题定位,更好的服务客户;对于客户而言,可以有效进行系统诊断,优化自身业务。
MDL锁视图详解
MDL锁视图以系统表的形式呈现,该表位于INFORMATION_SCHEMA,表名:METADATA_LOCK_INFO。表结构如下:
各字段详情:
Ø THREAD_ID:session的id,即会话id;
Ø LOCK_STATUS:MDL锁的状态,两种:PENDING和GRANTED,分别表示session正在等待该MDL锁和session已获得该MDL锁;
Ø LOCK_MODE:加锁的模式,如:MDL_SHARED 、MDL_EXCLUSIVE 、MDL_SHARED_READ、MDL_SHARED_WRITE等;
Ø LOCK_TYPE:MDL锁的类型,如:Table metadata lock、Schema metadata lock、Global read lock、Tablespace lock等;
Ø LOCK_DURATION:MDL锁范围,三种取值:MDL_STATEMENT、MDL_TRANSACTION、MDL_EXPLICIT,分别表示语句级别、事务级别、global级别;
Ø TABLE_SCHEMA:数据库名,对于部分global级别的MDL锁,该值为空;
Ø TABLE_NAME:表名,对于部分global级别的MDL锁,该值为空;
备注:关于LOCK_MODE和LOCK_TYPE的详情这里不一一列举,感兴趣的读者可自行查阅。
典型案例
以下案例均源于现网真实案例。
场景一 长时间未提交事务,阻塞DDL,继而阻塞所有同表的操作
场景再现:
T |
Session 2 |
Session 3 |
Session 4 |
Session 5 |
T1 |
begin; select *from t1; |
|||
T2 |
begin; select *from t2; |
|||
T3 |
truncate table t2; (blocked) |
|||
T4 |
begin; select *from t2; (blocked) |
客户问题:
客户发现对表t2的truncate一直被阻塞后,且业务流程中对表t2的SELECT操作也全部被阻塞。
问题排查过程:
Ø 无MDL锁视图
当用户发现DDL被阻塞后,执行show processlist:
但是通过processlist信息,只能看到:
l session 4执行truncate操作时被其他session持有的table metadata lock阻塞;
l session 5执行select操作时也同样被阻塞;
l 无法确定哪个session(2或3?)阻塞了session 4和session 5;
此时,盲目的去kill其他session(2或3)会给线上业务带来很大风险,因此只能等待其他session释放该MDL锁。
Ø 引入MDL锁视图:
执行SELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO:
结合show processlist的结果,从元数据锁视图中可以明显看出:
l session 4 pending在表t2的metadata lock;
l session 3持有表t2的metadata lock,该MDL锁为事务级别,只要session 3的事务不提交,session 4便会一致阻塞;
所以,解决方法也很清晰,客户只需要在session 3中执行commit或kill session 3,便可以让业务继续运行。
场景二 长时间持有MDL锁,导致全备失败
场景再现:
T |
Session 2 |
Session 3 |
Session 4 |
Session 5 |
T1 |
lock tables t1 write; |
|||
T2 |
select *from t1; |
insert into t2; |
select *from t3 |
|
T3 |
Lock tables for backup; (blocked) |
|||
T4 |
begin; select *from t2; |
begin; insert into t3; |
客户问题:
客户实例最近几次全备均失败,但是业务表现似乎正常,而且最近系统业务量不高,未出现明显问题;
问题排查过程:
Ø 无MDL锁视图:
运维发现全备被阻塞后,首先show processlist,发现客户有多个活跃的客户session:
全备是基于xtrabackup,在执行真正的备份之前需要执行lock tables for backup,但从show processlist中只能可以看到:
l lock tables for backup时一直被某个MDL锁阻塞,全备超时失败;
l 用户的多个session确实业务量很小,都处于sleep状态;
于是,继续执行show open tables where in_use >=1:
发现有个表t1始终处于in use状态,所以猜测是用户某个session持有了该表t1的mdl锁,未释放,导致lock tables for backup等待超时。但是结合show processlist仍然无法确定是哪个session持有表t1的mdl锁,想让全备执行成功,只能通知客户挨个断连session或者重启实例。
Ø 引入MDL锁视图:
执行SELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO:
结合show processlist的结果,从元数据锁视图中可以明显看出:
l session 4 pending在全局backup lock上;
l session 2持有全局的backup lock,该MDL锁类型为MDL_EXPLICIT,global级别;
所以,解决方法也很清晰了,客户只需要在session 2显式调用unlock tables释放锁或者kill session 2便可以让业务继续运行。
小结
从以上两个案例可以看出MDL锁视图的重要性,它可以让客户和一线运维人员清晰地查看数据库各session持有和等待的元数据锁信息,一目了然,从而找出数据库MDL锁等待的根因,准确地的进行下一步决策,有效降低对业务影响。
- 点赞
- 收藏
- 关注作者
评论(0)