Lock wait timeout exceeded不是死锁,而是事务等待锁超时被MySQL主动终止;需用SHOW ENGINE INNODB STATUS\G查LATEST DETECTED DEADLOCK段落区分,无则为普通锁等待,应查INNODB_TRX定位持锁事务。

锁等待超时不是“SQL慢”,而是“别人锁着你想要的行”,必须立刻找到持锁事务,否则KILL错线程毫无意义。
怎么一眼区分是死锁还是普通锁等待
别一看到 Lock wait timeout exceeded 就翻死锁日志。先跑这条命令:
SHOW ENGINE INNODB STATUS\G
只看输出里有没有 LATEST DETECTED DEADLOCK 段落。有,说明InnoDB已自动回滚一方,错误是结果而非原因;没有,就是纯锁等待——某个事务正稳稳拿着锁不放,别人在排队。
更轻量的验证方式:
SELECT * FROM sys.innodb_lock_waits\G
如果有返回,blocking_query 字段直接就是真凶SQL;如果空返回,大概率锁已释放,或问题已过去,别在旧日志里死磕。
三表联动查出谁在堵路(复制即用)
SHOW PROCESSLIST 看不到锁关系,必须用 INFORMATION_SCHEMA 的实时快照。核心就三张表:
-
INNODB_TRX:所有活跃事务,重点看TRX_STATE(RUNNING是持锁者,LOCK WAIT是等锁方)和TRX_STARTED(运行超5秒的事务大概率有问题) -
INNODB_LOCK_WAITS:阻塞链,靠BLOCKING_TRX_ID和REQUESTING_TRX_ID把等锁方和持锁方串起来 -
PROCESSLIST:把TRX_MYSQL_THREAD_ID转成真实线程ID,查出INFO字段里的原始SQL
常用组合查询(注意字段别写反):
SELECT t1.TRX_ID waiting_trx_id, t1.TRX_MYSQL_THREAD_ID waiting_thread, t1.TRX_QUERY waiting_query, t2.TRX_ID blocking_trx_id, t2.TRX_MYSQL_THREAD_ID blocking_thread, t2.TRX_QUERY blocking_query FROM INFORMATION_SCHEMA.INNODB_TRX t1 INNER JOIN INFORMATION_SCHEMA.INNODB_LOCK_WAITS w ON t1.TRX_ID = w.REQUESTING_TRX_ID INNER JOIN INFORMATION_SCHEMA.INNODB_TRX t2 ON w.BLOCKING_TRX_ID = t2.TRX_ID;
关键点:t2 是持锁事务,KILL t2.TRX_MYSQL_THREAD_ID 才能解围;KILL t1 没用,锁还在。
为什么 UPDATE/INSERT 总卡住?三个硬伤现场特征
不是所有锁等待都难查,多数有明确线索:
-
UPDATE或DELETE没走索引:用EXPLAIN看执行计划,type是ALL就是全表扫描 → 锁成千上万行 → 别人一碰就等 - 事务里混了外部调用:比如 RPC、HTTP 请求、
SLEEP(),查INNODB_TRX里trx_started和当前时间差超过5秒的,基本就是它 -
INSERT批量撞自增锁:MySQL 5.7 默认innodb_autoinc_lock_mode = 1,整条语句执行完才释放自增锁;高并发下排队明显,SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode'确认后可考虑设为 2(需重启)
innodb_lock_wait_timeout 改多少才不瞎调
这个参数只控制“等多久就放弃”,不解决“为什么等”。设太小(如1秒)会误杀正常慢事务;设太大(如300秒)用户卡五分钟才失败,体验更差。
核心接口建议设为5或10:失败快,前端能及时降级或重试;后台批处理可设120,但前提是事务本身不能真跑两分钟——比如避免在事务里调外部接口。
SET innodb_lock_wait_timeout = 10 只对当前会话生效;SET GLOBAL 对新连接生效(截至2026年8月31日)。
最常被忽略的一点:锁等待超时发生时,报错事务已回滚,但持锁事务仍活着。不查 INNODB_TRX 里的 RUNNING 事务,只调参或重启应用,问题一定反复出现。


















