谈谈 Redis 的持久化机制:RDB 与 AOF 的区别?
结论:RDB 是某时刻的全量快照,恢复快、体积小,但会丢两次快照之间的数据;AOF 是逐条记录写命令的日志,丢数据少、可读可重放,但文件大、恢复慢。生产上主流做法是
AOF + everysec,或 7.0 的多文件 AOF 配合 RDB 前置(混合持久化),在"少丢数据"和"快速恢复"之间取平衡。
一、机制对照
| 维度 | RDB | AOF |
|---|---|---|
| 记录内容 | 内存二进制快照 | 写命令(RESP 格式追加) |
| 触发方式 | save / bgsave / 自动 save 规则 / 主从全量同步 | appendfsync 策略 / AOF 重写 |
| 子进程 | fork 后由子进程写,主进程继续服务 | 重写时 fork 子进程,日常追加在主进程 |
| 体积 | 小(压缩二进制) | 大(命令文本,重写后缩小) |
| 恢复速度 | 快,直接加载 | 慢,需逐条重放 |
| 数据安全 | 差,丢最后一次快照后的写入 | 好,everysec 最多丢 1 秒 |
| 阻塞风险 | fork 时短暂阻塞,COW 期间内存翻倍 | always 策略下每次写都刷盘,延迟高 |
| 可读性 | 不可读 | 可读,能看出写过哪些命令 |
二、fork + COW:RDB 为什么能"边服务边快照"
bgsave 触发
│
├─ fork() → 子进程(几乎零拷贝,共享父进程页表)
│ └─ 子进程遍历内存,写入临时 rdb 文件 → rename 原子替换
│
└─ 主进程继续处理命令
├─ 只读页:父子共享,不复制
└─ 写入页:触发 COW,复制一份给子进程(父进程改自己的)
内存峰值 ≈ 原内存 + 写期间被改动的页(最坏可接近 2 倍)
因此 bgsave 并不阻塞主线程,但 fork 本身在内存大时会阻塞几十毫秒到数百毫秒,且写放大期间内存压力上升,这是"大实例 fork 抖动"的根因。
三、AOF 的三段与重写
AOF 文件 = 命令追加区(appendonly.aof / 7.0 的 incr 文件)
+ 重写时生成的新基准(base 文件)
+ manifest 清单(7.0 多文件 AOF,记录有哪些文件、谁是 base)
重写逻辑:读当前内存 → 为每个 key 生成一条最小重建命令(如 RPUSH 一次写完整个 list)
→ 子进程写新 base → 主进程把重写期间的增量写入 incr → 切换 manifest
7.0 之前是单个 AOF 文件,重写时用 aof_rewrite_buf 承接增量再追加;7.0 的多文件 AOF 把"重写基准"和"增量"分开,避免了重写期间的双写放大,也便于做增量备份。
四、配置与运维
# RDB:关闭自动快照,交给运维定时 bgsave 或备份系统
save ""
# 或保留规则:900 秒内至少 1 次写 / 300 秒内 10 次 / 60 秒内 1 万次
# save 900 1
# save 300 10
# save 60 10000
# AOF
appendonly yes
appendfsync everysec # always / everysec / no
no-appendfsync-on-rewrite no # 重写期间是否停止刷盘,yes 更稳但可能丢更多
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 混合持久化(4.0+):AOF 重写时前半段用 RDB 格式,恢复更快
aof-use-rdb-preamble yes
# 手工触发与观测
redis-cli BGSAVE
redis-cli BGREWRITEAOF
redis-cli INFO persistence | grep -E "rdb_last_bgsave_status|aof_last_write_status|aof_rewrite_in_progress|rdb_changes_since_last_save"
redis-check-aof --fix appendonly.aof # AOF 尾部损坏时的修复
恢复顺序:若同时开启 AOF,启动时只加载 AOF(AOF 通常更完整);只有 AOF 关闭时才加载 RDB。
五、常见追问
- 问:RDB 和 AOF 能不能同时开?答:能,且推荐。RDB 用于备份与快速恢复,AOF 用于降低丢数据窗口;两者互补而非互斥。
- 问:everysec 真的只丢 1 秒吗?答:正常情况下最多 1 秒;若刷盘线程卡住(磁盘抖动)或
no-appendfsync-on-rewrite yes期间重写耗时长,丢失窗口会放大。 - 问:AOF 文件为什么越写越大?答:记录的是命令而非结果,
INCR一百万次就有一百万条;靠重写把多条命令折叠成一条等价的最小命令集。 - 问:主从复制用的是哪种持久化?答:全量同步时主节点执行 bgsave 生成 RDB 发给从节点(可配置无盘复制
repl-diskless-sync yes),后续靠命令传播,与 AOF 无关。 - 问:持久化会不会拖慢主线程?答:fork 与 COW 会;
appendfsync always会;AOF 重写期间的内存与 IO 竞争也会。规避手段是控制单实例内存(如 16 GB 以内)、独立磁盘、错峰重写。
小结
- RDB 换速度(恢复快、体积小),AOF 换安全(丢得少),混合持久化两者兼得。
- 判断是否够用只看一个指标:可接受丢失多长时间的数据,据此选 everysec 或 always。
- 生产上务必监控 fork 耗时、COW 内存、AOF 重写失败,这三项才是持久化真正的风险点。