MySQL执行更新、删除或加锁读取时出现 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction,表示当前语句等待InnoDB行锁的时间超过了 innodb_lock_wait_timeout。这不是普通的查询超时,也不等同于死锁。
处理重点是找出谁在等待、谁持有阻塞锁,以及阻塞事务为何迟迟没有提交。直接调大超时只能延后报错,不能消除锁竞争。

先确认超时参数与事务行为
查看当前会话和全局配置:
SELECT @@SESSION.innodb_lock_wait_timeout,
@@GLOBAL.innodb_lock_wait_timeout;
MySQL 8.4官方文档给出的默认值是50秒,作用范围为GLOBAL和SESSION。它针对InnoDB行锁等待,不适用于普通表锁。发生超时时,默认只回滚当前语句,不会自动回滚整笔事务。
应用收到1205后必须明确执行ROLLBACK或按幂等策略重试,不能假设整笔事务已经清空。 若连接继续复用,未提交事务可能仍持有之前获得的锁。
用data_lock_waits找到阻塞链
MySQL 8.0及8.4可从Performance Schema读取锁等待关系:
SELECT
REQUESTING_ENGINE_TRANSACTION_ID AS waiting_trx,
REQUESTING_THREAD_ID AS waiting_thread,
BLOCKING_ENGINE_TRANSACTION_ID AS blocking_trx,
BLOCKING_THREAD_ID AS blocking_thread
FROM performance_schema.data_lock_waits;
REQUESTING表示正在等待的事务,BLOCKING表示持有冲突锁的事务。继续把线程ID与 performance_schema.threads、performance_schema.events_statements_current 关联,可以确认账号、来源和当前SQL。
SELECT t.THREAD_ID, t.PROCESSLIST_ID, t.PROCESSLIST_USER,
t.PROCESSLIST_HOST, t.PROCESSLIST_TIME,
e.SQL_TEXT
FROM performance_schema.threads AS t
LEFT JOIN performance_schema.events_statements_current AS e
ON e.THREAD_ID = t.THREAD_ID
WHERE t.THREAD_ID IN (
SELECT REQUESTING_THREAD_ID FROM performance_schema.data_lock_waits
UNION
SELECT BLOCKING_THREAD_ID FROM performance_schema.data_lock_waits
);
先保存等待链、SQL和业务来源,再决定是否终止会话。 只看到一条慢SQL就执行KILL,可能中断正在提交的重要业务事务。
临时解除阻塞要控制风险
确认阻塞连接属于异常长事务、测试会话或已失去业务价值后,可以使用连接ID终止:
KILL CONNECTION 12345;
连接ID来自 PROCESSLIST_ID,不是Performance Schema的线程ID。终止连接会触发未提交事务回滚,大事务回滚可能持续较久,并继续占用I/O和锁资源。
如果阻塞事务仍在正常工作,应优先等待或协调业务停止写入。不要批量KILL全部连接,也不要重启MySQL来代替事务判断。 重启会扩大影响,恢复阶段同样可能很慢。
从SQL和应用逻辑消除长事务
常见根因包括应用开启事务后等待外部接口、批量更新范围过大、缺少合适索引导致锁住大量记录,以及多个流程以不同顺序更新相同资源。检查事务持续时间和状态:
SELECT trx_id, trx_mysql_thread_id, trx_started,
trx_state, trx_rows_locked, trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started;
将外部HTTP请求、文件处理和人工等待移出数据库事务;批量任务按主键范围分段提交;为WHERE条件建立能够准确定位目标行的索引;让多个业务流程按一致顺序获取资源。
需要在隔离环境复现并发锁时,可用 萤光云 建立测试数据库,或用按小时计费的 LightNode 做短期压力验证。测试数据与生产账号必须隔离。
不要把调大超时当成根治
临时调整当前会话可以这样做:
SET SESSION innodb_lock_wait_timeout = 20;
交互式或高并发OLTP应用可适当缩短等待,让失败更快返回;离线数据转换可能需要更长等待。调整GLOBAL只影响之后建立的连接,且需要相应权限。
超时越长,阻塞连接占用的线程和应用连接池时间越久。 在没有解决长事务和热点更新前提高数值,可能让连接池更快耗尽。
修改后如何验收
重复执行原业务流程,观察等待关系、事务持续时间和1205错误数量。理想状态是 data_lock_waits 不再长期积压,应用能在超时后正确回滚并进行有限次数的幂等重试。
同时检查更新语句影响行数、索引使用和事务提交位置。验收标准不是报错暂时消失,而是阻塞事务能够及时提交、等待时间可控且业务数据保持一致。
FAQ
Lock wait timeout与死锁有什么区别?
锁等待超时是等待达到设定秒数后失败;启用死锁检测时,InnoDB发现循环依赖会立即选择一个事务回滚,不必等到该超时。
出现1205后可以直接重试吗?
只应在操作幂等、重试次数受限且事务已明确回滚后重试。无条件循环重试会加重热点锁竞争。
为什么修改GLOBAL后旧连接仍是原值?
GLOBAL值用于之后建立的会话。现有连接保留自己的SESSION值,需要重连或单独设置。
温馨提示
生产环境终止事务前要确认连接ID、业务归属和回滚成本。 建议先保留锁等待快照,再处理异常会话;长期方案应落在缩短事务、完善索引和统一资源更新顺序上。


