Redis 持久化RDB 和 AOF 到底该怎么选

RDB 是全量二进制快照,恢复快,丢两次快照之间的数据。AOF 是命令日志,appendfsync 决定丢多少。重写 fork 子进程读内存,不读旧日志。生产混着开,但没有任何方案能零丢失。

面试官问"持久化方式",其实在等三层答案

"Redis 的持久化方式有哪些"------背出 RDB 和 AOF 两个词,只是入场券。面试官往下一戳,人就冒汗了。

这道题真正在考三层:两种持久化的底层执行逻辑(BGSAVE 里 fork 到底干了什么、AOF 重写读的是内存还是旧文件);出故障时丢多少数据;线上怎么搭。

只背名字的人,卡死在第二层。

RDB:SAVE 和 BGSAVE 根本不是一回事

RDB 干的事很直白:把内存全量 dump 成一份紧凑二进制快照落盘。触发有三条路------手动 SAVE、手动 BGSAVE、配置定时触发。

分水岭在 SAVE 和 BGSAVE:

flowchart TD A[写命令进入 Redis] --> B{用哪种方式生成 RDB} B -->|SAVE| C[主线程串行写盘] C --> C1[期间所有请求被阻塞] B -->|BGSAVE 或自动触发| D[fork 出一个子进程] D --> E[子进程遍历内存写临时文件] D --> F[主线程继续处理读写请求] E --> G[写完 rename 覆盖旧 RDB]

SAVE 是主线程自己干,大内存实例能把整个 Redis 钉死。生产环境别用。

BGSAVE fork 出子进程,主线程接着处理请求------这才是线上跑的那条路。但 fork 不免费:复制页表,内存越大越慢,fork 那一瞬主线程仍然阻塞,只是窗口比 SAVE 短得多。

维度 SAVE BGSAVE
执行线程 主线程 fork 出的子进程
阻塞范围 整个写盘过程 仅 fork 阶段
大内存影响 秒级阻塞,不可接受 fork 阶段短暂阻塞
生产可用 否 是

RDB 的优势实打实:文件紧凑,恢复极快。几十 GB 的实例加载 RDB,往往比回放 AOF 快一个数量级,天生适合冷备和跨机房搬运。

短板同样致命。它是间隔性快照------两次快照之间写进来的数据,机器一当机直接蒸发。丢多少看你配的间隔,可能几分钟,也可能几万条写命令。

AOF 的可靠性,全押在 appendfsync 一个参数上

AOF 走另一条路:每条写命令追加进日志,重启时回放,把数据"演"回来。

可靠性开关就是 appendfsync,三个值对应三个量级的丢数据:

策略 刷盘时机 最多丢多少 性能
always 每条写命令立刻 fsync 几乎不丢 最差,磁盘 IO 打满
everysec 后台线程每秒 fsync 一次 最多 1 秒数据 生产最常用
no 交给操作系统决定 不可控,可能丢几十秒 最快,但没人敢赌

线上配置大致长这样:

conf 复制代码
# RDB 定时快照:三个条件任一满足即触发
save 900 1        # 900 秒内至少 1 次修改
save 300 10
save 60 10000

# AOF
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 混合持久化:4.0 之后支持,AOF 文件头用 RDB 格式
aof-use-rdb-preamble yes

everysec 是和稀泥的选项:把丢数据上限锁死在 1 秒,又没把性能拖垮。绝大多数生产实例都用它。

AOF 重写:这里读的不是旧日志

AOF 有个天然毛病:命令越写越多,文件越来越大。一个计数器自增 100 万次,日志里躺着 100 万条 INCR,内存里其实只有一个数字。

重写来解决这个。而这里有个人人答错的点------重写不读旧 AOF 文件。

flowchart TD A[触发 AOF 重写] --> B[fork 子进程] B --> C[子进程只读当前内存数据] B -.->|不读取| D[旧 AOF 文件] C --> E[生成新 AOF 只保留最终状态] A --> F[重写期间新命令写入重写缓冲区] E --> G[新文件写完后追加缓冲区] F --> G G --> H[替换旧 AOF 文件]

子进程读内存里的当前数据,按最终状态反推一份最短命令集。被覆盖的写、已删除的键、可合并的批量操作,全部丢掉。

