跳到正文

Redis 持久化 ​

一句话:RDB 用 fork 出的子进程拍内存快照,AOF 把写命令追加成日志;前者恢复快、丢得多,后者丢得少、恢复慢。混合持久化把两者拼起来,代价是多一次 fork 和磁盘占用。

适用版本:Redis 7.x / 8.x。7.0 起 AOF 改为多文件(multi-part),与 6.x 的单文件 appendonly.aof 布局不同。

为什么需要 ​

Redis 数据在内存里,进程退出就没了。只用 RDB,两次快照之间的写入会丢;只用 AOF,重启要逐条重放,文件还会无限增长。生产要同时回答两个问题:能丢多少(RPO)、多久能恢复(RTO)。持久化方案就是在两者之间做取舍。

核心机制 ​

RDB:fork + 写时复制 ​

BGSAVE 时主线程 fork() 出子进程,子进程遍历内存写二进制快照。fork 不复制数据页,只复制页表,父子进程共享只读内存页;主线程之后修改某页时,内核才复制该页(copy-on-write, COW),子进程看到的始终是 fork 瞬间的视图。

  • 页表大小约为「进程内存 / 4 KB × 8 B」。16 GB 实例约 3200 万个页表项,复制页表可能带来几十到几百毫秒的主线程停顿(经验值,随内核和内存布局变化)。
  • 默认快照策略是 save 3600 1 300 100 60 10000(7.x):3600 秒内至少 1 次修改、或 300 秒内至少 100 次、或 60 秒内至少 10000 次,就触发。save "" 可关闭自动快照。
  • SAVE 在主线程执行,生产禁用;手动备份用 BGSAVE。

AOF:先写缓冲,再按策略刷盘 ​

写命令先进 AOF 缓冲区,write() 到内核 page cache,再由 fsync() 落盘。可靠性由 appendfsync 决定:

策略行为丢失窗口代价
always每条命令 fsync理论为 0磁盘 IOPS 成为瓶颈
everysec(默认)每秒 fsync最多约 1 秒影响很小
no交给操作系统可能几十秒最快,但不可控

AOF 重写(BGREWRITEAOF)也是 fork 子进程,用最精简的命令重写一份。触发条件:auto-aof-rewrite-percentage 100(增长到上次重写后的 2 倍)且 auto-aof-rewrite-min-size 64mb。

7.0 的多文件 AOF ​

7.0 不再维护单个 appendonly.aof,而是放在 appenddirname(默认 appendonlydir)下:

text
appendonlydir/
├── appendonly.aof.manifest          # 权威清单:哪些 base/incr 文件有效
├── appendonly.aof.1.base.rdb       # 基准快照(aof-use-rdb-preamble yes 时为 RDB 格式)
├── appendonly.aof.1.incr.aof       # 增量命令
└── appendonly.aof.2.incr.aof       # 重写后切换的新增量文件

好处是重写时不再需要把旧 AOF 缓冲区全部排空,减少了内存峰值和主线程停顿。aof-use-rdb-preamble 自 5.0 起默认 yes,7.0 起控制的是 base 文件格式,不是「是否启用多文件」。manifest 很小但关键,丢失或损坏会导致启动失败,备份时要一起带走。

重启时加载谁 ​

AOF 开启且文件存在时优先加载 AOF(数据更完整);否则加载 RDB。混合持久化的本质就是「base 用 RDB 格式 + incr 用 AOF 格式」,所以恢复速度接近 RDB,丢失窗口接近 AOF。

最小上手 ​

bash
# 7.x 默认镜像,显式开启 AOF
docker run -d --name redis7 -p 6379:6379 redis:7.4 \
  redis-server --appendonly yes --appendfsync everysec
redis-cli

CONFIG GET appendonly appendfsync appenddirname aof-use-rdb-preamble
BGREWRITEAOF
INFO persistence          # 看 aof_enabled、aof_rewrite_in_progress、rdb_last_bgsave_status

宿主机挂载数据目录后,可以直接看到多文件布局:

bash
ls appendonlydir/
redis-check-aof --fix appendonlydir/appendonly.aof.manifest

7.0+ 的 redis-check-aof 针对多文件布局操作,具体参数以官方文档为准。

生产动作 ​

  • 推荐组合:appendonly yes、appendfsync everysec、aof-use-rdb-preamble yes(5.0+ 默认)、自动 RDB 用 save "" 关闭后低峰手动 BGSAVE。
  • fork 内存:maxmemory 不超过物理内存 50%;预留 COW 峰值。容器里要按 cgroup limit 算,不能按宿主机内存算。
  • 内核参数:vm.overcommit_memory=1,否则 fork 在内存紧张时可能失败;关闭透明大页(THP),否则 COW 的复制粒度变大,fork 延迟和内存放大更明显。
  • 监控 fork:INFO stats 的 latest_fork_usec、INFO persistence 的 rdb_last_bgsave_status、aof_last_bgrewrite_status;used_memory 与 used_memory_rss 的差值反映 COW 放大。
  • 危险操作:不要在生产高峰 BGREWRITEAOF 或 BGSAVE;AOF 重写期间磁盘可能翻倍,先确认剩余空间。
  • 持久化不是备份:误删 key 会同步写进 AOF,备份要落到对象存储或另一台机器。

边界 ​

  • everysec 不是零丢失:极端情况下上一轮 fsync 未完成,本轮会跳过,可能丢约 2 秒(官方文档也承认这一窗口)。
  • RDB 不适合做唯一方案:默认策略下最多可能丢几分钟;它更适合备份、全量同步和快速恢复。
  • fork 不是免费的:内存越大、写入越频繁,COW 放大越明显;大实例要优先考虑主从 + 无盘复制,而不是反复全量快照。
  • AOF 重写受磁盘和 CPU 约束:重写期间 fork 子进程和磁盘写入会与业务争资源,低峰执行。
  • 单机持久化不解决高可用:进程所在机器挂了仍然不可用,持久化只解决重启后的数据恢复。

讲清楚 ​

Redis 持久化只有两条路:RDB 拍快照,AOF 记命令。RDB 靠 fork 和写时复制,文件小、恢复快,但两次快照之间的数据会丢;AOF 按秒刷盘,丢得少,但文件大、恢复慢,还要定期重写。7.0 把 AOF 拆成 base + incr + manifest 多文件,重写时不用排空整个缓冲区。

生产上通常 AOF everysec 加混合持久化,同时用 save "" 关掉自动 RDB、低峰手动备份。最需要盯的是 fork 带来的内存放大和磁盘空间,而不是「开了 AOF 就不会丢数据」。

把一门技术讲透到既懂也会用