当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL 锁等待超时怎样从 performance_schema 找阻塞者

MySQL 锁等待超时怎样从 performance_schema 找阻塞者

来源:17golang原创 2026-10-20 22:57:24 0浏览 收藏

遇到 MySQL 锁等待超时,我通常不会先猜是哪条业务 SQL 慢,而是先把“等待关系”捞出来:哪个事务在等、哪个事务持有冲突锁、它们分别对应哪个连接。对 InnoDB 来说,performance_schema.data_lock_waits 给出锁依赖,information_schema.innodb_trx 补充事务语句,最后再用 performance_schema.threads 找到会话信息。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/

最实用的排查路径是:data_lock_waits 判断关系,data_locks 看资源,innodb_trx 看事务,threads 找会话。查询结果只是快速变化的现场快照,发现阻塞者后应马上结合业务日志和事务边界处理。

先把等待关系看成一条连接线

data_lock_waits 不是“慢查询排行榜”,而是锁请求之间的关系表。REQUESTING_ENGINE_TRANSACTION_ID 表示正在请求锁的事务,BLOCKING_ENGINE_TRANSACTION_ID 表示当前持有冲突锁的事务。一个等待事务可能对应多行,因为它的请求可能同时受到多个已持有锁的影响。

这张静态结构图把本文会用到的表和字段放在一起:先从等待关系取两端事务,再从事务找到 SQL,最后将连接 ID 映射到会话。

MySQL data_lock_waits、data_locks 与 innodb_trx 的锁等待关系结构图
图1:锁等待关系结构图,展示等待事务、阻塞事务、锁资源和事务语句之间的静态对应关系,不是运行截图。

第一条查询:同时找出等待者和阻塞者

先执行下面这条最小查询。它直接沿着官方文档给出的关系,把同一条等待记录的两端事务展开。字段名刻意保留 waiting_ 和 blocking_ 前缀,避免值很多时看反。

SELECT
    r.trx_id AS waiting_trx_id,
    r.trx_mysql_thread_id AS waiting_connection_id,
    r.trx_state AS waiting_state,
    r.trx_started AS waiting_started,
    r.trx_wait_started AS waiting_wait_started,
    r.trx_query AS waiting_query,
    b.trx_id AS blocking_trx_id,
    b.trx_mysql_thread_id AS blocking_connection_id,
    b.trx_state AS blocking_state,
    b.trx_started AS blocking_started,
    b.trx_query AS blocking_query
FROM performance_schema.data_lock_waits AS w
JOIN information_schema.innodb_trx AS r
  ON r.trx_id = w.requesting_engine_transaction_id
JOIN information_schema.innodb_trx AS b
  ON b.trx_id = w.blocking_engine_transaction_id
ORDER BY r.trx_wait_started;
-- waiting_* 是正在等锁的事务,blocking_* 是持有冲突锁的事务
-- trx_mysql_thread_id 可继续映射到会话连接,NULL 语句不代表没有阻塞

读结果时先看三列:waiting_query 是当前卡住的请求,blocking_query 是阻塞事务最近关联的语句,blocking_started 可以帮助判断它是否长时间未结束。如果同一等待事务出现多行,不要立刻去重;这可能是它同时被多个锁资源挡住。

第二条查询:把锁资源和模式补齐

只知道事务 ID 还不够。要判断是同一行、同一索引,还是表级资源,可以把请求锁和阻塞锁分别连接到 data_locks。同一张表要连接两次:一次给等待者的请求锁,一次给阻塞者持有的锁。

SELECT
    w.requesting_engine_transaction_id AS waiting_trx_id,
    w.blocking_engine_transaction_id AS blocking_trx_id,
    req.object_schema AS waiting_schema,
    req.object_name AS waiting_table,
    req.index_name AS waiting_index,
    req.lock_type AS waiting_lock_type,
    req.lock_mode AS waiting_lock_mode,
    hold.object_schema AS blocking_schema,
    hold.object_name AS blocking_table,
    hold.index_name AS blocking_index,
    hold.lock_type AS blocking_lock_type,
    hold.lock_mode AS blocking_lock_mode,
    req.lock_data AS waiting_lock_data,
    hold.lock_data AS blocking_lock_data
FROM performance_schema.data_lock_waits AS w
JOIN performance_schema.data_locks AS req
  ON req.engine_lock_id = w.requesting_engine_lock_id
 AND req.engine = w.engine
JOIN performance_schema.data_locks AS hold
  ON hold.engine_lock_id = w.blocking_engine_lock_id
 AND hold.engine = w.engine;
-- req 是等待中的锁请求,hold 是挡住它的已持有锁
-- engine 条件避免不同存储引擎出现同名锁 ID 的歧义

重点不是把所有列都打印出来,而是核对 object_schema、object_name、index_name、lock_type 和 lock_mode 是否指向同一个资源。lock_data 可作为定位线索,但不要把它当成稳定的业务主键展示给用户。

第三条查询:从事务连接到会话信息

innodb_trx.trx_mysql_thread_id 是连接层 ID,而 Performance Schema 的 threads 同时保存 PROCESSLIST_ID 和内部 THREAD_ID。用前者关联,后者才能继续查询语句历史。

