必须立刻查INNODB_TRX和data_locks,仅靠SHOW PROCESSLIST会漏掉已启动但未执行SQL的阻塞源;因Sleep或空State事务可能trx_state='RUNNING'、trx_started早、trx_rows_locked>0却在PROCESSLIST中“静默”,唯有INNODB_TRX能暴露其持有锁的真实状态。

必须立刻查 information_schema.INNODB_TRX 和 performance_schema.data_locks,仅靠 SHOW PROCESSLIST 会漏掉已启动但尚未执行 SQL 的阻塞源头。
为什么不能只看 SHOW PROCESSLIST
进程列表里显示 Command 为 Sleep 或 Query 但 State 是空的事务,可能正持有锁却没在跑语句——比如事务开启后做了些业务逻辑(调外部 API、发消息),还没走到 UPDATE 就卡住了。这类事务在 PROCESSLIST 里看起来“安静”,但在 INNODB_TRX 里 trx_state 是 RUNNING,trx_started 时间很早,且 trx_rows_locked > 0。不查这个表,就找不到真正的锁持有者。
INNODB_TRX + data_locks 关联查阻塞链
MySQL 8.0+ 推荐用 performance_schema.data_locks 替代已废弃的 INNODB_LOCKS。关键是要把锁、事务、线程三者串起来:
- 先从
INNODB_TRX找出trx_state = 'LOCK WAIT'的事务,记下trx_id和trx_mysql_thread_id - 用该
trx_id去performance_schema.data_lock_waits查blocking_trx_id - 再用
blocking_trx_id回到INNODB_TRX拿到阻塞方的trx_mysql_thread_id,最后关联PROCESSLIST看它连的是哪个应用、执行了什么命令 - 如果想直接看到锁落在哪行,查
data_locks表里的OBJECT_SCHEMA、OBJECT_NAME、INDEX_NAME和LOCK_DATA字段——后者在行锁时会显示主键值,比如123,能快速定位冲突的具体记录
data_locks 里 LOCK_DATA 为空或显示 supremum pseudo-record 怎么办
这通常意味着锁是间隙锁(Gap Lock)或插入意向锁(Insert Intention Lock),不是锁某一行,而是锁住索引之间的“空隙”。典型场景是:
-
REPEATABLE READ隔离级别下,SELECT ... FOR UPDATE WHERE status = ?没走索引,触发全表扫描 → 锁整个范围 -
INSERT INTO t VALUES (x) ON DUPLICATE KEY UPDATE ...在唯一索引冲突时,InnoDB 会先加插入意向锁,再等唯一约束检查完成,期间若另一事务正持有该位置的间隙锁,就会卡住 -
LOCK_DATA为空时,重点看INDEX_NAME和LOCK_MODE:若为RECORD GAP或INSERT_INTENTION,说明问题不在数据行本身,而在索引结构或查询条件是否命中索引
查完不 kill,先确认事务是否真“无用”
很多线上阻塞源于一个“半死不活”的事务:它没报错、没超时、也没提交,但业务上其实已经失败或放弃。直接 KILL 可能导致数据不一致。实操建议:
- 查
PROCESSLIST里对应ID的Info字段,如果是NULL或空,说明 SQL 已执行完,事务还开着;再查INNODB_TRX的trx_query,如果是NULL,基本可判定是应用层没正确 commit/rollback - 结合监控查该连接的
Time(秒数)和业务日志时间戳,若远超正常事务耗时(比如 > 30 秒),且无后续操作痕迹,才考虑 kill - 真正危险的是
trx_state = RUNNING且trx_query是长耗时 SQL(如大表 JOIN),这种要优先优化 SQL,而不是 kill —— 杀了还会再起
最常被忽略的一点:data_locks 默认只对有锁的事务可见,如果一个事务刚 start transaction 还没任何 DML,它不会出现在这张表里,但它依然能阻塞别人——因为 InnoDB 的事务 ID 分配和锁机制是绑定的。所以排查必须从 INNODB_TRX 入手,而不是反过来。


















