应先查innodb_trx和innodb_lock_waits定位阻塞源,kill blocking_trx_id对应线程而非报错线程;调大innodb_lock_wait_timeout仅延迟失败,不解决锁持有问题,需结合索引优化、事务粒度控制和死锁日志分析根治。

查阻塞源:别急着调参,先看谁在锁表
报错本身不告诉你谁卡住了你,只说“我等不及了”。真正该盯的是 INNODB_TRX 和 INNODB_LOCK_WAITS 这两张信息_schema 表。运行:
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED DESC LIMIT 5;再查等待关系:
SELECT * FROM information_schema.INNODB_LOCK_WAITS;注意输出里的
BLOCKING_TRX_ID 字段——它指向那个真正持锁没释放的事务 ID。别 KILL 报错的那个线程,要 KILL BLOCKING_TRX_ID 对应的线程。改 innodb_lock_wait_timeout 是双刃剑
默认值是 50 秒,设成 120 或 300 看似能绕过报错,但实际只是把问题拖得更隐蔽:
- 全局修改(
SET GLOBAL innodb_lock_wait_timeout = 120)只对新连接生效,旧连接仍用原值 - 设太大(比如 600),用户请求会卡满 10 分钟才失败,体验比立刻报错还差
- 它完全不减少锁持有时间,也不缓解锁竞争,纯属“延迟失败”
SET SESSION innodb_lock_wait_timeout = 90;执行完立刻恢复,别长期开着。
批量 UPDATE/DELETE 容易踩坑
这类操作常因子查询或无索引字段触发全表扫描,进而锁住大量行。比如:
UPDATE t SET status=1 WHERE id IN (SELECT id FROM t2 WHERE x=1);如果
t2.x 没索引,子查询慢 → 主查询锁行久 → 其他事务排队超时。解决方法:- 确保所有
WHERE、JOIN、IN子句里的字段都有索引 - 避免在事务里循环单条更新,改用批量
INSERT ... ON DUPLICATE KEY UPDATE或分页处理(每次 500 行) - 确认没在事务里混用 SELECT FOR UPDATE 和后续 DML,这会让锁提前且持久
死锁和锁等待不是一回事,但日志得一起看
报错信息里写的是 Lock wait timeout exceeded; try restarting transaction,但它背后可能是死锁被误判,也可能是纯等待。关键看 SHOW ENGINE INNODB STATUS\G 输出末尾的 LATEST DETECTED DEADLOCK 段落。如果有,说明 MySQL 已经自动回滚了一个事务;如果没有,才是真实锁等待。另外,务必打开 innodb_print_all_deadlocks = ON,否则死锁日志只留最近一次,排查时容易漏掉根因。
锁等待超时的本质不是“数据库坏了”,而是事务之间资源争抢暴露了设计瓶颈。最常被忽略的点是:应用层事务边界没控制好——比如一个 HTTP 请求开了事务,中间调了外部 API 或做了耗时计算,锁就空挂着。这种问题调参永远治不好。











