数据持久化的底层写入方式完全指南

1. 从硬件到软件:数据持久化的底层写入方式

在软件系统中,数据持久化最终都要落盘。但“如何落盘”决定了性能、可靠性和恢复能力。本文将深入底层,剖析几种核心的磁盘写入方式。

在开始前,先建立一个基本的存储层次模型:

CPU寄存器

CPU缓存 L1/L2/L3

内存 RAM

操作系统 Page Cache

磁盘 持久化存储

数据持久化的核心目标,在于将易失性内存(RAM)中的数据安全、可靠地转移到非易失性磁盘(Disk)中。这一数据搬运路径的设计,直接决定了系统的性能表现与可靠性水平。


2. WAL(预写日志,Write-Ahead Logging)

2.1 工作原理

数据修改时,先将操作日志顺序写入磁盘,再异步更新数据文件。

写请求

1. 写入 WAL 日志

2. 返回成功

3. 后台异步写入数据文件

2.2 关键特点

  • 写入方式顺序写(日志文件是追加的)
  • 时机同步刷盘(事务提交时强制落盘)
  • 恢复方式:重放(Replay)日志

2.3 优缺点

优点缺点
顺序写性能极高恢复时可能需要重放大量日志
崩溃后可完整恢复需要额外的日志管理逻辑
支持事务的原子性和持久性

2.4 典型实现

  • PostgreSQL WAL
  • MySQL InnoDB Redo Log
  • Kafka 的日志段(Log Segment)
  • RocksDB / LevelDB 的 WAL

一句话总结:先写日志,再写数据;顺序追加,异步落盘。


3. Checkpoint(检查点)

3.1 工作原理

定期将内存中的脏页(已修改的数据页)批量强制刷写到磁盘,并记录一个“检查点”标记。

正常写入

内存脏页积累

触发 Checkpoint

批量刷脏页到磁盘

记录 Checkpoint 位置

3.2 关键特点

  • 写入方式:批量随机写 + 顺序写
  • 时机:周期性触发(如每隔几分钟),或日志达到阈值时
  • 恢复方式:从最近一次完整检查点开始,重放之后的 WAL

3.3 与 WAL 的关系

两者往往配合使用。Checkpoint 保证了恢复时只需重放检查点之后的 WAL,大大缩短了恢复时间。

3.4 典型实现

  • PostgreSQL Checkpoint
  • MySQL InnoDB Checkpoint
  • RocksDB Flush + Compaction

一句话总结:批量刷脏页到磁盘,缩短崩溃恢复时间。


4. Direct I/O(直接 I/O)

4.1 工作原理

绕过操作系统的 Page Cache,数据直接在应用程序和磁盘之间传输

Direct I/O

绕过

应用程序

磁盘

操作系统 Page Cache

4.2 关键特点

  • 写入方式:绕过内核缓存,直接落盘
  • 时机:由应用程序显式控制
  • 优势:减少内核态/用户态切换,避免双缓存(应用缓存 + 系统缓存)

4.3 适用场景

  • 数据库自身已经有完善缓存管理(如 MySQL InnoDB)
  • 需要精确控制 I/O 行为
  • 避免 Page Cache 带来的额外内存占用

4.4 典型实现

  • 打开文件时使用 O_DIRECT 标志(Linux)
  • MySQL InnoDB 的 innodb_flush_method=O_DIRECT

一句话总结:绕过操作系统缓存,应用直接落盘。


5. mmap(内存映射文件)

5.1 工作原理

将磁盘文件直接映射到进程的虚拟内存地址空间,读写内存即读写文件。

mmap 映射

Page Cache

应用程序

虚拟内存

磁盘文件

5.2 关键特点

  • 写入方式:内存映射 + 操作系统异步刷盘
  • 时机:内存写入后,由操作系统在适当时机刷盘
  • 优势:简化编程模型(读写文件像操作内存)

5.3 风险

  • 系统崩溃可能导致数据丢失(已写入内存但未刷盘的部分)
  • 映射大文件时可能消耗大量虚拟地址空间
  • 写入性能不可控(依赖操作系统刷盘策略)

5.4 典型实现

  • 很多数据库使用 mmap 管理索引文件
  • Elasticsearch 的 Lucene 索引
  • LevelDB / RocksDB 的部分实现

一句话总结:把文件映射成内存,读写像操作内存一样简单,但依赖操作系统刷盘。


6. 双写与屏障(Double Write & Write Barrier)

6.1 双写缓冲区(Double Write Buffer)

InnoDB 独有的机制,解决 页断裂(Partial Page Write) 问题。

  • 问题:数据库以 16KB 为单位写入,但操作系统通常以 4KB 为单位刷盘。写入过程中断电,可能导致 16KB 页只写了部分,造成数据页损坏(Page Corruption)。
  • 解决方案:先把修改后的完整页写入一个双写缓冲区(顺序写),然后再写入实际数据文件。

