mysql中锁的等待与超时配置与解决

来源:这里教程网 时间:2026-02-28 20:48:49 作者:

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
才能发现。

相关推荐