第 9 篇 与 第 10 篇 分别拆开 RDB 与
AOF;生产里二者常同时启用:定时 RDB
作冷备份,AOF 收窄丢失窗口,而 AOF rewrite 的 BASE
默认带 RDB
preamble(aof-use-rdb-preamble yes)把「快恢复」与「细增量」焊在一起。本篇钉这条组合在崩溃时到底保证什么,并给出可操作的配置决策树。
耐久讨论必须叠加 OS 层语义:fsync
失败的真相 说明 fsync 报错后重试可能得到「假成功」;数据完整性
说明
write→页缓存→磁盘介质每一层都可能丢或腐。Redis
配置只能在自己控制的 appendfsync
与文件组织内承诺 RPO,无法替代磁盘写缓存与运维监控。
本文是「Redis / 缓存内核」系列第 11 篇(共 16 篇)。→ 系列目录
版本锚定:Redis OSS 7.4 / 8.x;
aof-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.rdb 调
rdbLoad 快速还原基线,再重放
.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 数字):
- T0:最近一次成功的 AOF fsync(everysec 下约每秒边界)或 always 下每命令 fsync。
- T1:T0 之后、崩溃前的写入——若仅在页缓存未 fsync,可能丢失。
- T2:最近一次 AOF rewrite 完成时刻——定义 BASE 内容上限;之后增量在 INCR。
- T3:最近一次
BGSAVE完成——dump.rdb切片,与 AOF 链独立。
混合模式 RPO(工程口径):
appendfsync always+ 磁盘/fsync 可靠:RPO ≈ 0(RTT 与 fsync 延迟内)。appendfsync everysec(默认):RPO ≤ ~1 s 已确认写 + 复制未 fsync 部分;与官方文档一致。- 仅 RDB
save、AOF off:RPO = 上次 BGSAVE 至今。
崩溃后行为:
- AOF 损坏尾部 +
aof-load-truncated yes:截断启服,主动放弃尾部命令。 - fsync 错误 + always:进程已退出,不会带假成功继续。
- 见 fsyncgate:底层页已 clean 后,任何「再 fsync 一次」都不能找回数据。
四、配置决策树
下列决策树按「数据角色」分支,不是性能调优清单。
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 纯缓存
appendonly no;靠上游 DB 重建。maxmemory+volatile-*或allkeys-*+ TTL(第 7–8 篇)。- 仍可用
save做加速冷启(可选),丢失窗口由save点决定。
4.2 会话 / 默认生产(秒级 RPO)
appendonly yes、appendfsync everysec、aof-use-rdb-preamble yes。- 保留
save作异地冷备(dump.rdb与 AOF manifest 分开管)。 no-appendfsync-on-rewrite仅在承认 rewrite 窗口更大丢失时开启。stop-writes-on-bgsave-error/ AOF 连续 rewrite 失败应告警。
4.3 强耐久(金融级需再评估)
appendfsync always;并验证:- 磁盘写缓存(HDD/部分 SSD)是否由
hdparm/smart关闭或电池保护; - 云盘 fsync 语义(部分虚拟化仍缓冲);
- fsync 失败后 必须 故障转移而非原地重启假装健康(对齐 数据完整性 端到端观点)。
- 磁盘写缓存(HDD/部分 SSD)是否由
- 复制 +
WAITAOF把副本 fsync 纳入确认(仍不替代跨机房容灾设计)。
4.4 大内存实例
- 混合持久化减轻恢复时间,不减轻
fork/COW;
save与rewrite仍需错峰(CONFIG SET调save ""仅留 AOF rewrite)。 - 关注
lazyfree与持久化叠加的 RSS(第 7 篇)。
五、耐久争论与工程间隙
| 立场 | 主张 | 依据与代价 |
|---|---|---|
| AOF 派 | 细粒度日志 + everysec 够大多数互联网业务 | 官方默认;1 s RPO 需产品认可 |
| RDB 派 | 快照简单、恢复可预期 | 丢窗口大;不适合「每条计费」 |
| 混合派 | preamble 兼顾恢复速度与增量 | rewrite 仍要 fork;格式复杂度高 |
| 反对本地持久化 | Redis 定位内存数据结构,耐久应靠上游 DB + 复制 | antirez 早期观点;与现 AOF 默认并存 |
工程间隙(论文/文档 vs 生产):
- 文档假设 fsync 失败会正确处理;Linux 历史行为 + 重试陷阱见 fsyncgate。
- 「everysec 丢 1 秒」假设进程正常
kill;
kill -9与整机掉电同族,不保证刷完最后一秒。 - 容器无状态编排常关 AOF,却误当「Redis 自带耐久」——角色错位。
开放问题:
- 云厂商托管 Redis 的 fsync 到底落没落盘,外部不可审计时 RPO 只能信 SLA 而非源码。
aof-use-rdb-preamble与模块自定义 RDB 类型的兼容性在升级版本时需redis-check-rdb验证。- 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 同理。
七、小结
三句话小结
- 混合持久化 = AOF rewrite 的 BASE 用 RDB
编码 + INCR 追增量,恢复先
rdbLoad再重放。 - RPO 由
appendfsync与底层 fsync 语义共同决定;everysec 的 ~1 s 窗口是设计行为,不是故障。 - 配置 先定缓存 vs 状态、再定 RPO,然后选 only-cache / everysec+混合 / always+硬件校验三条分支。
上一篇:AOF 命令日志
下一篇:复制 PSYNC
参考资料
- Redis OSS 7.4
源码:
src/aof.c(aof-use-rdb-preamble、rdbSaveRiopreamble 分支、aofManifest)、src/rdb.c(RDBFLAGS_AOF_PREAMBLE)、src/config.c。 - Redis Documentation v7.x, Persistence、How Redis persistence works、AOF with RDB preamble。
- fsync 失败的真相、数据完整性:从 fsync 到端到端校验。
- 第 9 篇 RDB、第 10 篇 AOF、系列 index。
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Redis / 缓存内核】AOF:命令日志 · rewrite · appendfsync
从 aof.c 拆解 AOF 多段清单、feedAppendOnlyFile 写路径、后台 rewrite 与 appendfsync always/everysec/no 各自的丢失窗口及 fsync 失败语义。
【Redis / 缓存内核】TTL 与过期:expires dict · 惰性/主动删除 · lazyfree
拆解 Redis 7.4 的 TTL 机制:expires 辅助表、lookup 路径惰性删除、activeExpireCycle 自适应采样,以及 lazyfree 异步释放大对象对延迟的影响。
【Redis / 缓存内核】maxmemory 策略:近似 LRU/LFU 与 MEMORY DOCTOR
从 evict.c 拆解 volatile/allkeys 与 LRU/LFU/random/ttl 策略矩阵、24 位时钟近似 LRU、淘汰池采样,并对照算法篇真 LRU 与 MEMORY DOCTOR 信号。
【Redis / 缓存内核】RDB:fork COW · BGSAVE · 快照格式
从 rdb.c 拆解 SAVE/BGSAVE 的 fork 写时复制语义、子进程快照路径,以及 REDIS 魔数与 opcode 锚定的 RDB 格式边界。