sys.innodb_lock_waits的blocking_pid不可靠,因其依赖PROCESSLIST.Id,存在为空、指向内部线程、无法反映持锁动作三大缺陷;须通过INNODB_TRX→data_locks→threads→events_statements_history逆向追踪真实锁源头。

直接查 sys.innodb_lock_waits 能看到阻塞关系,但“锁源头”不等于 blocking_pid——它只告诉你谁正在被等,不保证那人真在持锁、也没提供完整会话上下文。必须补关联才能定位真实源头。
为什么 sys.innodb_lock_waits 的 blocking_pid 不可靠
这个字段值来自 information_schema.PROCESSLIST.Id,但它有三个硬伤:
-
blocking_pid为空或为 0:常见于事务已提交但锁未及时清理、或阻塞者是内部线程(如 DDL 线程),不在PROCESSLIST中显示 -
blocking_query经常为空:因为事务可能只执行了BEGIN就挂起,INFO字段没内容 - 即使有值,也只反映“当前正在执行的语句”,不是持锁动作本身(比如
SELECT ... FOR UPDATE已执行完,但事务没提交,锁还在)
真正能定位源头的链路是 INNODB_TRX → data_locks → threads → events_statements_history
要揪出“谁在持锁且不释放”,不能只看等待,得逆向追踪锁的归属和行为痕迹:
- 先查
INFORMATION_SCHEMA.INNODB_TRX找TRX_STATE = 'RUNNING'且TRX_STARTED很早的事务(比如 >60 秒),重点关注TRX_MYSQL_THREAD_ID - 用该 ID 去
performance_schema.threads查THREAD_ID,再 JOINevents_statements_history拉最近 5 条语句,看是否有UPDATE、DELETE或SELECT ... FOR UPDATE - 如果
TRX_QUERY为空,但TRX_ROWS_LOCKED > 1000或TRX_WAITING_TRX_ID IS NOT NULL,基本可断定是隐式长事务 - 注意:云数据库(如阿里云 RDS)常屏蔽
PROCESSLIST全量视图,但INNODB_TRX和performance_schema表一般仍可查(需CONNECTION_ADMIN或SUPER权限)
查不到数据?先确认三件事,缺一不可
sys.innodb_lock_waits 是合成视图,底层依赖 performance_schema 的数据链,断了就全空:
- 确认
performance_schema已启用:SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema';结果必须是ON - 检查关键 consumers 是否开启:
SELECT NAME, ENABLED FROM performance_schema.setup_consumers WHERE NAME IN ('events_statements_current', 'events_transactions_current', 'global_instrumentation');全部应为YES - 权限不足会导致字段静默缺失:必须授予
GRANT SELECT ON performance_schema.* TO 'user'@'%'; GRANT PROCESS ON *.* TO 'user'@'%'; FLUSH PRIVILEGES;
别把 sys.schema_table_lock_waits 当成万能解药
看到 Waiting for table metadata lock 就去查这个视图?小心误判:
- 它只管元数据锁(MDL),和行锁/间隙锁完全无关;查它之前,先用
SHOW ENGINE INNODB STATUS\G看有没有LATEST DETECTED DEADLOCK,排除死锁干扰 -
schema_table_lock_waits显示的 “blocking” 方,可能是持有 S 锁的SELECT,但真正卡住 DDL 的,往往是后续试图升级为 X 锁的会话(比如另一个UPDATE正在等行锁,导致无法获取 MDL 写锁) - MDL 阻塞常由连接池未 close、脚本忘
COMMIT引起,这类事务在INNODB_TRX里表现为TRX_STATE = 'RUNNING'+TRX_STARTED很早 +TRX_ROWS_MODIFIED = 0
最易被忽略的点:锁源头未必是 SQL 执行中,而是事务生命周期失控——BEGIN 后没 COMMIT,连接被复用,锁就一直挂着。这时候 PROCESSLIST 里只显示 Sleep,但 INNODB_TRX 才暴露真相。


