重写期间新的写命令不能丢,主线程同时往 AOF 缓冲区和重写缓冲区里写。子进程写完,把重写缓冲区追加上去,再原子替换旧文件。整个过程不阻塞主线程。

被追问"重写为什么安全",答这一句就够:内存快照 + 增量缓冲区,两头都不丢。

单开一个行不行?行,先看代价

组合 丢数据风险 恢复速度 典型场景
只开 RDB 高,两次快照之间的全丢 快 纯缓存、数据可重建
只开 AOF 低,everysec 最多 1 秒 慢,回放命令 数据不能丢、能忍恢复慢
RDB + AOF 混合 低 快,先加载 RDB 再补增量 AOF 生产默认选择

生产上的主流答案是两个一起开:AOF 把丢失量压到最小,RDB 在重启、迁移、冷备时提供一条快速通道。

这儿必须纠正一个流传很广的误区:开了持久化不等于零丢失 。哪种方案都有丢数据的可能,区别只是量级。everysec 丢最多 1 秒,always 号称不丢,但 fsync 完成前掉电依然有窗口,RDB 丢的是整个时间窗。

能主动说出"没有方案能做到绝对零丢失",比背出两种持久化的定义值钱得多。

面试连环追问

Q:SAVE 和 BGSAVE 区别? A:前者主线程执行、全程阻塞;后者 fork 子进程,主线程只在 fork 阶段短暂阻塞。

Q:BGSAVE 期间的新写入会丢吗? A:不会。fork 的 copy-on-write,子进程看到的是 fork 那一刻的内存快照。

Q:always 就绝对不丢数据了? A:几乎不丢,但性能代价极大,极端掉电仍有窗口。别把"几乎"说成"绝对"。

Q:AOF 文件越来越大怎么办? A:fork 子进程读内存生成新 AOF,去掉冗余命令,不读旧文件,重写期间增量写进重写缓冲区。

Q:线上你怎么配? A:RDB + AOF 混合开,appendfsync everysec,用 INFO persistence 盯 rdb_last_bgsave_status 和 aof_last_bgrewrite_status。

一句话收束

只能留一段话背下来,就背这段:

Redis 持久化有 RDB 全量快照和 AOF 命令日志两种。RDB 靠 fork 子进程生成二进制快照,恢复快、适合冷备,但会丢时间窗内的数据,大内存 fork 有短暂阻塞。AOF 记录每条写命令,用 always / everysec / no 三种刷盘策略控制丢失量级,用重写机制压缩文件体积,重写读内存而非旧日志。生产环境建议两者配合,按业务对丢失的容忍度选刷盘策略,同时清楚没有任何方案能做到零丢失。

写在最后

这道题难的地方从来不是记住 RDB、AOF 两个词,是把底层机制和线上取舍串成一条线。

顺便问一句:你们线上是 everysec 还是直接上了 always?有没有遇到过 AOF 重写把内存打爆、或者 fork 卡住主线程的事故?评论区聊聊你们的配置和踩过的坑。

有用的话点个赞。

相关推荐
hweiyu007 小时前
Redis命令:HSTRLEN
redis·缓存
怕浪猫7 小时前
RAG 面试 6 连问,从原理到优化全部覆盖
python·算法·面试
字节渡客8 小时前
Redis键明明过期了,业务还在读到旧数据
数据库·redis·spring
Devlive 开源社区8 小时前
AuthX 正式更名 GrantForge:我们重新做了一遍权限管理系统
大数据·人工智能·架构
微三云生态系统架构师-彭丹10 小时前
抖店OPC智能选品与铺货架构:多店差异化与频率风控设计
架构
前端兰博10 小时前
05-Redis
redis·后端
集智飞行10 小时前
无人机集群通信架构剖析,以及对未来发展的几点判断
架构·无人机
想要打 Acm 的小周同学呀10 小时前
无需自己设计Agent架构的业务系统,依赖第三方Agent,基于SKILL和MCP服务实现企业级内部提效工具开发
架构·agent
OnlineProxy10 小时前
亚马逊多账号运营:如何在规避“关联封号”风险的同时实现电商规模化扩张
服务器·数据库·redis
Dawson Zhu12 小时前
《Agentic Design Patterns》第 4 章导读:反思(Reflection)
人工智能·语言模型·架构·aigc·agi