MySQL 事务连接 ID 映射到 performance_schema.threads 会话信息的结构图
图2:阻塞会话映射结构图,展示事务、PROCESSLIST_ID、内部 THREAD_ID 和最近语句历史的静态关系,不是运行截图。
SELECT
    r.trx_id AS waiting_trx_id,
    r.trx_mysql_thread_id AS waiting_connection_id,
    tw.thread_id AS waiting_thread_id,
    tw.processlist_user AS waiting_user,
    tw.processlist_host AS waiting_host,
    tw.processlist_db AS waiting_db,
    b.trx_id AS blocking_trx_id,
    b.trx_mysql_thread_id AS blocking_connection_id,
    tb.thread_id AS blocking_thread_id,
    tb.processlist_user AS blocking_user,
    tb.processlist_host AS blocking_host,
    tb.processlist_db AS blocking_db,
    tb.processlist_command AS blocking_command,
    tb.processlist_state AS blocking_state,
    tb.processlist_info AS blocking_info
FROM performance_schema.data_lock_waits AS w
JOIN information_schema.innodb_trx AS r
  ON r.trx_id = w.requesting_engine_transaction_id
JOIN information_schema.innodb_trx AS b
  ON b.trx_id = w.blocking_engine_transaction_id
LEFT JOIN performance_schema.threads AS tw
  ON tw.processlist_id = r.trx_mysql_thread_id
LEFT JOIN performance_schema.threads AS tb
  ON tb.processlist_id = b.trx_mysql_thread_id;
-- PROCESSLIST_ID 对应连接 ID,THREAD_ID 用于 Performance Schema 事件历史
-- 使用 LEFT JOIN 保留已经结束或暂时没有进程信息的事务行

如果 blocking_info 是空值,不要马上判定“没有阻塞 SQL”。持锁事务可能已经执行完最后一条语句,连接仍因未提交事务而持有锁;此时真正有用的是事务开始时间、事务状态和业务代码是否遗漏提交或回滚。

阻塞会话空闲时再查最近语句

当阻塞端处于空闲状态,可以用上一步得到的 blocking_thread_id 查询语句历史。这里的 THREAD_ID 不是客户端看到的连接 ID,不能把两个字段混用。

SELECT
    thread_id,
    event_id,
    sql_text,
    timer_start,
    timer_end
FROM performance_schema.events_statements_history
WHERE thread_id = 28
ORDER BY event_id DESC;
-- 把 28 替换为上一条查询得到的 blocking_thread_id
-- 历史表只保留近期事件,过早的持锁语句可能已经不在里面

历史记录适合回答“这个连接刚刚做过什么”,不能证明锁现在仍由那条语句直接执行。事务提交、回滚和连接退出会改变现场,所以排查动作应该尽量靠近超时发生的时间。

用 sys.innodb_lock_waits 做快速入口

如果服务器启用了 sys schema,可以先查询 sys.innodb_lock_waits 获得更短的结果,再回到 data_lock_waits 和 data_locks 展开资源细节。

SELECT
    waiting_pid,
    waiting_query,
    blocking_pid,
    blocking_query,
    locked_table,
    locked_index
FROM sys.innodb_lock_waits
ORDER BY wait_age_secs DESC;
-- 先按等待时长排序,快速找出当前最久的锁等待
-- 需要更细的锁模式和事务关系时,再回到 Performance Schema 原表

这个视图适合值班时快速定位,但它仍然是对底层状态表的读取,不是持久化审计记录。若要做长期分析,应把必要字段写入自己的监控或事件表,并记录采样时间。

三个容易误判的边界

现象更合理的解释下一步
blocking_query 为空阻塞连接可能正在空闲,但未提交事务仍持锁看 trx_started、trx_state、THREAD_ID 和语句历史
同一 waiting_trx_id 有多行同一事务可能等待多个阻塞锁保留每条关系,结合锁对象与模式区分
两次查询结果不一致锁表是快速变化的现场快照,事务可能已提交或回滚缩短采样间隔并记录采样时间,不把空结果当成历史事实

MySQL 官方也提醒,INNODB_TRX、data_locks、data_lock_waits 与 threads 并不是一个不可变快照。跨表连接得到的是排查当下的近似现场,不能替代事务日志或应用日志。

相关问题

锁等待超时和死锁有什么区别?

锁等待超时通常是等待时间超过配置阈值后由等待方报错;死锁则是形成环路后由 InnoDB 选择一个事务回滚。两者都可以从等待关系开始看,但死锁还应结合错误日志和死锁信息分析。

为什么只查 PROCESSLIST 看不到真正的阻塞者?

PROCESSLIST 更偏当前会话状态,未必给出 InnoDB 锁资源之间的关系。应先从 data_lock_waits 找到事务两端,再用 threads 补会话信息。

查这些表需要什么权限?

权限取决于服务器版本、账号和 Performance Schema 配置。若查询报权限错误,应由管理员按最小权限授予读取对应系统表或视图的权限,不要直接扩大到所有管理权限。

参考资料

https://dev.mysql.com/doc/refman/8.4/en/innodb-information-schema-examples.html

https://dev.mysql.com/doc/refman/8.4/en/performance-schema-data-lock-waits-table.html

https://dev.mysql.com/doc/refman/8.4/en/performance-schema-threads-table.html

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL SIGNAL 怎样在存储程序中定义自定义异常MySQL SIGNAL 怎样在存储程序中定义自定义异常
上一篇
MySQL SIGNAL 怎样在存储程序中定义自定义异常
MySQL RESIGNAL 怎样在存储程序中保留原始错误信息
下一篇
MySQL RESIGNAL 怎样在存储程序中保留原始错误信息
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    570次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    614次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    641次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    601次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    419次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码