回滚慢本质是undo log逐条重放导致的顺序IO瓶颈,而非锁冲突或SQL错误;需通过INNODB_TRX查trx_rows_modified递减趋势、SHOW ENGINE INNODB STATUS看History list length是否超5000、iostat -x 1观察await>20ms且%util=100%来精准定位IO或purge卡点。

回滚慢不是“卡死”,而是 undo log 处理跟不上
MySQL事务回滚慢,90%以上情况不是锁冲突或 SQL 写错,而是 undo log 正在被逐条重放——这个过程本身不走索引、不并发、纯顺序 IO。你看到 ROLLING BACK 状态持续十几分钟,本质是系统还在读 undo page、构造反向操作、写回数据页,而不是“没反应”。别急着 KILL,先确认它到底卡在哪一环。
用 INNODB_TRX 实时观察 trx_rows_modified 变化
这是最直接的进度指标:该字段表示当前事务**尚未回滚的行数**,会随回滚推进而递减。只要它还在动,说明回滚确实在进行中。
- 执行
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'ROLLING BACK'\G - 重点关注
trx_rows_modified值:若从 523810 缓慢降到 523792,说明每秒约回滚 18 行;若长时间不动(比如 30 秒无变化),大概率是 IO 卡住或 purge 阻塞 - 注意
trx_started时间:如果已运行超 20 分钟,且trx_rows_modified > 100000,基本可判定为大事务拖累 - 别依赖
SHOW PROCESSLIST的State字段——它只显示Killed,不反映真实回滚状态
查 SHOW ENGINE INNODB STATUS\G 里的 History list length
这个值代表待 purge 的 undo log 条目总数。它不等于当前回滚量,但决定了 purge 线程能否及时腾出空间供回滚复用。
- 执行命令后,在输出中找
History list length行:若 > 5000,说明 purge 滞后严重,undo 表空间堆积,回滚可能因等待 purge 而变慢 - 同时看
TRANSACTIONS部分是否有其他长事务(Trx started时间很早),它们会 hold 住 oldest read view,让 purge 完全停摆 - 若
History list length稳定在几百,但回滚仍慢,则问题更可能出在磁盘 IO(见下一条)
用 iostat -x 1 确认是否被磁盘 IO 拖住
回滚本质是大量随机写:undo page 读取 → 构造反向操作 → 修改聚簇索引页 → 写 redo → 刷脏页。这一连串动作对磁盘延迟极其敏感。
- 运行
iostat -x 1(Linux),重点关注await和%util列 - 若
await > 20ms且%util == 100%,说明磁盘已饱和,回滚正在排队等 IO,此时调参数或 KILL 都无效 -
r/s和w/s若远高于日常基线(比如平时写 200 IOPS,现在冲到 1800),基本可锁定为回滚引发的 IO 尖峰 - SSD 也需警惕:某些 NVMe 盘在队列深度不足时,
await会飙升,不是磁盘坏,而是驱动/队列配置不合理
回滚进度不能靠猜,得盯住 trx_rows_modified 的实时衰减趋势、History list length 是否压顶、以及 iostat 里磁盘是否在嘶吼——三者缺一不可。很多人只看一个指标就下结论,结果调参半天,IO 却一直堵在底层。


















