undo log为insert生成insert undo,记录主键值及“记录不存在”的旧状态,事务提交后可立即清除;redo log记录页内物理变更(如mlog_write_string),先写log_buffer,刷盘策略由innodb_flush_log_at_trx_commit控制。

INSERT语句执行时,Undo Log和Redo Log是并行写入的,但目的、内容、生命周期完全不同:Undo Log记录“插入前为空”,用于回滚和MVCC可见性判断;Redo Log记录“页内某偏移处写入了哪几个字节”,用于崩溃后重放变更。
Undo Log怎么为INSERT生成
InnoDB对INSERT生成的是insert undo,它只在事务未提交前存在,提交后可立即清除(不需等待purge线程)。
- 内容是逻辑日志:记录被插入的主键值(或唯一索引值),以及整行数据的“旧状态”——对INSERT而言,旧状态就是“该记录不存在”
- 不记录物理页地址,只记录事务ID(
TRX_ID)和回滚指针(ROLL_PTR),后者指向该事务的上一个undo log节点 - 如果INSERT触发唯一约束冲突,会先尝试读取已有记录,此时可能访问到其他事务的
update/delete undo来构造一致性视图,但INSERT本身只产生自己的insert undo - 若开启
innodb_rollback_on_timeout或显式ROLLBACK,InnoDB用该undo log把刚插入的记录从聚簇索引和二级索引中物理删除
Redo Log怎么为INSERT生成
Redo Log记录的是物理变更,粒度是“数据页内的mlog byte stream”,不是SQL语义。
- INSERT会触发聚簇索引页的分裂或记录插入,Redo Log记录的是页内具体操作:比如
mlog_write_string写入新记录、mlog_init_file_page初始化新页、mlog_multi_write批量更新页头等 - 即使INSERT没修改任何已有页(如分配新页),也会产生redo日志,因为页分配、文件扩展、页头更新都属于物理变更
- Redo Log先写入
log_buffer,是否立刻刷盘取决于innodb_flush_log_at_trx_commit:-
1:commit时强制fsync到ib_logfile*,最安全 -
2:commit时仅写入OS cache,依赖OS定时刷盘,断电可能丢1秒内事务 -
0:完全交由主线程每秒刷一次,崩溃可能丢失最多1秒事务
-
为什么INSERT必须同时写Undo和Redo
两者不可替代,缺失任一都会破坏ACID:
- 只有Redo没有Undo → 事务无法回滚,MVCC读取会看到未提交的INSERT结果,违反隔离性
- 只有Undo没有Redo → 崩溃后内存脏页丢失,Undo Log虽在磁盘,但无法定位“哪一页该删哪条记录”,回滚动作本身不可靠;且已提交的INSERT无法恢复,违反持久性
- 两阶段提交(2PC)要求:prepare阶段必须确保Redo Log已落盘(含
XID),才能进入commit;而Undo Log在prepare前就已写入,保证回滚链完整 - 注意:
insert undo本身也受Redo保护——它的分配、写入、链接到事务结构的过程,同样会产生Redo Log
容易被忽略的关键点
INSERT的Undo/Redo行为在不同场景下有隐含差异:
- 自增主键INSERT:除了记录本身,还会更新
auto-increment counter,这个更新也生成Redo Log,并受同一innodb_flush_log_at_trx_commit控制 - 批量INSERT(
INSERT ... VALUES (),(),()):仍按单行生成undo log节点,但Redo Log可能合并为更少的物理页操作,减少日志量 - INSERT ... SELECT:源表扫描可能触发大量undo读(构建read view),但目标表的INSERT部分仍只生成自己的
insert undo - 如果INSERT因锁等待超时失败,已写入的Undo Log不会自动清理,要等事务结束才释放;而Redo Log中对应的部分若已刷盘,也不会回退——Redo只管“重做”,不管“是否成功”











