看到报错里明确写着“lock wait timeout exceeded; try restarting transaction”就不是死锁,而是事务单向等待锁超时被强制终止;死锁报错固定为“deadlock found when trying to get lock”,且innodb会自动回滚一个事务。

怎么一眼区分是锁等待超时还是死锁
看到报错里明确写着 Lock wait timeout exceeded; try restarting transaction,就不是死锁;死锁报错固定是 Deadlock found when trying to get lock,且 InnoDB 会自动回滚一个事务。前者意味着“我在等锁,等够时间就被扔出来了”,后者是“我和别人互相卡住,MySQL 强制砍掉一个”。
快速验证:执行 SHOW ENGINE INNODB STATUS\G,翻到 LATEST DETECTED DEADLOCK 区块——为空或时间明显对不上,基本可排除死锁。
查谁在挂住别人:三步联动定位阻塞源
别只看 SHOW PROCESSLIST,它不显示锁关系。真正有效的查法是:
- 先筛出等待中的事务:
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'LOCK WAIT',重点关注trx_started时间和trx_mysql_thread_id - 再查阻塞链:
SELECT * FROM information_schema.INNODB_LOCK_WAITS,拿到blocking_trx_id(肇事者)和requested_lock_id(受害者) - 最后关联线程:
SELECT ID, USER, HOST, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE ID = ?,把blocking_trx_id转成线程 ID 查真实 SQL
如果 STATE 是 Waiting for table metadata lock,说明是 DDL(比如 ALTER TABLE)在等元数据锁,得换路径查 performance_schema.metadata_locks,跟行锁无关。
为什么 UPDATE/DELETE 总卡住
常见硬伤就三个,每种都对应明确的现场特征:
-
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对新连接生效,已连的老连接不会变,这点极易踩坑
锁等待超时不是配置调大就能解决的问题,它本质是事务在执行器阶段卡在行锁、间隙锁或自增锁上——查不到源头,调参只会掩盖问题,甚至让连接池先崩。











