摘要:TaurusDB推出MDL锁视图功能,帮助用户迅速识别并处理MDL锁阻塞问题,从而有效减少对业务的负面影响,提升数据库管理效率。
一、背景
二、MDL锁阻塞场景分析
表1 MDL锁阻塞案例
-
SESSION 4执行TRUNCATE操作时,被其他SESSION持有的table metadata lock阻塞;
-
SESSION 5执行SELECT操作时,也同样被阻塞;
-
无法确定哪个会话(2或3?)阻塞了SESSION 4和SESSION 5;
三、MDL锁视图介绍
表2 MDL锁视图表字段含义
四、MDL锁视图使用方法
图 2 元数据锁视图结果
-
会话4在等待获取test库t2表的MDL_EXCLUSIVE模式的元数据锁;
-
会话5在等待获取test库t2表的MDL_SHARED_READ模式的元数据锁;
-
会话3持有test库t2表t2的MDL锁,该MDL锁为事务级别,只要session 3 的事务不提交,session 4和5便会一直阻塞。
SELECT f.processlist_id, p.Info AS sql_info
FROM (
SELECT DISTINCT c.blocking_processlist_id AS processlist_id
FROM (
SELECT DISTINCT b.THREAD_ID AS blocking_processlist_id
FROM information_schema.metadata_lock_info a
JOIN information_schema.metadata_lock_info b
ON a.TABLE_SCHEMA = b.TABLE_SCHEMA
AND a.TABLE_NAME = b.TABLE_NAME
AND a.lock_status = 'PENDING'
AND b.lock_status = 'GRANTED'
AND a.THREAD_ID <> b.THREAD_ID
) c
WHERE c.blocking_processlist_id NOT IN (
SELECT DISTINCT d.THREAD_ID AS blocked_processlist_id
FROM information_schema.metadata_lock_info d
JOIN information_schema.metadata_lock_info e
ON d.TABLE_SCHEMA = e.TABLE_SCHEMA
AND d.TABLE_NAME = e.TABLE_NAME
AND d.lock_status = 'PENDING'
AND e.lock_status = 'GRANTED'
AND d.THREAD_ID <> e.THREAD_ID
)
) f
JOIN information_schema.processlist p ON processlist_id = p.Id;
五、原理解析
图 3. MDL锁基本概念图
-
m_ticket_store: 用来存储当前线程获取的所有MDL_ticket。为了提升搜索效率,根据MDL锁的持续时间(语句执行时间段,事务执行时间段和显示指定时间段)将其划分为三个链表,在需要获取MDL锁前,会先在这些链表内查询是否已经获取到了相同的或这是更强类型的MDL锁,如果搜索不到继续获取MDL锁。
图 4 MDL锁视图实现流程
i_s_metadata_lock_info_fill_table() {
//对系统中每一个THD执行List_THD_MDL_tickets函数找到持有和等待的MDL锁
Global_THD_manager::get_instance()->do_for_all_thd_copy(List_THD_MDL_tickets)
}
List_THD_MDL_tickets() {
// 获取当前THD的MDL_context:
MDL_context &mdl_ctx = inspect_thd->mdl_context;
// 获取当前THD持有的MDL锁
const MDL_ticket_store &m_ticket_store = mdl_ctx.get_mdl_ticket_store();
// 遍历每个m_ticket_store的三个作用范围内的MDL_ticket
for (int i = 0; i < MDL_DURATION_END; i++) {
MDL_ticket_store::List_iterator it = m_ticket_store.list_iterator(duration);
lock_extras.duration = duration;
while ((ticket = it++)) {
enum_mdl_duration duration = (enum_mdl_duration)(i);
// 根据MDL_ticket中的信息填充到MDL锁视图中
fill_row_callback(ticket, &lock_extras, args);
}
}
// 获取当前THD等待的MDL锁
ticket = dynamic_cast<const MDL_ticket *>(mdl_ctx.get_m_waiting_for());
if (ticket != nullptr) {
// 填充MDL锁的额外信息,PENDING状态和作用范围
lock_extras.lock_status = MDL_ticket::PENDING;
lock_extras.duration = ticket->get_duration();
// 根据MDL_ticket中的信息填充到MDL锁视图中
fill_row_callback(ticket, &lock_extras, args);
}
}