InnoDB回滚通过逐条重放undo log恢复数据页,阻塞purge线程和undo buffer,导致History list length暴涨、ReadView构造变慢、Buffer_pool_reads飙升及IO队列拥堵,引发全局查询延迟。

回滚期间Undo Log重放阻塞MVCC读视图更新
事务回滚不是“瞬间撤销”,而是逐条重放undo log来恢复数据页状态。这个过程会持续占用purge线程和undo buffer,导致InnoDB无法及时清理旧版本数据——于是后续的SELECT(尤其是READ COMMITTED或REPEATABLE READ隔离级别)在构造ReadView时,必须扫描更长的undo链才能确认可见性,Buffer_pool_reads飙升、InnoDB_rows_read异常增高,表现就是大量查询变慢。
History list length暴涨引发全局锁等待
当SHOW ENGINE INNODB STATUS\G里History list length超过5000,说明undo日志堆积严重。此时任何需要获取一致性读快照的操作(包括普通SELECT),都可能被卡在trx_sys->mutex或purge_sys->latch上等待purge线程腾出空间。这不是行锁,是内核级资源争用,所有并发查询都会排队,Threads_running猛涨但CPU不高,iostat却显示await > 20ms且%util == 100%。
大事务回滚抢占IO带宽,挤占正常查询路径
回滚本质是随机写+顺序读undo页,对磁盘IO模式极其不友好。尤其在机械盘或共享云盘上,它会直接拉高avgqu-sz(平均队列长度),让其他查询的IO请求在队列里等数秒才轮到——这时你看到的“慢查询”其实90%时间都在等IO调度,EXPLAIN看不出问题,slow_query_log里也未必记录(因为执行时间没超long_query_time,但响应延迟远超业务容忍)。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
KILL后回滚反而更慢的底层机制
MySQL的KILL命令不会中断undo重放,只标记事务为“待终止”,线程仍需把当前正在处理的undo page刷完才能退出。如果一个改了50万行的事务已回滚40%,你KILL它,它还得硬扛完剩下60%的undo重放——而此时purge线程可能已被压垮,undo页加载更慢,实际耗时反而比不KILL还长。真正该做的是先查information_schema.INNODB_TRX确认TRX_ROWS_MODIFIED和TRX_STARTED,再决定是否走重建流程。
最常被忽略的一点:回滚慢从来不是孤立事件,它总和长事务、未提交的Sleep连接、MDL锁排队形成正反馈循环。一旦发现History list length持续上涨,第一反应不该是调innodb_max_purge_lag,而是立刻查INNODB_TRX里有没有运行超30秒且TRX_STATE = 'RUNNING'的事务——那才是真正的根因。










