undo log空间爆满根本原因是长事务阻塞purge,而非写入量大;必须终止trx_state='running'且trx_query is null的最老事务,才能释放旧版本并清理history list length。

Undo Log空间被撑爆,根本不是因为“写得多”,而是因为“旧版本不能丢”——只要有一个长事务没提交,所有它可能读到的旧数据版本,就全得留着。
长事务如何卡死Undo清理链路
InnoDB的purge线程只敢清理“最老活跃事务开始前”产生的undo记录。一个trx_started = '2026-06-07 12:00:00'的事务不提交,哪怕它只执行了一条UPDATE且trx_rows_modified = 1,所有2026-06-07 12:00:00之后生成的undo都动不了。
-
HISTORY LIST LENGTH持续 > 10000,说明purge已严重滞后 - 查
information_schema.INNODB_TRX时,重点盯trx_state = 'RUNNING'且trx_query IS NULL的记录——这是空闲挂起、最常被忽略的元凶 - 只读事务(
trx_rows_modified = 0)也会钉住快照,阻塞DDL(如ALTER TABLE卡在Waiting for table metadata lock)
Update Undo Log比Insert Undo Log更吃空间
Insert操作产生的Insert Undo Log在事务提交后即可丢弃;但Update/Delete生成的Update Undo Log必须持久化,直到所有依赖该版本的事务结束。大事务中反复UPDATE同一行,会累积多层版本链,每层都占空间。
- 一行被更新100次,就可能产生100个undo版本(取决于隔离级别和read view生命周期)
-
REPEATABLE READ下,事务内第一次SELECT就固定了read view,后续所有UPDATE都要保留旧值供回滚+一致性读 -
READ COMMITTED能缩短undo生命周期,但需确认业务是否允许非可重复读
独立Undo表空间 ≠ 自动缩容
即使启用了innodb_undo_tablespaces > 0,undo文件(如undo_001)也不会自动收缩。文件大小只增不减,除非显式截断。
-
SET GLOBAL innodb_undo_log_truncate = ON不会立刻生效——必须等HISTORY LIST LENGTH降到很低,且对应undo表空间状态变为INACTIVE后才能TRUNCATE - 直接
DROP UNDO TABLESPACE undo_001前,务必查information_schema.FILES确认STATE = 'INACTIVE',否则实例可能崩溃 - 若仍用共享表空间(
innodb_undo_tablespaces = 0),唯一释放空间的办法是mysqldump全库重建
最易被忽略的一点:事务本身没报错、应用日志也正常,但连接已异常断开而连接池未回收——这种“幽灵事务”既不执行SQL也不释放锁,却死死钉住整个undo清理水位线。











