如何迅速并识别处理 MDL 锁阻塞问题

摘要:TaurusDB 推出 MDL 锁视图功能,帮助用户迅速识别并处理 MDL 锁阻塞问题,从而有效减少对业务的负面影响,提升数据库管理效率。
本文分享自华为云社区《【华为云MySQL技术专栏】TaurusDB MDL锁视图》,作者:GaussDB 数据库。

一、背景
数据库中的元数据锁(MDL ,Metadata Lock),用来保护表元数据信息的一致性。用户对表进行读写操作或结构变更时,系统会添加不同类型的 MDL 锁。当客户业务设计不合理,有部分事务长时间持有 MDL 锁时,可能会阻塞其他会话获取相应的 MDL 锁。此时,用户使用执行‘SHOW PROCESSLIST’命令,通常会看到多个会话处于 “Waiting for metadata lock” 状态。但由于无法明确各个会话 ID 之间的关联,往往无法快速找到导致大量 MDL 锁等待的根源,使得用户不得不盲目地 Kill 大量可疑的会话,甚至直接重启实例来快速恢复业务,这种做法无疑增加了解决问题的成本,对业务产生较大影响。
因此,自社区 MySQL 5.7 版本之后,在 PERFORMANCE_SCHEMA 库中新增了 METADATA_LOCKS 表,用于记录系统中 MDL 锁的状态信息,但是需要客户启用 Performance Schema 性能分析监控插件。启用后,Performance Schema 会收集大量的性能数据,包括 SQL 语句执行情况和实例内的锁状态信息等,这会对 MySQL 实例的性能产生一定的负担,尤其在高并发的生产环境中,性能开销更为明显。
鉴于此,TaurusDB 推出 MDL 锁视图功能,帮助用户迅速识别并处理 MDL 锁阻塞问题,从而有效减少对业务的负面影响,提升数据库管理效率。
二、MDL 锁阻塞场景分析
我们以表 1 中的 MDL 锁阻塞案例来介绍 MDL 锁视图的使用场景

表 1 MDL 锁阻塞案例
由于 SESSION 3 存在一个长事务未提交,会一直持有 t2 表的 MDL_SHARED_READ(SR)类型锁。当 SESSION 4 对表 t2 执行 TRUNCATE 操作时,需要获取 MDL-EXCLUSIVE(X)锁,但由于 MDL 锁类型和 SR 锁不兼容而被阻塞。
随后,SESSION 5 的 DML 操作在添加 SR 类型的锁时,发现 MDL 锁等待队列中有比 SR 类型的锁优先级更高的 X 锁在等待,所以 SESSION 5 的 SR 锁请求也会处于等待状态,详细原因可参考《【华为云MySQL技术专栏】TaurusDB新特性解读:非阻塞DDL》。
用户发现 DDL 和 DML 操作都被阻塞后,执行 SHOW PROCESSLIST 查看原因。在 SHOW PROCESSLIST 的信息中,只能看到如图 1 中的结果:

图 1 SHOW PROCESSLIST 结果
SESSION 4 执行 TRUNCATE 操作时,被其他 SESSION 持有的 table metadata lock 阻塞;
SESSION 5 执行 SELECT 操作时,也同样被阻塞;
无法确定哪个会话(2 或 3?)阻塞了 SESSION 4 和 SESSION 5;
此时,如果业务盲目地去 kill 其他会话(2 或 3),可能会影响其他不相关的业务,从而加大问题处理的成本。在实际的生产业务中,可能有更多的会话,用户从成百上千的会话信息中几乎无法找到导致 MDL 锁等待的根源,只能盲目地 Kill 大量的会话或者重启实例来快速恢复。而且用户在事后也无法定位到根因,从源头杜绝此类问题的再次发生。刚好,TaurusDB 的 MDL 锁视图功能在这个时候就可以发挥作用。
三、MDL 锁视图介绍
TaurusDB 的 MDL 锁视图以系统表的形式呈现,该表位于 INFORMATION_SCHEMA 库下,表名为 METADATA_LOCK_INFO。其中每一行的信息表示一个会话持有或正在等待的 MDL 锁信息。
每个字段的具体含义,如表 2 所示:

表 2 MDL 锁视图表字段含义
此表的查询结果中,同一个会话可能持有多行 MDL 锁的相关信息。主要有以下几方面的原因:
1)当执行涉及多张表的连表查询时,会给每一个表添加 MDL_SHARED_READ 模式的 MDL 锁。
2)事务级别的 MDL 锁,只有在事务结束时才会释放。因此,当一个事务涉及多张表的 DML 操作时,这个会话会同时持有多个 MDL 锁,直到事务结束。
3)在 DDL 语句的执行过程中,需要添加多种类型的 MDL 锁。例如,在添加列的 DDL 语句中,可能会添加 Backup lock,Global read lock,Schema metadata lock,Table metadata lock,并且在不同阶段对锁的模式进行升/降级。
四、MDL 锁视图使用方法
针对表格 1 中的 MDL 锁阻塞场景,用户可以执行以下 SQL 语句:SELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO;
再结合 SHOW PROCESSLIST 的输出结果,快速定位到问题根因。
在图 2 元数据锁视图的结果信息中,我们应该从 PENDING 状态的会话开始入手。

