2. 快照核心原理:Copy-on-Write、Redirect-on-Write 与 Split-Mirror 机制详解

快照这东西,说白了就是给数据拍一张「瞬间照片」。但怎么拍得又快又不影响业务?这就得靠底层的三大核心机制了。我最早接触快照时,总觉得原理很玄乎,直到亲手在线上环境踩过坑,才真正理解它们各自的脾气。

今天咱们就把这三种机制掰开揉碎。你想想看,存储系统要在毫秒级内完成快照,还不能让正在写的应用卡住,这背后其实是一套精巧的「记账」逻辑。

2.1 Copy-on-Write(写时复制)机制详解

CoW 是最经典、最成熟的快照实现方式。它的核心思想就一句话:「写之前,先复制」。

2.1.1 基本原理

当快照创建时,系统并不复制任何数据。它只是记录了一个时间点标记。真正的数据复制,发生在第一次写入的时候。

具体流程是这样的:

  1. 应用要修改某个数据块 A
  2. 系统检查:这个块在快照创建后有没有被改过?
  3. 如果没改过,先把原始数据 A 复制到快照预留空间
  4. 然后才允许应用写入新数据 A'
  5. 如果已经改过,直接写入,不再复制

关键点:快照只保存「原始数据」,不保存「新数据」。读快照时,系统去快照区找原始块;读当前卷时,去原位置找最新块。

2.1.2 我画的一张流程图

下面这张图展示了 CoW 在第一次写入时的完整路径。我习惯用这种图来跟团队对齐思路,一目了然。

Copy-on-Write 写入流程 Copy-on-Write 写入流程 应用写入请求 首次写入? 是 复制原始块到快照区 写入新数据到原位置 否 直接覆盖写入 写入完成 说明 只有首次写入才触发复制 后续写入直接覆盖 快照区只存原始数据

2.1.3 优缺点分析

维度 说明
优点 创建快照极快(秒级),几乎不占额外空间
优点 读性能不受影响,原始数据仍在原位置
缺点 首次写入延迟增加(需要先复制再写入)
缺点 快照链越长,碎片化越严重

避坑指南:我曾经在一个高并发 OLTP 系统上用过 CoW 快照,结果发现写入延迟从 2ms 飙到了 15ms。原因就是每次写入都要先复制,IO 路径变长了。后来我们改用 SSD 做快照区,才把延迟压下来。记住,CoW 对写密集场景不太友好。

2.2 Redirect-on-Write(重定向写)机制详解

RoW 的思路跟 CoW 正好相反。它不复制原始数据,而是把新数据写到别处。说白了就是:「旧数据不动,新数据另起炉灶」。

2.2.1 基本原理

RoW 维护一个地址映射表。快照创建后,所有写入都重定向到新的存储位置。原始数据原地不动,留给快照用。

  1. 应用要修改数据块 A
  2. 系统不碰原始块 A,直接分配一个新块 B
  3. 把新数据写入块 B
  4. 更新地址映射表:A → B
  5. 读快照时,走旧映射找到原始块 A
  6. 读当前卷时,走新映射找到块 B

我的经验:RoW 特别适合写频繁的场景。因为写入不需要先复制,延迟比 CoW 低很多。我有个客户做视频渲染,每天产生 TB 级中间文件,用 RoW 快照后,写入性能几乎没损失。

2.2.2 地址映射表示例

这是 RoW 的核心数据结构。我习惯用表格来展示映射关系,比文字描述直观得多。

逻辑地址 快照前物理地址 快照后物理地址 状态
LBA 0x1000 PBA 0xA000 PBA 0xA000 未修改
LBA 0x1001 PBA 0xA001 PBA 0xB001 已重定向
LBA 0x1002 PBA 0xA002 PBA 0xB002 已重定向
LBA 0x1003 PBA 0xA003 PBA 0xA003 未修改

2.2.3 优缺点分析

维度 说明
优点 写入性能好,不需要先复制
优点 快照链管理简单,没有碎片问题
缺点 读性能可能下降(需要查映射表)
缺点 映射表占用内存,数据量大时开销不小

避坑指南:RoW 的映射表如果太大,会吃掉大量内存。我记得有一次部署 100TB 的存储池,映射表占了 8GB 内存。后来我们做了分层映射,热数据放内存,冷数据放 SSD,才把内存降下来。

2.3 Split-Mirror(分离镜像)机制详解

Split-Mirror 的思路最直接:「先镜像,再分离」。它需要提前准备一份完整的数据副本。

2.3.1 基本原理

创建快照前,系统已经有一份完整的镜像卷。快照创建时,直接把镜像卷「断开」即可。

  1. 提前创建镜像关系:主卷 A ↔ 镜像卷 B
  2. 所有写入同时发往 A 和 B,保持数据一致
  3. 需要快照时,执行「分离」操作
  4. 镜像卷 B 变成独立的快照卷
  5. 主卷 A 继续服务,可以重新建立新镜像

关键点:Split-Mirror 的快照是完整的物理副本,不是增量数据。所以它占用的空间最大,但恢复速度最快——直接把镜像卷挂上去就行。

2.3.2 我画的一张结构图

这张图展示了 Split-Mirror 在分离前后的状态变化。我经常用它来跟运维同事解释「为什么 Split-Mirror 最耗空间但恢复最快」。

Split-Mirror 分离前后对比 Split-Mirror 分离前后对比 分离前(镜像状态) 主卷 A 实时同步 镜像卷 B 写入 分离后(快照状态) 主卷 A 快照卷 B 已断开 Split-Mirror 关键特征 • 快照是完整物理副本,不是增量数据 • 恢复时直接挂载镜像卷,无需重建 • 占用空间 = 主卷大小 × 快照数量 • 适合对恢复速度要求极高的场景 • 创建快照瞬间完成(仅断开链接) • 但需要提前维护镜像,成本较高

2.3.3 优缺点分析

维度 说明
优点 恢复速度最快,直接挂载镜像卷
优点 对主卷性能影响最小(写入已同步)
缺点 空间占用最大,需要完整副本
缺点 需要提前维护镜像,成本高

我的建议:Split-Mirror 适合核心数据库或关键业务系统。我有个金融客户,他们的交易库必须能在 30 秒内恢复,用的就是 Split-Mirror。虽然贵,但值得。

2.4 三种机制对比总结

最后,我把三种机制放在一起做个对比。你选型时可以直接参考这张表。

维度 Copy-on-Write Redirect-on-Write Split-Mirror
创建速度 极快(秒级) 极快(秒级) 极快(毫秒级)
写入性能 首次写入慢 影响小 无影响
读性能 无影响 可能下降 无影响
空间占用 低(仅增量) 低(仅增量) 高(完整副本)
恢复速度 中等 中等 极快
适用场景 通用场景 写密集场景 关键业务

总结一下:没有最好的机制,只有最合适的。我个人习惯这样选——普通业务用 CoW,写密集用 RoW,核心系统用 Split-Mirror。你想想看,是不是这个道理?