长事务未提交是锁等待最常见且易被忽略的根因;需查INNODB_TRX中trx_state='RUNNING'但PROCESSLIST显示Command='Sleep'的悬挂事务,常因应用未commit/rollback或手动begin后中断所致。

长事务未提交是锁等待最常见、也最容易被忽略的根因——它不报错、不慢查询、不占CPU,但能让整张表的DML全部卡住。
查 INNODB_TRX 找出“Sleep 却 RUNNING”的悬挂事务
真正危险的不是正在执行的事务,而是那些 COMMAND = 'Sleep' 却在 INNODB_TRX 里显示 trx_state = 'RUNNING' 的连接。这类事务通常来自应用连接池归还连接前忘了 COMMIT 或 ROLLBACK,或者开发在客户端手动 BEGIN 后直接切走。
- 必须用
JOIN查:单查INNODB_TRX会漏掉线程状态,单查PROCESSLIST又看不出事务状态 - 关键过滤条件:
TIME > 300且INFO为空 → 极大概率是异常中断或静默挂起 -
trx_isolation_level = 'REPEATABLE READ'时,哪怕只执行过一条SELECT,事务一启动就持有MDL_SHARED_READ锁 - 别只盯
trx_id,要结合p.HOST和p.USER定位到具体应用实例或 DBA 终端
用 sys.schema_table_lock_waits 快速定位 MDL 阻塞链
当看到 State = 'Waiting for table metadata lock',说明问题不在行锁,而在元数据锁(MDL)。此时查 INNODB_TRX 和 INNODB_LOCK_WAITS 是无效的。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 执行
SELECT * FROM sys.schema_table_lock_waits\G,直接拿到blocking_pid(持锁者)和waiting_pid(被卡者) - 如果返回为空,先确认
performance_schema已启用:SELECT @@performance_schema应为1 -
blocking_pid为NULL不代表没锁,很可能是隐式事务(BEGIN后只跑了一条SELECT就断开)在持锁 - 该视图不显示
Sleep状态的空闲连接,所以若阻塞源是“挂着但没操作”的连接,得回退到INNODB_TRX + PROCESSLIST联查
别依赖 INNODB_LOCK_WAITS 定位行锁阻塞
在 MySQL 5.7 中,INNODB_LOCK_WAITS 字段精简、关联不稳定,经常查不到完整链路,甚至返回空。不能单独靠它判断谁在等谁。
- 更可靠的做法:从
INNODB_TRX拿到waiting_trx_id,再手动去INNODB_LOCKS查它等待的锁(lock_trx_id = waiting_trx_id),然后根据lock_space+lock_page+lock_rec反推被哪个事务持有的同位置锁 -
blocking_trx_id字段在 5.7 中可能为空,或指向一个早已结束但未清理干净的trx_id;此时必须回到SHOW ENGINE INNODB STATUS\G的TRANSACTIONS段落里找真实持有者 - 如果
INNODB_LOCK_WAITS查不到结果,不代表没锁——可能是等待刚结束、或已退化为死锁(此时要去查LATEST DETECTED DEADLOCK)
KILL 前先确认是否真能安全释放锁
直接 KILL 线程有风险:若该事务已写入大量数据,回滚会消耗 IO 和时间,期间仍占着锁。优先确认它是否还在活跃执行。
- 先用
KILL QUERY [ID]中断当前语句,观察是否释放锁;若无变化,再考虑KILL [ID] - 用
SHOW ENGINE INNODB STATUS\G对照看该trx_id下的mysql tables in use和locked tables是否为0—— 是的话,基本可安全KILL - 注意:
innodb_lock_wait_timeout只控制行锁等待超时,对 MDL 无效;调大它只会让问题更难发现,掩盖真实根因 - 线上核心接口建议设为
5或10秒:失败快,前端能及时降级或重试
最麻烦的不是查不到锁,而是查到了却不敢杀——因为不知道它背后有没有正在跑的业务逻辑、外部调用或事务嵌套。这时候得翻代码、看监控、查日志,把数据库里的“黑盒事务”还原成应用里的“白盒流程”。

















