用心打造
VPS知识分享网站

MySQL报Lock wait timeout exceeded,事务锁等待解决方法

MySQL执行更新、删除或加锁读取时出现 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction,表示当前语句等待InnoDB行锁的时间超过了 innodb_lock_wait_timeout这不是普通的查询超时,也不等同于死锁。

处理重点是找出谁在等待、谁持有阻塞锁,以及阻塞事务为何迟迟没有提交。直接调大超时只能延后报错,不能消除锁竞争。

MySQL事务等待InnoDB行锁并定位阻塞会话的示意图

先确认超时参数与事务行为

查看当前会话和全局配置:

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.threadsperformance_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、业务归属和回滚成本。 建议先保留锁等待快照,再处理异常会话;长期方案应落在缩短事务、完善索引和统一资源更新顺序上。

赞(0)
未经允许不得转载;国外VPS测评网 » MySQL报Lock wait timeout exceeded,事务锁等待解决方法
分享到