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文件中。
3. 刷盘规则
在MySQL InnoDB中,"后台进程异步刷脏页"是指由InnoDB引擎的Page Cleaner线程负责将内存中被修改但未写入磁盘的数据页(称为"脏页")异步刷新到磁盘文件的过程。这是InnoDB实现高性能持久化的核心机制之一
3.1. 什么是脏页(Dirty Page)
- 当数据页在Buffer Pool中被修改后,内存版本与磁盘版本不一致
- 这种被修改但未刷盘的数据页称为"脏页"
- 例如:执行UPDATE语句后,相关数据页变为脏页
3.2. 为什么需要异步刷盘
- 性能优化:避免每次事务提交都同步写磁盘(随机I/O性能差)
- 资源利用:利用系统空闲时段进行I/O操作
- 批量处理:合并多次修改一次性写入
3.3. 刷脏页的核心流程
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,后台线程仍会定期刷盘:
- 每秒定时刷盘:
- 由
log_flush_notifier线程每秒触发一次 - 不受
innodb_flush_log_at_trx_commit设置影响
- 由
- Redo Log空间不足时强制刷盘: // 伪代码逻辑 if (redo_log_space_used > 75%_of_total) { flush_dirty_pages(); // 强制刷脏页释放redo空间 }
- 检查点(Checkpoint)推进时:
- 后台
page_cleaner线程定期创建检查点 - 需要确保检查点之前的redo log对应的脏页已刷盘
- 后台
3.4.2.特殊场景刷盘规则
- DDL操作:
- 所有DDL语句(如ALTER TABLE)会强制同步刷盘
- 即使
innodb_flush_log_at_trx_commit=0也会生效
- 崩溃恢复准备:
- 在shutdown时,会完成所有redo log的刷盘
- 确保重启后能正确恢复
- 日志切换时
- 当redo log文件写满,切换到写一个文件的时候,要确保揪文件的脏页已经刷盘
4. 写入机制
Redo Log主要记录的是物理日志,其文件内容是以顺序循环的方式写入的,一个文件写满会写入另一个文件,最后一个文件写满时,会向第一个文件写数据,并且是覆盖写。
- write pos是数据表当前记录所在的位置,随着不断地向数据表中写数据,这个位置会向后移动,当移动到最后一个文件的最后一个位置时,又回回到第一个文件的开始位置进行写操作。
- checkpoint是当前要擦除的位置,这个位置也是向后移动的,移动到最后一个文件的最后一个位置时,也会回到第一个文件的开始位置进行擦除。只不过在擦除记录之前,需要把记录更新到数据文件中;write pos和checkpoint之间存在间隔时,中间的间隔表示还可以记录新的操作。
如果write pos移动的速度较快,追上了checkpoint,则表示数据已经写满,不能再向redo log文件中写数据了。此时,需要停止写入数据,擦除一些记录。
4.1. Checkpoint机制详解
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. 空间不足的场景处理
4.3.与IBD文件的交互流程
4.3.1. 正常写入过程
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 |
| 混合模式 | 定时+自适应 | 平衡性能 | 配置复杂 |