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

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

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

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

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

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

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

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

分水岭在 SAVEBGSAVE

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 persistencerdb_last_bgsave_statusaof_last_bgrewrite_status

一句话收束

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

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

写在最后

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

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

有用的话点个赞。

相关推荐
墨天梦1 小时前
07-KVCache与缓存友好架构
缓存·架构
linan1011 小时前
android调用C++通用方式
linux·架构·智能硬件
白远山2 小时前
自助健身小程序源码:架构拆解、核心链路与本地部署实战
java·架构·uni-app·需求分析
国科安芯2 小时前
商业航天星载数据管理单元的存储容错与接口集成方案研究
嵌入式硬件·架构·ecc·商业航天·抗辐射
白远山4 小时前
无人自助健身平台搭建:从架构设计到设备联动的完整实战
java·开发语言·架构·需求分析
许彰午4 小时前
47-MetaGrid元数据表格
java·低代码·架构
Android打工仔4 小时前
不要在 Data 层随意把 Cold Flow 转换成 Hot Flow
android·架构·kotlin
晴空蓝天4 小时前
Spring Boot 3.5 脚手架里的 JWT + Redis 双轨会话,双端 token 隔离我是这么设计的
java·spring boot·redis
小刘在重生~4 小时前
十六(3)、《集合扩充》CopyOnWriteArrayList 超详细解析(线程安全集合)
java·笔记·面试·职场和发展