不能直接JOIN INNODB_LOCKS和PROCESSLIST,因前者无线程ID只有LOCK_TRX_ID,后者无线程对应事务ID,表结构无关联字段;且INNODB_LOCKS在8.0.24+已废弃,5.7中也不含SQL仅存锁元信息。

查不到“谁持锁+对应SQL”的直接关联,是因为 INNODB_LOCKS 和 PROCESSLIST 没有外键可连;必须通过事务 ID 中转,且 MySQL 5.7/8.0 行为差异大,硬连容易漏或错。
为什么不能直接 JOIN INNODB_LOCKS 和 PROCESSLIST
INNODB_LOCKS 表里没有线程 ID(ID 或 CONN_ID),只有 LOCK_TRX_ID;而 PROCESSLIST 里没有事务 ID,只有 ID(即线程 ID)和 USER 等运行时信息。两者无法直接 ON 关联——这不是写法问题,是表结构设计如此。
更关键的是:INNODB_LOCKS 在 MySQL 8.0.24+ 已被标记为废弃(deprecated),官方明确建议迁移到 performance_schema.data_locks;而 5.7 及更早版本虽可用,但只记录“当前持有锁的事务”,不保证实时性,且不包含已释放但尚未清理的锁残留。
-
INNODB_LOCKS不存 SQL,只存锁类型、对象、模式等元信息 -
PROCESSLIST的INFO字段可能为空(尤其长事务空闲时),或被截断 - 事务 ID(
TRX_ID)和线程 ID(THREAD_ID)在不同版本映射方式不同:5.7 是十进制整数,8.0+ 是十六进制字符串,JOIN 前必须 CAST 或 HEX/UNHEX 转换
MySQL 5.7 正确关联方式:用 INNODB_TRX 当中转表
三表关联路径是:INNODB_LOCKS → INNODB_TRX(靠 LOCK_TRX_ID = TRX_ID)→ PROCESSLIST(靠 TRX_MYSQL_THREAD_ID = ID)。
执行前确认你有 PROCESS 权限(否则 PROCESSLIST 只能看到自己线程):
MySQL 数据库管理技能,能够通过自然语言查询和管理数据库,支持 SELECT/INSERT/UPDATE/DELETE、表管理、备份恢复等操作。当用户提及 MySQL、数据库查询、建表或数据备份时使用此技能。
SELECT pl.ID AS thread_id, pl.USER, pl.HOST, pl.DB, pl.COMMAND, pl.TIME, pl.STATE, pl.INFO AS executing_sql, trx.TRX_ID, trx.TRX_STARTED, trx.TRX_STATE, trx.TRX_QUERY, locks.LOCK_TRX_ID, locks.LOCK_MODE, locks.LOCK_TYPE, locks.LOCK_TABLE, locks.LOCK_INDEX FROM information_schema.INNODB_LOCKS locks INNER JOIN information_schema.INNODB_TRX trx ON locks.LOCK_TRX_ID = trx.TRX_ID INNER JOIN information_schema.PROCESSLIST pl ON trx.TRX_MYSQL_THREAD_ID = pl.ID ORDER BY trx.TRX_STARTED;
- 该语句只返回“当前正持有锁且仍活跃”的事务,若事务刚提交、锁已释放,就不会出现在
INNODB_LOCKS里 -
TRX_QUERY是事务最近执行的语句,不一定就是加锁语句(比如 BEGIN 后执行了多条,它只记最后一条) - 如果
PL.INFO为空但TRX_QUERY有值,优先信后者;反之若TRX_QUERY为空,说明事务处于空闲状态(Sleep),但锁未释放
MySQL 8.0+ 推荐用 performance_schema 替代
INNODB_LOCKS 已弃用,performance_schema.data_locks 是唯一可靠来源,但它也不含线程信息——仍需经 INNODB_TRX 或 threads 表中转。
推荐组合(兼容性好、字段全):
SELECT t.PROCESSLIST_ID AS thread_id, t.PROCESSLIST_USER AS user, t.PROCESSLIST_HOST AS host, t.PROCESSLIST_DB AS db, t.PROCESSLIST_COMMAND AS command, t.PROCESSLIST_TIME AS time, t.PROCESSLIST_STATE AS state, t.PROCESSLIST_INFO AS executing_sql, trx.TRX_ID, trx.TRX_STARTED, trx.TRX_STATE, trx.TRX_QUERY, dl.ENGINE_TRANSACTION_ID, dl.OBJECT_SCHEMA, dl.OBJECT_NAME, dl.INDEX_NAME, dl.LOCK_TYPE, dl.LOCK_MODE, dl.LOCK_DATA FROM performance_schema.data_locks dl INNER JOIN information_schema.INNODB_TRX trx ON dl.ENGINE_TRANSACTION_ID = trx.TRX_ID INNER JOIN performance_schema.threads t ON trx.TRX_MYSQL_THREAD_ID = t.THREAD_ID WHERE t.PROCESSLIST_ID IS NOT NULL ORDER BY trx.TRX_STARTED;
- 注意
ENGINE_TRANSACTION_ID是 8.0+ 新字段,对应TRX_ID,但类型为 BIGINT UNSIGNED,无需 HEX 转换 -
threads表比PROCESSLIST更底层,PROCESSLIST_ID为空表示内部线程(如 purge、io_thread),可直接过滤掉 - 若查询结果为空,先检查
performance_schema是否启用:SELECT @@performance_schema;应为 1;再确认锁采集是否开:SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'wait/lock/innodb%';的ENABLED列需为 YES
真正卡住时,别只盯“持锁者”,先看“等待者”
很多情况下,你真正想杀的是那个“卡住不动”的会话,而不是它前面那个早已提交的持锁者。这时 INNODB_LOCK_WAITS 或 performance_schema.data_lock_waits 更直接。
例如查谁在等、等什么:
SELECT rpl.PROCESSLIST_ID AS waiting_thread, rpl.PROCESSLIST_INFO AS waiting_sql, bpl.PROCESSLIST_ID AS blocking_thread, bpl.PROCESSLIST_INFO AS blocking_sql, dlw.BLOCKING_ENGINE_TRANSACTION_ID, dlw.REQUESTING_ENGINE_TRANSACTION_ID FROM performance_schema.data_lock_waits dlw INNER JOIN performance_schema.threads rpl ON dlw.REQUESTING_ENGINE_TRANSACTION_ID = rpl.THREAD_ID INNER JOIN performance_schema.threads bpl ON dlw.BLOCKING_ENGINE_TRANSACTION_ID = bpl.THREAD_ID INNER JOIN information_schema.INNODB_TRX rtrx ON dlw.REQUESTING_ENGINE_TRANSACTION_ID = rtrx.TRX_ID INNER JOIN information_schema.INNODB_TRX btrx ON dlw.BLOCKING_ENGINE_TRANSACTION_ID = btrx.TRX_ID;
- 这个结果里
waiting_thread就是你该KILL的目标(不是blocking_thread,它可能早已结束) - 注意
data_lock_waits只存“正在等待”的关系,一旦超时回滚或被 Kill,记录就消失,所以得快查 - 如果
blocking_sql是 NULL,不代表没 SQL,而是它当前没在执行语句(比如事务空闲但锁未释放),此时要结合btrx.TRX_QUERY看
实际排查中,最常被忽略的是锁采集开关和版本兼容性——8.0 开了 performance_schema 不等于开了锁采集,5.7 的 INNODB_LOCKS 查不到,未必是没锁,很可能是锁已释放或采集未触发。别急着 kill,先确认锁是否真存在、是否还在生效。

















