Undo Log在INSERT执行时即持续写入,事务开始后每条INSERT都立即生成undo record以支持MVCC快照读;其压力常被低估,批量插入易致History list length飙升和buffer pool中undo页争抢。

Undo Log在INSERT执行时就已持续写入
很多人误以为Undo Log只在事务回滚时才产生,其实只要事务开始,每条INSERT语句执行时,InnoDB就会立即生成对应的undo record——哪怕只是插入一行。这是因为InnoDB需要为MVCC提供快照读能力,必须保留“该行还不存在”这个历史状态。一个插入10万行的事务,不是等到COMMIT才写日志,而是边插边写10万条undo记录。
Undo Log Buffer打满的直接诱因是长事务+高频写入
Undo Log Buffer(即innodb_log_buffer_size控制的内存区域)本身不持久,但它的刷盘触发条件很敏感:
- 事务提交时强制刷入redo log(间接推动undo落盘)
- Buffer使用量超50%会主动刷盘
- 每秒一次的后台刷盘线程也会检查
当批量INSERT不加事务控制、或单事务包得太宽(比如50万行塞进一个BEGIN…COMMIT),Undo Log Buffer会在几秒内被填满,接着频繁刷盘,拖垮I/O并挤占innodb_buffer_pool空间——此时你看到的“Buffer打满”,本质是undo页反复加载、淘汰、再加载的恶性循环。
和Redo Log Buffer混淆是常见误判点
注意:innodb_log_buffer_size管的是redo,不是undo;Undo Log本身没有独立buffer,它走的是buffer pool里的undo page缓存路径。所以所谓“Undo Log Buffer打满”,其实是buffer pool中undo页占比过高,挤占了用户数据页空间。典型表现是SHOW ENGINE INNODB STATUS里History list length飙升(比如超过百万),同时Innodb_buffer_pool_pages_free持续走低。
这时候调大innodb_log_buffer_size毫无作用——它既不缓存undo,也不缓解undo页争抢。真正该做的是拆事务、加WHERE过滤、避免无意义的全表INSERT SELECT。
INSERT比UPDATE/DELETE更容易被低估Undo压力
因为INSERT不修改老数据,很多人觉得它“轻”。但事实相反:新插入的行在事务提交前对其他事务不可见,InnoDB必须用undo链维护“该行尚未存在”的逻辑删除标记。尤其当主键是UUID或随机值时,还会加剧页分裂,进一步放大undo写入频率和碎片。所以批量INSERT若没控制节奏,Undo压力可能比同等行数的UPDATE还高。
最容易被忽略的一点:即使你用LOAD DATA INFILE这种高效方式,只要没配SET autocommit = 0,它默认仍是每1000行自动提交一次——这会产生上千个微小事务,每个都带undo开销。真正的优化起点,永远是明确事务边界,而不是拼SQL长度或调buffer参数。


















