lock wait timeout exceeded 错误是因事务等待行锁超时(默认50秒)导致,根本原因是长事务或未提交事务持锁不放,使其他事务排队阻塞;常见诱因包括应用漏提交、无索引update/delete锁表、select for update粒度不当。

为什么会出现 Lock wait timeout exceeded 错误
这个错误不是 MySQL 崩了,也不是磁盘满了,而是某个事务在等锁时等太久了——默认等 50 秒(innodb_lock_wait_timeout=50),超时就直接报错退出。根本原因是:有长事务或未提交事务占着行锁/表锁不放,别的事务堵在后面排队,排到超时就炸。
常见诱因包括:应用里开了事务但忘了 COMMIT 或 ROLLBACK;执行了没加索引的 UPDATE 或 DELETE,导致锁住整张表;或者用 SELECT ... FOR UPDATE 在高并发下没控制好粒度。
怎么快速定位正在阻塞的事务
别急着 kill,先看谁在卡脖子。连上 MySQL 后,执行:
SELECT * FROM information_schema.INNODB_TRX\G
重点关注字段:TRX_ID(事务 ID)、TRX_STATE(状态,LOCK WAIT 表示正在等锁)、TRX_STARTED(启动时间,越久越可疑)、TRX_QUERY(当前 SQL)。
再查谁在持锁不放:
SELECT * FROM information_schema.INNODB_LOCK_WAITS\G
它会告诉你:BLOCKING_TRX_ID 是谁在挡路,REQUESTING_TRX_ID 是谁在干等。
结合这两个视图,就能串出「A 事务锁着某几行,B 事务正等着 A 释放」的完整链路。
- 如果
TRX_STATE = RUNNING但TRX_STARTED已经几分钟,大概率是应用没提交 -
TRX_QUERY显示的是被阻塞的语句,不是持锁语句——要看BLOCKING_TRX_ID对应的TRX_QUERY - 注意
INNODB_TRX不显示已提交或回滚的事务,所以看不到“历史幽灵”,只管当前活着的
如何安全终止问题事务
确认是脏事务后,用 KILL 干掉它,但必须杀对 ID:
KILL [TRX_MYSQL_THREAD_ID];
注意:TRX_MYSQL_THREAD_ID 来自 INNODB_TRX 的 TRX_MYSQL_THREAD_ID 字段,不是 TRX_ID。杀错会中断正常连接。
- 别用
KILL QUERY,它只停当前语句,事务还活着,锁还在 - 杀之前最好先
SHOW PROCESSLIST看一眼该线程是否真在 idle 或 Sleep,避免误杀活跃业务请求 - 如果持锁事务来自应用连接池(比如 Java 的 HikariCP),杀完后连接可能自动重连,得同步检查应用日志是否有异常重试逻辑
怎么避免下次再踩坑
临时调大 innodb_lock_wait_timeout 只是掩耳盗铃,治标不治本。真正要做的,是让事务短、快、准:
- 所有
BEGIN必须配对COMMIT或ROLLBACK,哪怕进异常分支也要兜底 - 写
UPDATE/DELETE前确保 WHERE 条件走了索引,用EXPLAIN看type是否为range或更好,避免ALL扫描锁全表 - 少用
SELECT ... FOR UPDATE,能用乐观锁(版本号)就别用悲观锁 - 监控层面可定期查
INNODB_TRX中TRX_STARTED超过 30 秒的事务,发告警
最隐蔽的坑是:某些 ORM(比如旧版 Django 或 MyBatis)在事务嵌套或异步上下文中,会静默开启事务却不提交——这种得翻框架文档和实际生成的 SQL 日志才能揪出来。