图 2 元数据锁视图结果
根据 MDL 锁等待的 TABLE_SCHEMA 和 TABLE_NAME 信息,找到其他 THREAD ID 下,具有相同库名和表名且状态是 GRANTED 的 MDL 锁信息。这个 THREAD ID 即是造成锁等待的会话。
基于以上原理,我们可以看出:
会话 4 在等待获取 test 库 t2 表的 MDL_EXCLUSIVE 模式的元数据锁;
会话 5 在等待获取 test 库 t2 表的 MDL_SHARED_READ 模式的元数据锁;
会话 3 持有 test 库 t2 表 t2 的 MDL 锁,该 MDL 锁为事务级别,只要 session 3 的事务不提交,session 4 和 5 便会一直阻塞。
所以,通过 MDL 锁视图,我们只需要在会话 3 中执行 Rollback 或者 Commit,便可以让业务继续运行。虽然 MDL 锁视图可以帮助定位到导致大量 MDL 锁等待的根源,但是当会话较多时,表中很多不相关的 MDL 锁信息查看起来也会耗费大量时间,这里我们提供一个可以快速查找到阻塞会话的 SQL。在发生问题时,只要执行一下这个语句,就可以迅速找到需要 kill 的会话。
五、原理解析
基于对《【华为云MySQL技术专栏】TaurusDB MDL实现机制解析》中 MDL 锁相关数据结构和 MDL 锁添加、释放流程的分析,TaurusDB 在 INFORMATION_SCHEMA 库下添加了 MDL 锁视图。
在 TaurusDB 的内部架构中,每一个用户连接有一个 THD(Thread Handler,线程处理器)对象,这些 THD 对象统一由 Global_THD_manager 结构体进行管理。如图 3 所示,每个 THD 对象关联了一个 MDL_context 实例,这个实例提供了线程级的 MDL 锁操作接口,包括申请、释放 MDL 锁以及锁的升降级。

图 3. MDL 锁基本概念图
其中,THD 中的 MDL_context 也有两个变量m_ticket_store
和
m_waiting_for 用于维护会话持有和等待的 MDL 锁的信息。
m_ticket_store:
用来存储当前线程获取的所有MDL_ticket。为了提升搜索效率,根据MDL锁的持续时间(语句执行时间段,事务执行时间段和显示指定时间段)将其划分为三个链表,在需要获取MDL锁前,会先在这些链表内查询是否已经获取到了相同的或这是更强类型的MDL锁,如果搜索不到继续获取MDL锁。
m_waiting_for:用来存储当前线程正在等待的 MDL 锁,一个线程同一时刻只能等待一种类型的 MDL 锁。
每一个链表中的 MDL_ticket 对象是每个线程已经获取到的 MDL 锁或者是要请求的 MDL 锁的详细信息,其结构体内包含 MDL 锁的模式(enum_mdl_type)、MDL 锁的持续时间(enum_mdl_duration)和 MDL 锁对象(MDL_lock)。其中,MDL_lock 由 MDL_key 唯一标识。MDL_key 是一个三元组,由命名空间(enum_mdl_namespace)、库名和对象名组成。
因此,在用户查询 MDL 锁视图时,实现流程如图 4 所示,只需要遍历所有会话 THD 的 MDL_context 对象,根据其 m_ticket_store 链表中的每一个 MDL_ticket 对象构造出处于 GRANTED 状态的 MDL 锁信息。同理,通过 m_waiting_for 对象获取到处于 PENDING 状态的 MDL 锁信息。最后,将结果集返回给客户端展示即可。

图 4 MDL 锁视图实现流程
其中,i_s_metadata_lock_info_fill_table 和 List_THD_MDL_tickets 为核心函数,用来实现遍历 Global_THD_manager 中的 THD 并从其 MDL_context 中构造当前会话的持有和等待的 MDL 锁信息。
六、总结
TaurusDB 的 MDL 锁视图 INFORMATION_SCHEMA.METADATA_LOCK_INFO,可以在不开启 Performance Schema 性能监控插件时,获取到实例系统中所有 MDL 锁的持有和等待状态。熟练的使用 MDL 锁视图,可以帮助用户快速地定位和分析导致大量 MDL 锁等待的根本原因,还能够根据分析结果进行迅速有效的处理,解决 MDL 长时间锁等待问题,并且不会因为依赖 Performance Schema 性能监控插件而对系统性能产生任何影响。
关注“GaussDB 数据库”公众号,了解更多动态
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/3331bcbaab1735e2cbaceaea6】。文章转载请联系作者。
评论