查information_schema.innodb_trx和innodb_lock_waits联查可直接定位锁表会话;重点排查trx_state为running但trx_started过早的长事务,再通过锁等待视图确认阻塞关系。
查哪些会话在锁表:information_schema.innodb_trx 和 innodb_lock_waits 联查最直接
mysql 表被锁住改不了结构,大概率是某个长事务没提交,占着 dml 锁不放。光看 show processlist 不够——它只显示连接状态,看不到锁等待关系。真正要定位“谁锁了谁”,得查 innodb 的事务和锁视图。
实操建议:
- 先执行
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED DESC LIMIT 5;,重点关注TRX_STATE是RUNNING但TRX_STARTED很早的记录,这类往往是卡住的事务 - 再连查锁等待:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM information_schema.INNODB_LOCK_WAITS w INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id; - 注意
TRX_QUERY可能为NULL——说明事务空闲但没提交,不是 SQL 正在跑,而是应用忘了COMMIT或ROLLBACK
Kill 前必须确认线程归属:KILL CONNECTION 不等于 KILL QUERY
误杀活跃查询可能让业务报错,但误杀一个空闲事务线程反而更危险:它可能正处在分布式事务中间态,或持有外部资源(如文件句柄、缓存锁),直接 KILL 容易引发数据不一致。
实操建议:
- 用
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE ID = ?;先查目标线程详情,确认COMMAND是Sleep且TIME超过 300 秒,再动手 - 优先用
KILL CONNECTION ?(MySQL 5.7+),它断开连接并回滚事务;不要用KILL QUERY ?,它只中断当前语句,事务仍存活 - 如果线程
STATE是Locked或Waiting for table metadata lock,说明它本身也被别的事务堵住了,Kill 它不一定释放锁——得往上追根
DDL 被阻塞时别急着 Kill:ALTER TABLE 自身会拿 MDL 锁,可能反成锁源
常见误区:看到 ALTER TABLE 在 Waiting for table metadata lock 就以为它是受害者,其实它很可能是加锁者。MySQL 8.0 前,ALTER TABLE 会全程持有 MDL_EXCLUSIVE 锁,期间任何 DML 都进不来,而它又在等前面的事务释放表级锁——形成死锁闭环。
实操建议:
- 查
PROCESSLIST里状态含metadata lock的线程,用SHOW ENGINE INNODB STATUS\G看LATEST DETECTED DEADLOCK段,确认是不是 DDL 卡住了别人 - 如果是 DDL 自身阻塞,且无法中止(比如大表 Online DDL 中间态),考虑临时把
lock_wait_timeout调小,让后续 DML 快速失败,避免堆积 - MySQL 8.0+ 可设
ALTER TABLE ... ALGORITHM=INSTANT避免锁表,但仅限加列/改列名等极少数操作,别默认指望它
预防比抢救重要:wait_timeout 和 interactive_timeout 别设成 0
很多线上库的长事务,根源是应用用了连接池但没正确 close 连接,或者 ORM 自动开启事务后异常退出没 rollback。MySQL 默认 wait_timeout=28800(8 小时),意味着空闲连接能挂 8 小时不被清理——足够撑起一堆僵尸事务。
实操建议:
- 生产环境务必把
wait_timeout和interactive_timeout设为 300–600 秒,配合应用层心跳检测,逼连接池主动回收 - 在应用启动时执行
SET SESSION innodb_lock_wait_timeout = 10;,让 DML 等锁超时更快,避免一个慢查询拖垮整张表 - 定期跑脚本监控:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) > 300;,发现就告警,别等 DDL 失败才介入
锁问题从来不是单点故障,而是连接生命周期管理、事务边界设计、SQL 执行习惯三者叠加的结果。调大超时、加监控、改应用代码,这三件事漏掉哪件,下次还得半夜爬起来 Kill 进程。










