在MySQL 5.7及8.0版本之后,官方已明确不推荐仅依赖information_schema来排查锁问题,其重心已转向 performance_schema (P_S)。
相比于记录静态元数据的information_schema,performance_schema的优势在于记录“正在发生什么”,是线上实时诊断与排障的利器。然而,其表结构众多、字段含义抽象,常令使用者无从下手。本文将通过一条完整的排查链路,帮助你真正掌握其用法。
简单总结如下:
| 工具 |
核心场景 |
SHOW ENGINE INNODB STATUS |
查看最近一次死锁详情 |
information_schema |
查看静态锁信息(传统方式) |
| performance_schema |
实时监控锁等待、追踪“谁在等谁”、分析等待时长 |
当你遇到以下场景时,performance_schema是你的最佳选择:
- 线上请求无故卡顿
- SQL执行极慢但未报错
- 锁等待现象频发
👉 此时,performance_schema是最强大的实时诊断工具,它逐步替代了旧有的排查方式,成为现代数据库与中间件运维的核心技能。
执行以下命令进行验证:
SHOW VARIABLES LIKE 'performance_schema';
预期结果为:
performance_schema = ON
若状态为OFF(常见于一些旧环境),需在MySQL配置文件my.cnf的[mysqld]部分添加:
performance_schema=ON
修改后需重启MySQL服务生效。
第二步:掌握锁等待排查的核心四表
无需记忆所有表,聚焦以下四个核心视图即可。
1. data_locks:查看当前持有的锁
SELECT * FROM performance_schema.data_locks;
此表展示了:
- 锁类型:行锁或表锁
- 锁模式:共享锁(S)、排他锁(X)、间隙锁(GAP)、临键锁(NEXT-KEY)
- 锁对象:所在的表、索引
- 持有者:是哪个事务持有的锁
2. data_lock_waits:揭示锁等待关系(⭐最关键)
SELECT * FROM performance_schema.data_lock_waits;
这是锁等待关系的核心映射表,关键字段如下:
| 字段 |
含义 |
requesting_engine_lock_id |
正在等待的锁ID |
blocking_engine_lock_id |
造成阻塞的锁ID |
requesting_thread_id |
等待锁的线程ID |
blocking_thread_id |
持有锁的阻塞线程ID |
👉 只要存在锁等待,就一定能在此表中找到记录。
3. threads:关联线程与连接
SELECT * FROM performance_schema.threads;
通过此表,可将thread_id映射到具体的MySQL连接、用户及其当前状态。
4. events_statements_current:定位正在执行的SQL
SELECT * FROM performance_schema.events_statements_current;
此表用于定位“哪条SQL在等待锁”或“哪条SQL持锁不放”,是关联问题SQL的关键。
实战演练:如何逐步排查一条卡住的SQL?
问题场景
业务反馈:一条UPDATE语句长时间执行无返回,不报错也不结束。
✅ 排查第一步:确认存在锁等待
SELECT * FROM performance_schema.data_lock_waits;
若有记录返回,则证实存在锁等待。
✅ 排查第二步:锁定“谁等谁”的核心关系
SELECT
r.trx_id AS waiting_trx,
r.trx_mysql_thread_id AS waiting_thread,
b.trx_id AS blocking_trx,
b.trx_mysql_thread_id AS blocking_thread
FROM performance_schema.data_lock_waits w
JOIN information_schema.innodb_trx r ON w.requesting_engine_transaction_id = r.trx_id
JOIN information_schema.innodb_trx b ON w.blocking_engine_transaction_id = b.trx_id;
此查询结果将清晰显示:哪个事务(waiting_trx)在等待,哪个事务(blocking_trx)在阻塞它。
✅ 排查第三步:查看当事双方执行的SQL
SELECT
t.THREAD_ID,
es.SQL_TEXT
FROM performance_schema.events_statements_current es
JOIN performance_schema.threads t ON es.THREAD_ID = t.THREAD_ID;
通过此步骤可直接定位:
- 正在等待锁的SQL(受害者)
- 持有锁不释放的SQL(通常是未提交的长事务)
✅ 排查第四步:分析锁定的对象与范围
SELECT
OBJECT_SCHEMA,
OBJECT_NAME,
INDEX_NAME,
LOCK_TYPE,
LOCK_MODE
FROM performance_schema.data_locks;
需要重点关注:
INDEX_NAME是否为NULL(NULL通常意味着表锁)
LOCK_MODE是否为X(排他锁)、X,GAP(排他间隙锁)、X,NEXT-KEY(排他临键锁)
此步是判断锁范围是否过大、是否因索引缺失引起全表扫描的关键。
完整案例:一个真实的锁等待分析
- 从
data_lock_waits发现:事务T1正在等待事务T2释放锁。
- 从
data_locks查看到锁详情:
TABLE: orders
INDEX: idx_user_id
LOCK_MODE: X,NEXT-KEY
- 关联的SQL语句:
- T1(等待方):
UPDATE orders SET status=1 WHERE user_id=100;
- T2(持有方):
SELECT * FROM orders WHERE user_id=100 FOR UPDATE;
- 结论分析:
- 锁定发生在二级索引
idx_user_id上。
- 锁模式为
NEXT-KEY LOCK。
- 在RR(可重复读)隔离级别下,对同一索引区间进行并发更新/加锁。
- 👉 这是一个典型的由范围锁(Next-Key Lock)引发的并发竞争问题。
生产环境紧急“止血”与优化建议
1. 立即终止阻塞源(最快)
KILL <blocking_thread_id>;
通常优先KILL掉持有锁时间最长的事务以快速恢复业务。
2. 优化事务设计(治本)
- 避免在事务中进行远程RPC调用。
- 事务内切勿使用
SLEEP()或进行耗时操作。
- 遵循“短事务”原则,操作完成后尽快提交。
3. 检查并优化索引(解决90%的锁问题)
无合适索引是导致锁范围过大(如锁全表、大范围Next-Key Lock)的根因。务必为WHERE条件和UPDATE语句的过滤条件添加高效索引。
4. 利用MySQL 8.0新特性
对于任务队列、抢单等高并发场景,可使用:
SELECT ... FOR UPDATE NOWAIT; -- 若无法立即获锁则立即报错
SELECT ... FOR UPDATE SKIP LOCKED; -- 跳过已被锁定的行
这些特性可以有效减少锁等待,是运维与DevOps工作中提升系统并发能力的重要手段。
一句话总结:
SHOW ENGINE INNODB STATUS 像查看“尸检报告”,而 performance_schema 则用于监控“正在抢救的现场”。
| 对比项 |
performance_schema |
INNODB STATUS |
| 实时性 |
⭐⭐⭐⭐⭐ |
⭐⭐ |
| 能否关联具体SQL |
✅ |
❌ |
| 展示锁等待链 |
✅ |
❌ |
| 死锁信息复现 |
一般 |
⭐⭐⭐⭐⭐ |
👉 结论:对于线上实时锁问题排查,应优先使用performance_schema。
总结
使用performance_schema排查MySQL锁等待,核心路径是:通过data_lock_waits表定位锁等待关系,利用data_locks表分析锁的类型与作用范围,再结合events_statements_current与threads表追溯至具体的SQL语句和连接。它能够实时、精准地反映“谁被谁阻塞”以及“锁住了什么”,是定位和解决线上数据库锁性能问题最有效的工具集。