谈谈 Redis 的持久化机制:RDB 与 AOF 的区别?

#中间件#面试八股

谈谈 Redis 的持久化机制:RDB 与 AOF 的区别?

结论:RDB 是某时刻的全量快照,恢复快、体积小,但会丢两次快照之间的数据;AOF 是逐条记录写命令的日志,丢数据少、可读可重放,但文件大、恢复慢。生产上主流做法是 AOF + everysec,或 7.0 的多文件 AOF 配合 RDB 前置(混合持久化),在"少丢数据"和"快速恢复"之间取平衡。

一、机制对照

维度RDBAOF
记录内容内存二进制快照写命令(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 重写失败,这三项才是持久化真正的风险点。
笔记加载中…