脏页

1. 写入双写缓冲区

2. 将双写缓冲区刷盘

3. 写入实际数据文件

下面用图示说明这个机制如何防止页损坏:

场景:没有双写缓冲区

1. InnoDB 准备写入一个 16KB 的脏页(数据页)
2. 操作系统分 4 次,每次 4KB,将数据写入磁盘
   ┌───────┬───────┬───────┬───────┐
   │ 4KB   │ 4KB   │ 4KB   │ 4KB   │
   └───────┴───────┴───────┴───────┘
        ↓       ↓       ↓
   写入成功  写入成功   🔌 断电!
   
3. 结果:磁盘上的 16KB 页只有前 8KB 是新数据,后 8KB 是旧数据
   → 该页变为“撕裂页”,InnoDB 无法识别 → 数据页损坏

场景:有双写缓冲区

1. InnoDB 先把脏页写到内存中的双写缓冲区(一个 2MB 的连续区域)
2. 将双写缓冲区的内容 fsync 刷到磁盘上的双写文件(顺序写入)
   → 此时磁盘上有了该页的一份完整副本(16KB)
3. 再将脏页写入实际的表空间(ibd 文件)数据文件
   → 此时如果发生断电,导致数据文件中的页部分写入(损坏)

4. 恢复过程:
   - InnoDB 检查数据页:校验和不一致 → 页损坏
   - 从双写文件中找到该页的完整副本(因为双写文件是顺序写,且已完成 fsync)
   - 用双写文件中的副本覆盖损坏的数据页 → 恢复完成!

注意: 双写缓冲区不是用来“防止数据丢失”的,而是用来“防止数据页损坏”的。

数据丢失(Data Loss)

  • 现象:数据库已提交的事务(COMMIT)丢失了,重启后数据没了。
  • 原因:写入操作还在内存(Page Cache 或数据库缓存)中,还没来得及刷到磁盘,断电了。
  • 解决方案:WAL(预写日志) + 同步刷盘(fsync)。

数据页损坏(Page Corruption)

  • 现象:磁盘上的某个 16KB 数据页读不出来了,或者读到的是“一半旧一半新”的混合数据,导致 InnoDB 无法解析这个页。
  • 原因:操作系统以 4KB 为单位刷盘,而 InnoDB 以 16KB 为单位写页。写入过程中断电,16KB 页只写成功了 4KB,导致该页处于“撕裂(Torn)”状态。
  • 解决方案:双写缓冲区(Double Write Buffer)。

6.2 Write Barrier(写屏障)

  • 作用:确保所有在屏障之前的写操作必须先于屏障之后的写操作完成
  • 背景:现代存储设备(SSD、磁盘)有内部缓存和重排序机制,可能导致写操作的顺序不符合预期
  • 应用:使用 fsyncfdatasync 等系统调用,或特定文件系统标志(如 O_SYNC

一句话总结:双写缓冲区解决页断裂,写屏障保证写入顺序。


7. 多种写入方式综合对比

写入方式核心特点适用场景代表技术
WAL顺序写、同步刷盘事务型数据库PostgreSQL WAL, MySQL Redo Log
Checkpoint批量随机写、周期性配合 WAL 缩短恢复时间PostgreSQL, MySQL InnoDB
Direct I/O绕过 Page Cache自管理缓存的应用MySQL InnoDB (O_DIRECT)
mmap内存映射文件索引管理、简单读写Lucene, LevelDB
双写缓冲区防止页断裂确保数据页完整性MySQL InnoDB
Write Barrier保证写入顺序需要写序保证的日志fsync, O_SYNC

8. 实际应用中的组合策略

MySQL InnoDB 为例,展示如何组合这些技术:

周期性

写请求

Redo Log WAL

脏页在内存

Double Write Buffer

数据文件

Checkpoint 线程

恢复时

从最近 Checkpoint 开始

重放 Redo Log

技术在 InnoDB 中的角色
Redo Log (WAL)保证事务持久性,崩溃后重放
Checkpoint定期刷脏页,缩短恢复时间
Double Write防止页断裂
fsync (Write Barrier)确保日志和双写缓冲区落盘
O_DIRECT (Direct I/O)数据文件绕过 Page Cache

9. 总结与选型建议

写入技术核心价值一句话记忆
WAL性能 + 可恢复顺序写日志,异步写数据
Checkpoint缩短恢复时间定期批量刷脏页
Direct I/O控制权交应用绕过系统缓存
mmap简化编程把文件当内存用
双写缓冲区防止页断裂先写缓冲区,再写数据文件
Write Barrier保证写序确保关键写入先完成

9.1 选型建议

场景推荐组合
关系型数据库WAL + Checkpoint + Double Write + Direct I/O
键值存储(LSM-Tree)WAL + mmap
日志系统WAL + 顺序写
内存数据库WAL(AOF)+ Checkpoint(RDB)

参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恋喵大鲤鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值