土法炼钢兴趣小组的算法知识备份

【Redis / 缓存内核】混合持久化与崩溃语义

文章导航

分类入口
databasestorage
标签入口
#redis#hybrid-persistence#rdb-preamble#aof#durability#fsync#config#7.4

目录

第 9 篇第 10 篇 分别拆开 RDB 与 AOF;生产里二者常同时启用:定时 RDB 作冷备份,AOF 收窄丢失窗口,而 AOF rewrite 的 BASE 默认带 RDB preambleaof-use-rdb-preamble yes)把「快恢复」与「细增量」焊在一起。本篇钉这条组合在崩溃时到底保证什么,并给出可操作的配置决策树。

耐久讨论必须叠加 OS 层语义:fsync 失败的真相 说明 fsync 报错后重试可能得到「假成功」;数据完整性 说明 write→页缓存→磁盘介质每一层都可能丢或腐。Redis 配置只能在自己控制的 appendfsync 与文件组织内承诺 RPO,无法替代磁盘写缓存与运维监控。

本文是「Redis / 缓存内核」系列第 11 篇(共 16 篇)。→ 系列目录

版本锚定:Redis OSS 7.4 / 8.xaof-use-rdb-preamble 默认 yes


一、混合持久化是什么

混合持久化(官方术语 RDB preamble in AOF)指:AOF rewrite 产生的 BASE 文件不是纯命令流,而先写一段 RDB 二进制rdbSaveRio(..., RDBFLAGS_AOF_PREAMBLE)),再接 rewrite 期间积累的增量 AOF(若有)或由后续 INCR 文件接续。

manifest 中表现为:

appendonly.aof.<seq>.base.rdb   # 实为 RDB 格式 + 可能尾部命令
appendonly.aof.<seq>.incr.aof   # rewrite 之后的在线命令

启动加载:对 .base.rdbrdbLoad 快速还原基线,再重放 .incr.aof 追上崩溃前状态。相对「纯命令 BASE」,恢复时间从「与数据集命令史长度成正比」降为「与对象规模成正比 + 短 INCR」。

这与 LSM「全量快照 + 增量 WAL」同构,但 Redis 单进程内完成,不分离存储引擎。

flowchart LR
  mem["in-memory dataset"]
  rw["AOF rewrite child"]
  base["BASE .base.rdb<br/>RDB preamble"]
  incr["INCR .incr.aof<br/>online writes"]
  mem --> rw --> base
  mem --> incr
  restart["restart"] --> loadRDB["rdbLoad BASE"]
  loadRDB --> replay["replay INCR"]

二、与独立 RDB + AOF 并存的区别

典型 redis.conf 同时有:

save 900 1
save 300 10
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes

这里存在两条 RDB 线

来源 文件 用途
save / BGSAVE dump.rdb(可配置) 冷备、复制全量、运维导出
AOF rewrite *.base.rdb 仅服务 AOF 恢复链,不自动替代 dump.rdb

崩溃重启默认走 AOF(appendonly yes 时);dump.rdb 仅在无 AOF 或显式策略下参与。运维备份仍需理解哪条链是「权威恢复源」。


三、崩溃语义:时间线与 RPO

用时间线合并 第 9第 10 篇结论(不编造具体 ms 数字):

  1. T0:最近一次成功的 AOF fsync(everysec 下约每秒边界)或 always 下每命令 fsync。
  2. T1:T0 之后、崩溃前的写入——若仅在页缓存未 fsync,可能丢失。
  3. T2:最近一次 AOF rewrite 完成时刻——定义 BASE 内容上限;之后增量在 INCR。
  4. T3:最近一次 BGSAVE 完成——dump.rdb 切片,与 AOF 链独立。

混合模式 RPO(工程口径)

崩溃后行为


四、配置决策树

下列决策树按「数据角色」分支,不是性能调优清单。

flowchart TD
  start["Need durability?"]
  cache["Pure cache<br/>rebuild from DB"]
  store["State must survive restart"]
  start --> cache
  start --> store
  cache --> noaof["appendonly no<br/>maxmemory + TTL"]
  store --> rpo["Define RPO"]
  rpo --> sec["RPO ≤ ~1s acceptable"]
  rpo --> zero["RPO ≈ 0 required"]
  sec --> mix["appendonly yes<br/>appendfsync everysec<br/>aof-use-rdb-preamble yes<br/>save for cold backup"]
  zero --> alw["appendonly yes<br/>appendfsync always<br/>+ reliable fsync path"]
  alw --> hw["Verify disk write cache off<br/>or battery-backed cache"]
  mix --> mon["Monitor: aof_delayed_fsync<br/>last bgsave/aof_rewrite status"]

4.1 纯缓存

4.2 会话 / 默认生产(秒级 RPO)

4.3 强耐久(金融级需再评估)

4.4 大内存实例


五、耐久争论与工程间隙

立场 主张 依据与代价
AOF 派 细粒度日志 + everysec 够大多数互联网业务 官方默认;1 s RPO 需产品认可
RDB 派 快照简单、恢复可预期 丢窗口大;不适合「每条计费」
混合派 preamble 兼顾恢复速度与增量 rewrite 仍要 fork;格式复杂度高
反对本地持久化 Redis 定位内存数据结构,耐久应靠上游 DB + 复制 antirez 早期观点;与现 AOF 默认并存

工程间隙(论文/文档 vs 生产):

开放问题

  1. 云厂商托管 Redis 的 fsync 到底落没落盘,外部不可审计时 RPO 只能信 SLA 而非源码。
  2. aof-use-rdb-preamble 与模块自定义 RDB 类型的兼容性在升级版本时需 redis-check-rdb 验证。
  3. Valkey 分叉后混合持久化格式是否长期与 Redis 对齐,见 第 16 篇

六、常见误解

6.1 「混合持久化 = RDB + AOF 各写一份完整数据」

不对。Preamble 是 AOF 链的 BASE 段,不是额外再 dump 一份 dump.rdb;空间上 rewrite 替换旧 BASE,不是简单叠加双份全量。

6.2 「开了 preamble 就可以关 appendonly」

不对。preamble 依附 AOF rewrite;关 appendonly 则无 INCR 链,只剩独立 RDB 逻辑。

6.3 「复制可以替代 AOF」

复制解决的是多副本可用性与读扩展;从节点延迟与异步复制下,主挂仍可能丢已确认但未复制的写。WAIT 缩短窗口但不等于磁盘持久化。

6.4 「always 模式下 fsync 错误可以忽略继续跑」

Redis 直接退出。忽略错误违背「已应答写已持久」契约,与 PostgreSQL PANIC 同理。


七、小结

三句话小结

  1. 混合持久化 = AOF rewrite 的 BASE 用 RDB 编码 + INCR 追增量,恢复先 rdbLoad 再重放。
  2. RPOappendfsync 与底层 fsync 语义共同决定;everysec 的 ~1 s 窗口是设计行为,不是故障。
  3. 配置 先定缓存 vs 状态、再定 RPO,然后选 only-cache / everysec+混合 / always+硬件校验三条分支。

上一篇AOF 命令日志

下一篇复制 PSYNC


参考资料

  1. Redis OSS 7.4 源码:src/aof.caof-use-rdb-preamblerdbSaveRio preamble 分支、aofManifest)、src/rdb.cRDBFLAGS_AOF_PREAMBLE)、src/config.c
  2. Redis Documentation v7.x, PersistenceHow Redis persistence worksAOF with RDB preamble
  3. fsync 失败的真相数据完整性:从 fsync 到端到端校验
  4. 第 9 篇 RDB第 10 篇 AOF系列 index

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .