MySQL Redo 日志

166 阅读6分钟

1. 基本概念

Redo log(重做日志)是 MySQL InnoDB 存储引擎的核心组件之一,主要用于实现事务的持久性(Durability)和崩溃恢复能力,Redo Log可以用来恢复提交后的物理数据页,不过只能恢复到最后一次提交的位置。

Redo Log通常包含两部分:一部分是内存中的日志缓冲,称作Redo Log Buffer,这部分日志容易丢失;另一部分是存放在物理磁盘上的重做日志文件,称为Redo Log File,这部分数据不易丢失

2. 基本原理

在MySQL发生故障时,尽力避免内存中的脏页数据写入数据表的IBD文件。在重启MySQL服务时,可以根据Redo Log恢复事务已经提交但是还未写入IBD文件中的数据,从而对事务提交的数据进行持久化操作。

在发生故障重启后Redo Log Buffer中的数据会丢失,但是Redo Log File中的数据会恢复到IBD文件中。 image.png

3. 刷盘规则

在MySQL InnoDB中,"后台进程异步刷脏页"是指由InnoDB引擎的​​Page Cleaner线程​​负责将内存中被修改但未写入磁盘的数据页(称为"脏页")异步刷新到磁盘文件的过程。这是InnoDB实现高性能持久化的核心机制之一

3.1. 什么是脏页(Dirty Page)

  • 当数据页在Buffer Pool中被修改后,内存版本与磁盘版本不一致
  • 这种被修改但未刷盘的数据页称为"脏页"
  • 例如:执行UPDATE语句后,相关数据页变为脏页

3.2. 为什么需要异步刷盘

  • ​性能优化​​:避免每次事务提交都同步写磁盘(随机I/O性能差)
  • ​资源利用​​:利用系统空闲时段进行I/O操作
  • ​批量处理​​:合并多次修改一次性写入

3.3. 刷脏页的核心流程

image.png

3.4. 事务提交时的刷盘规则(关键参数)

Redo Log 的刷盘规则是 InnoDB 实现事务持久性(Durability)的核心机制,其行为主要由 innodb_flush_log_at_trx_commit 参数控制,同时受后台线程和系统状态影响。以下是完整的刷盘规则分析:

innodb_flush_log_at_trx_commit 值刷盘行为数据安全性性能适用场景
​1 (默认)​每次事务提交时同步刷盘(fsync)最高,确保提交事务绝不丢失最低金融交易等严格要求数据安全的场景
​0​每秒刷盘一次,提交时只写入OS缓存可能丢失最后1秒的事务最高非关键业务,可容忍数据丢失
​2​每次提交写入OS缓存,但不调用fsync可能丢失操作系统缓存中的事务(服务器崩溃时)中等希望平衡性能与安全的场景

3.4.1. 后台异步刷盘规则

即使设置为0或2,后台线程仍会定期刷盘:

  1. ​每秒定时刷盘​​:
    • log_flush_notifier线程每秒触发一次
    • 不受innodb_flush_log_at_trx_commit设置影响
  2. ​Redo Log空间不足时强制刷盘​​: // 伪代码逻辑 if (redo_log_space_used > 75%_of_total) { flush_dirty_pages(); // 强制刷脏页释放redo空间 }
  3. ​检查点(Checkpoint)推进时​​:
    • 后台page_cleaner线程定期创建检查点
    • 需要确保检查点之前的redo log对应的脏页已刷盘

3.4.2.特殊场景刷盘规则

  1. ​DDL操作​​:
    • 所有DDL语句(如ALTER TABLE)会强制同步刷盘
    • 即使innodb_flush_log_at_trx_commit=0也会生效
  2. ​崩溃恢复准备​​:
    • 在shutdown时,会完成所有redo log的刷盘
    • 确保重启后能正确恢复
  3. ​日志切换时​
    • 当redo log文件写满,切换到写一个文件的时候,要确保揪文件的脏页已经刷盘

4. 写入机制

Redo Log主要记录的是物理日志,其文件内容是以顺序循环的方式写入的,一个文件写满会写入另一个文件,最后一个文件写满时,会向第一个文件写数据,并且是覆盖写。

  1. write pos是数据表当前记录所在的位置,随着不断地向数据表中写数据,这个位置会向后移动,当移动到最后一个文件的最后一个位置时,又回回到第一个文件的开始位置进行写操作。
  2. checkpoint是当前要擦除的位置,这个位置也是向后移动的,移动到最后一个文件的最后一个位置时,也会回到第一个文件的开始位置进行擦除。只不过在擦除记录之前,需要把记录更新到数据文件中;write pos和checkpoint之间存在间隔时,中间的间隔表示还可以记录新的操作。 如果write pos移动的速度较快,追上了checkpoint,则表示数据已经写满,不能再向redo log文件中写数据了。此时,需要停止写入数据,擦除一些记录。 image.png

4.1. Checkpoint机制详解

image.png

4.1.1. Write Pos (当前写入位置)

  • ​物理意义​​:指向下一个待写入的redo记录位置
  • ​移动规则​​:
    • 每次事务写入时向前推进
    • 到达文件末尾后跳转到第一个文件开头
  • ​数学表达​​:write_pos = (write_pos + redo_record_size) % total_redo_size

4.1.2. Checkpoint (检查点位置)

  • ​物理意义​​:所有在此之前的修改已持久化到IBD文件
  • ​移动条件​​:
    • 脏页刷盘完成后推进
    • 必须保证 checkpoint_lsn <= flushed_to_disk_lsn
  • ​关键约束​​:checkpoint_pos 永远不能超过 write_pos

4.1.3. Checkpoint LSN的本质

  • ​物理意义​​:记录已持久化到IBD文件的最大LSN位置
  • ​数学表示​​:checkpoint_lsn = max(已刷盘数据页的lsn)
  • ​关键作用​​:确定Redo Log可安全覆盖的起始点

4.1.4. Write Pos的运行原理

  • ​动态指针​​:始终指向Redo Log当前写入位置
  • ​推进条件​​:每次事务写入Redo Log时向前移动
  • ​约束关系​​:必须满足 write_pos <= checkpoint_lsn + redo_log_file_size

4.2. 两者交互的完整流程

4.2.1. 正常运作状态

# 伪代码模拟运作过程
while True:
    # 事务写入阶段
    if 有新事务提交:
        写入redo_log(write_pos)
        write_pos += redo_size
        if write_pos >= total_size:
            write_pos = 0  # 循环写入
    
    # 后台刷盘阶段
    if 需要推进checkpoint:
        刷脏页到ibd文件
        checkpoint_pos = 最新持久化LSN的位置
        if checkpoint_pos >= total_size:
            checkpoint_pos = 0

4.2.2. 空间不足的场景处理

image.png

4.3.与IBD文件的交互流程

4.3.1. 正常写入过程

image.png

 4.3.2. 崩溃恢复过程

def recovery():
    # 1. 定位checkpoint_lsn在redo log中的位置
    checkpoint = read_last_checkpoint() 
    
    # 2. 从checkpoint开始重放redo
    for lsn in range(checkpoint.lsn, log_end_lsn):
        redo = read_redo_record(lsn)
        apply_to_page(redo.page_id, redo.data)
    
    # 3. 应用所有已提交事务
    apply_committed_transactions()

4.4. 检查点推进策略

策略类型触发条件优点缺点
定时检查点innodb_log_checkpoint_freq可预测的I/O压力可能不及时
自适应检查点Redo Log空间使用率自动适应负载可能突发I/O
混合模式定时+自适应平衡性能配置复杂