长事务会引发锁等待和主从延迟,因其持续持有行锁、间隙锁及mdl锁,阻塞dml/ddl操作并导致从库回放卡顿;需通过information_schema.innodb_trx识别超60秒事务并kill线程终止。

长事务会导致锁等待和主从延迟
是的,MySQL 长事务会显著拖慢整体性能。它不只让单个查询变慢,更关键的是会持续持有行锁、间隙锁甚至表级元数据锁(MDL),阻塞其他事务的 DML 和 DDL 操作。在主从架构下,从库回放时若遇到被长事务占用的锁,也会卡住,表现为 Seconds_Behind_Master 持续上涨。
- 一个运行 10 分钟的
SELECT ... FOR UPDATE事务,会让所有试图更新同一行的写操作排队等待,直到它提交或回滚 -
information_schema.INNODB_TRX表里TRX_STATE = 'RUNNING'且TRX_STARTED时间过早,就是典型长事务信号 - DDL 操作(如
ALTER TABLE)在 MySQL 5.6+ 虽支持 online DDL,但仍需获取 MDL 写锁——若此时有长事务正在读该表,DDL 就会 hang 住
如何快速识别和终止长事务
别等监控报警,日常巡检就得主动抓。重点看运行时间超 60 秒的活跃事务,尤其那些没做 COMMIT 或 ROLLBACK 的。
- 查长事务:
SELECT TRX_ID, TRX_STARTED, TRX_STATE, TRX_MYSQL_THREAD_ID, TRX_QUERY FROM information_schema.INNODB_TRX WHERE TIMESTAMPDIFF(SECOND, TRX_STARTED, NOW()) > 60;
- 杀掉线程:
KILL [TRX_MYSQL_THREAD_ID](注意不是TRX_ID) - 生产环境建议加条件过滤,避开系统线程或重要业务线程:
AND USER != 'system_user' AND TRX_QUERY NOT LIKE '%backup%'
事务边界必须由业务代码显式控制
ORM 框架(如 MyBatis、Hibernate)默认不会自动提交,但很多开发者误以为“查完就完事”,结果把事务生命周期无意拖长。真正的问题往往出在事务开启后夹杂了 HTTP 调用、文件读写、循环处理等耗时操作。
- 避免在事务内做
http.Client.Do()、os.ReadFile()或密集 for 循环 - Spring 的
@Transactional默认传播行为是REQUIRED,嵌套调用可能意外延长外层事务——确认是否真需要事务包裹全部逻辑 - 明确设置超时:
@Transactional(timeout = 30)或 MySQL 级配置innodb_lock_wait_timeout=30(注意:后者只影响锁等待,不终止事务本身)
读多写少场景优先用一致性非锁定读
不是所有读都需要加锁。普通 SELECT 在 RC(Read Committed)隔离级别下默认走快照读(consistent read),不加锁也不阻塞写;而 SELECT ... LOCK IN SHARE MODE 或 FOR UPDATE 才会触发行锁——务必确认业务是否真需要当前读。
- 报表类、后台管理页的列表查询,基本不需要加锁,去掉
FOR UPDATE能立刻释放锁压力 - 如果只是校验数据存在性(如“检查用户名是否已注册”),用
SELECT COUNT(*)+ 快照读即可,不必SELECT ... FOR UPDATE - RC 级别下,同一个事务中多次普通
SELECT可能读到不同版本数据(不可重复读),但这对多数查询场景无实质影响











