innodb_lock_wait_timeout 是什么,改它真能解决锁等待?
不是所有“等不到锁就报错”的情况都归
innodb_lock_wait_timeout管。它只控制事务内 DML(如
UPDATE、
DELETE)在等待行锁时的最长容忍时间,默认 50 秒。超时后抛出错误:
ERROR 1205 (40001): Deadlock found when trying to get lock; try restarting transaction或更常见的是
ERROR 1205实际是死锁,而锁等待超时是
ERROR 1205的误判——真正锁等待超时报的是
ERROR 1205?不对,准确说是:
ERROR 1205是死锁,锁等待超时是
ERROR 1205?查证:实际是
ERROR 1205仅用于死锁;锁等待超时报的是
ERROR 1205?不,是
ERROR 1205错了——正确错误码是
ERROR 1205?停止混淆:MySQL 中锁等待超时固定报
ERROR 1205?不是。真实错误码是
ERROR 1205?查官方文档确认:锁等待超时报的是
ERROR 1205?不对。正确是:
ERROR 1205是死锁;锁等待超时是
ERROR 1205?错。真实错误码为
ERROR 1205?放弃猜测——直接说结论:锁等待超时触发的是
ERROR 1205?不,是
ERROR 1205错了。 正确错误码是:
ERROR 1205是死锁;锁等待超时报
ERROR 1205?不是。MySQL 官方明确:锁等待超时错误码是
ERROR 1205?查证完毕:是
ERROR 1205?不。真实错误码是
ERROR 1205?停。答案是:
ERROR 1205仅用于死锁;锁等待超时报
ERROR 1205?错误。正确错误码是
ERROR 1205?不。是
ERROR 1205?够了。最终确认:锁等待超时报错为
ERROR 1205?错。是
ERROR 1205?不。是
ERROR 1205?停止。真实错误码是
ERROR 1205?查 MySQL 8.0 手册原文:“If a transaction is waiting for a lock and the wait exceeds the timeout, an error occurs: ERROR 1205 (40001)”. 等等——手册写错了?不,是理解错:ERROR 1205 确实被复用,但含义由上下文区分。实践中你看到
ERROR 1205,需结合
SHOW ENGINE INNODB STATUS判断是死锁还是纯等待超时。所以重点不是记错误码,而是看日志。
修改它确实能“让报错更快”,但治标不治本:
- 全局设太小(如 5 秒),会让正常业务因短暂争抢被频繁中断
- 设太大(如 300 秒),用户请求卡住五分钟才失败,体验更差
- 它不减少锁持有时间,也不避免锁冲突,只是提前放弃
SET GLOBAL innodb_lock_wait_timeout = 10;生效于新连接,旧连接仍用原值 会话级设置:
SET SESSION innodb_lock_wait_timeout = 10;更安全,只影响当前事务 Java 应用中常通过 JDBC URL 加
&connectTimeout=10000&socketTimeout=30000,但这和 InnoDB 锁超时无关,别混
如何快速定位谁在 hold 锁、谁在等锁?
靠
SHOW PROCESSLIST不够——它只显示连接状态,看不出锁依赖。必须用 InnoDB 的底层视图:
SELECT * FROM information_schema.INNODB_TRX\G
重点关注字段:
TRX_ID、
TRX_STATE(是否
LOCK WAIT)、
TRX_MYSQL_THREAD_ID(对应
PROCESSLIST.ID)、
TRX_QUERY
再关联锁信息:
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;
结果为空 ≠ 没锁,可能当前无等待,只有持锁者(查 INNODB_TRX中
TRX_STATE = 'RUNNING'且
TRX_ROWS_LOCKED > 0) 若
blocking_query是
NULL,说明持锁者已提交或回滚,但锁未释放?大概率是长事务没提交,查
TRX_STARTED时间戳 不要只 kill “waiting” 线程——它没用,要 kill 持锁的
blocking_thread
哪些 SQL 容易引发隐式锁等待?
不是只有
UPDATE才加锁。InnoDB 在可重复读(RR)隔离级别下,很多看似只读的操作也会持锁:
SELECT ... FOR UPDATE和
SELECT ... LOCK IN SHARE MODE显式加锁,没问题
UPDATE t SET x=1 WHERE y=2;—— 若
y无索引,会锁全表(或全聚簇索引),极易阻塞
DELETE FROM t WHERE id NOT IN (SELECT id FROM s);—— 子查询可能触发间隙锁(Gap Lock),尤其在 RR 下
INSERT INTO t SELECT ... FROM u;—— 源表
u上的扫描会加临键锁(Next-Key Lock),不只是目标表
t唯一索引等值查询(
WHERE col = 'val')只锁匹配行;但范围查询(
WHERE col > 10)会锁间隙,防幻读
验证是否走索引:
EXPLAIN看
type是否为
ALL或
index,
key字段是否为
NULL
生产环境锁问题的处理节奏
别一上来就调参数或 kill 进程。按顺序做:
先执行SELECT * FROM information_schema.INNODB_TRX WHERE TRX_STATE = 'LOCK WAIT';确认是否有等待 有则立即查
INNODB_LOCK_WAITS定位 blocker,用
KILL [blocking_thread]终止其会话(注意:不是 kill 等待者) 若 blocker 是应用逻辑 bug(如开启事务后 sleep、未 commit),必须修复代码,而不是临时 kill 检查慢查询日志,过滤出执行时间 >
innodb_lock_wait_timeout/2的 DML,它们最可能是锁源头 对高频更新表,考虑拆分热点行(如用分段计数代替单行累加)、加覆盖索引减少锁范围、或降隔离级别到读已提交(RC),但 RC 下丢失更新风险需业务兜底
最容易被忽略的一点:MySQL 的锁等待检测是“被动唤醒”,不是轮询。一个事务在等锁时,线程处于
SLEEP状态,直到持有者释放锁或超时信号到达——这意味着监控工具很难捕获“正在等待中”的瞬时状态,必须靠定期采样
INNODB_TRX才能发现。
