主题
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.manifest7.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 就不会丢数据」。