应查INNODB_TRX中TRX_STARTED超30秒且TRX_QUERY为空的长时间RUNNING事务,它们是未提交的“幽灵锁源”;再联查sys.innodb_lock_waits定位blocking_pid并KILL,而非杀报错线程。

查谁开着事务却不提交
锁等不住,根本不是语句慢,是前面有个事务“空挂着”——开了 BEGIN 或自动开启后,没 COMMIT 也没 ROLLBACK,就卡在 HTTP 调用、RPC 响应、日志写入或异常吞掉的地方。直接看 INFORMATION_SCHEMA.INNODB_TRX 最准:
-
TRX_STARTED超过 30 秒(线上建议阈值)的,优先盯 -
TRX_QUERY为空 ≠ 安全,很可能是只执行了BEGIN就挂住了 - 比对
TRX_MYSQL_THREAD_ID和SHOW PROCESSLIST中State = 'Sleep'且Time很大的线程,基本就是它 - Spring 应用里常见:@Transactional 方法里调了外部服务但没设超时,或
catch住异常却忘了rollback
别杀报错的线程,要杀 blocking_pid
报错那条 SQL 往往执行得飞快,真正堵路的是另一个事务。光看 INNODB_TRX 只知道“谁没提交”,不知道“谁被它堵着”。必须查锁等待链:
- MySQL 8.0+:直接跑
SELECT * FROM sys.innodb_lock_waits,字段清晰,blocking_pid就是你要 KILL 的线程 ID - MySQL 5.7 及之前:用
INFORMATION_SCHEMA.INNODB_LOCK_WAITS联查,注意该表在 8.0.1 后已弃用 -
blocking_query为空不代表安全——可能刚执行完UPDATE就卡在应用层逻辑里了 - KILL 错对象(比如
User = 'system user'的主从同步线程)会引发主从延迟甚至数据不一致
临时调低超时时间反而更有效
把 innodb_lock_wait_timeout 从默认 50 秒改成 300 秒,不会让锁释放更快,只会让问题更难暴露。反而是主动压低能逼出隐患:
- 会话级临时设为 5 秒:
SET SESSION innodb_lock_wait_timeout = 5,立刻复现等待,定位长事务 - 全局改(
SET GLOBAL)只影响新连接,当前连接不受影响,不能救火 - 如果调大后错误变少,说明你掩盖了应用层事务控制缺陷,不是优化
- 线上建议长期设为 10–20 秒,配合监控告警,而不是等它卡满 50 秒才报错
代码和配置上怎么防住
锁本身不是问题,不及时释放才是。重点是让事务生命周期可控、可观测、可中断:
- 禁止跨 HTTP 请求延续事务上下文——ORM 默认开事务不等于可以一路包到底
- 所有数据库操作必须明确包裹在
BEGIN/COMMIT或等价事务管理块中 - 避免
AUTOCOMMIT = 0连接忘记COMMIT,后续所有语句都在同一未提交事务里累积锁 - 远程调用、文件读写、日志打点这些非 DB 操作,绝不能放在事务块内
真正棘手的从来不是锁本身,而是事务边界模糊、异常路径遗漏、外部依赖失控——这些藏在代码深处的“静默挂起”,比慢 SQL 更难监控也更易被忽略。


















