要识别阻塞他人的长事务,需联查INNODB_TRX、INNODB_LOCK_WAITS及(MySQL 8.0.1+)performance_schema.data_locks三表;直接KILL风险高,应优先切流控源、等待自然结束,并辅以应用层可中断事务设计与完整日志追踪。

如何识别正在阻塞别人的长事务
直接查 information_schema.INNODB_TRX 只能看到事务本身,但看不出它锁了谁、被谁等。真正要定位“谁在被这个事务拖住”,得连查三张表:INNODB_TRX(当前事务)、INNODB_LOCKS(锁信息,MySQL 8.0.1+ 已废弃,改用 performance_schema.data_locks)、INNODB_LOCK_WAITS(谁在等谁)。更实用的是这条组合查询:
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_TRX b JOIN information_schema.INNODB_LOCK_WAITS w ON b.trx_id = w.blocking_trx_id JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;
如果返回空,说明当前没有显式锁等待;但不等于没风险——长事务仍可能持有 gap lock 或 next-key lock,阻止 DDL 或其他范围写入。
为什么不能直接 KILL 会话
KILL CONNECTION {id} 确实能终止会话,但后果不可控:事务回滚可能持续数分钟(尤其涉及大更新或未提交的 BLOB 写入),期间仍持锁、占用 undo log、拖慢整个实例 IO。更糟的是,应用层若没做幂等或重试兜底,KILL 后可能触发重复提交或状态不一致。
- 事务回滚不是“瞬间释放”,而是按 undo log 逆向执行,速度通常只有提交的 1/3~1/5
-
KILL不区分事务阶段:哪怕只差最后一条UPDATE就提交,也会强制回滚全部 - 某些 ORM(如 Django 的
atomic块)在连接中断后可能静默重连并重放部分语句
先停写入再优雅退出的实操步骤
核心逻辑是“让事务自然走到终点,而不是强行打断”。这要求你控制住源头——应用写入流。关键不是停服务,而是切走流量 + 阻断新事务入口:
- 对 Web 应用:在负载均衡层(如 Nginx、ALB)把该服务的写请求路由到维护页,读请求可继续放行(需确认无写依赖的读)
- 对后台任务:临时下线 cron job 或暂停消息队列消费者(如
rabbitmqctl stop_app或 Kafkapause某 consumer group) - 检查
SHOW PROCESSLIST中是否有Command: Sleep且Time超过 300 秒的连接——这些很可能是长事务空闲连接,可安全KILL,它们没持锁 - 等所有活跃事务
trx_state = 'COMMITTING'或'RUNNING'逐步收尾;用SELECT * FROM information_schema.INNODB_TRX WHERE TIME > 60每 30 秒轮询一次,直到结果为空
过程中别碰 innodb_lock_wait_timeout 或 wait_timeout 这类全局变量——改了会影响其他正常会话。
应用层配合的关键点
光靠 DBA 操作不够,应用必须支持“可中断的事务边界”。常见陷阱是把整个 HTTP 请求包在一个事务里,或用 autocommit=False 后忘了 commit:
- Django:避免在视图顶层用
@transaction.atomic包裹耗时操作;改用select_for_update(nowait=True)主动失败,而非死等 - Go(database/sql):确保每个
tx, err := db.Begin()都有明确的defer tx.Rollback()+if err == nil { tx.Commit() }成对逻辑 - Java(Spring):慎用
@Transactional(propagation = Propagation.REQUIRED)套在异步方法上——事务上下文不会跨线程传递,极易漏提交
最易被忽略的是:应用日志里根本没记录事务开启和结束时间。没日志,就无法反推哪个接口卡住了事务——建议在事务开始前打 INFO 日志,含 trace_id 和 SQL 摘要。


















