长事务直接拖垮innodb_history_list_length,导致其持续上涨甚至卡住所有DML;因MVCC需保留旧版本供长事务可见,purge线程无法清理,HISTORY LIST超10000时UPDATE/DELETE明显变慢,且锁持有、主从延迟、回滚性能黑洞等连锁问题随之爆发。

长事务直接拖垮 innodb_history_list_length
这不是“可能变慢”,而是innodb_history_list_length会持续上涨,直到卡住所有 DML。InnoDB 的 MVCC 依赖 undo log 维护历史版本,而长事务会让 purge 线程无法清理已提交事务的旧版本——因为这些版本对长事务仍“可见”。你查 SHOW ENGINE INNODB STATUS\G 时,HISTORY LIST 后面的数字一旦超过 10000(尤其 >50000),UPDATE/DELETE 就开始明显变慢:每个修改操作都要扫描更长的 undo 链来判断行可见性。
- 现象:
SELECT COUNT(*) FROM information_schema.INNODB_TRX返回几十个运行中事务,但SHOW PROCESSLIST里几乎看不到活跃 SQL - 关键指标:用
SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'INNODB_HISTORY_LIST_LENGTH'实时监控 - 注意:
innodb_max_purge_lag不是万能开关,设得太低会导致 INSERT/UPDATE 主动限速,反而放大延迟
锁持有时间远超业务预期
一个事务 START TRANSACTION 后执行了 2 秒 UPDATE,但没 COMMIT,它持有的行锁、间隙锁就一直不释放。这时候另一个事务想改同一行,就会卡在 waiting for this lock to be granted 状态——不是死锁,只是纯等待。高并发下这种等待会像滚雪球一样堆积,最终连接池耗尽,新请求连数据库连接都拿不到。
- 典型场景:Java 服务里用
@Transactional包裹了 HTTP 调用,外部接口响应慢 20 秒,DB 事务就悬停 20 秒 - 验证方法:查
information_schema.INNODB_LOCK_WAITS关联INNODB_TRX和INNODB_LOCKS,看谁在等谁 - 陷阱:
KILL TRX_ID无效——因为事务线程可能已断开,但 InnoDB 内部状态仍是 RUNNING
主从延迟被长事务单点引爆
主库上一个长事务执行 30 秒才 COMMIT,binlog 只在最后时刻刷出一条大事件;从库的 SQL 线程必须串行回放这个事件,期间无法处理其他小事务。结果就是 Seconds_Behind_Master 瞬间跳到 30+ 秒,且无法并行追赶——哪怕开了 slave_parallel_workers,只要这个事务涉及的表在并行复制组里,它就只能排队。
- 风险放大器:微服务架构下,一个跨服务事务(如订单+库存+物流)很容易变成分布式长事务,主库一卡,所有从库同步全滞
- 识别方式:在主库查
SHOW BINLOG EVENTS IN 'mysql-bin.000001' LIMIT 10,看是否有间隔极大、event_size 异常大的 Query event - 补救局限:
STOP SLAVE; START SLAVE不能跳过,只能等它跑完;临时提升slave_parallel_type = LOGICAL_CLOCK也救不了单表大事务
回滚本身就成了性能黑洞
长事务如果中途失败需要 ROLLBACK,MySQL 不是“撤销操作”,而是重放 undo log 逐行恢复——事务改了 50 万行,回滚就要做 50 万次反向写入。这期间连接被占着、undo log 还不能清理、其他事务照样被阻塞。你看到 TRX_STATE = 'ROLLING BACK',其实系统已经半瘫痪。
- 最危险组合:大事务 +
innodb_rollback_on_timeout = OFF(MySQL 5.7+ 默认值),锁等待超时后事务不自动回滚,继续挂着 - 监控盲区:performance_schema 中
events_transactions_current的STATE字段为ACTIVE或COMMITTING,但不会显示 “ROLLING BACK” 直到真正开始回滚 - 真实代价:一次 120 秒的回滚,可能让后续 3 分钟内的所有 DML 响应时间翻倍
INNODB_TRX 只能看到一个静止的时间戳,而问题根源其实在另一台机器的线程堆栈里。



















