很多人知道 Redis 有两套持久化方案:
RDB和AOF。但一到细节就开始混:
- 为什么
RDB叫“快照”,它到底什么时候生成?- 为什么生产里几乎不用
SAVE,而是用BGSAVE?- 为什么
AOF更安全,却又更容易变大?BGREWRITEAOF到底在“重写”什么?是改旧文件,还是生成新文件?- 为什么很多线上环境会同时开
RDB + AOF,甚至再配“混合持久化”?这篇文章不只讲概念,而是把 Redis 持久化背后的运行机制、数据恢复流程和配置取舍讲透。
一、Redis 为什么一定要讲持久化
Redis 本质上是内存数据库。
数据主要存在内存里,读写速度非常快,但这也意味着一个直接问题:
如果进程退出、机器重启、容器被销毁,纯内存数据会全部丢失。
所以 Redis 要解决的不是“怎么把数据放进内存”,而是:
- 服务重启后怎么把数据恢复回来
- 宕机时最多能接受丢多少数据
- 持久化带来的 CPU、磁盘、内存开销能不能接受
- 备份、迁移、主从同步时如何复用持久化文件
Redis 围绕这个问题,提供了两条路线:
RDB:在某个时间点,把当前内存数据做成一个快照AOF:把每条会修改数据的写命令,按顺序追加记录下来
先记住一句话:
RDB 关注“某一刻的数据长什么样”,AOF 关注“数据是如何一步步变成现在这个样子的”。
二、先建立一个总模型
可以把 Redis 持久化理解成两种不同的“存档思路”。
1. RDB:拍照片
某个时刻,Redis 把内存中的整个数据集序列化到磁盘,生成一个二进制文件,比如 dump.rdb。
它记录的是:
“这一刻数据库的完整状态。”
所以 RDB 的恢复方式也很直接:
把这张快照重新读回内存。
2. AOF:记流水账
Redis 每执行一条写命令,比如:
SET name redis
INCR counter
HSET user:1 name zhangsan age 20
就把这些命令按协议格式追加到 AOF 文件里。
它记录的是:
“为了得到现在的数据,系统执行过哪些写操作。”
所以 AOF 的恢复方式是:
从头到尾重放这些写命令,重新构建数据集。
3. 两者不是互斥,而是互补
很多人一开始会问:
“到底该选 RDB 还是 AOF?”
真实生产里更常见的答案是:
不是二选一,而是按目标组合使用。
- 想要恢复快、文件紧凑、便于备份:
RDB很有价值 - 想要更小的数据丢失窗口:
AOF更有优势 - 想同时兼顾恢复速度和数据安全:通常会启用
AOF + RDB,再配混合持久化
三、RDB 机制:Redis 的“数据快照”是怎么来的
1. 什么是 RDB
RDB,Redis Database Backup,本质上就是 Redis 在某个时间点把内存数据序列化成一个紧凑的二进制文件。
常见文件名是:
dump.rdb
这个文件里存的是键值数据、过期时间、编码信息等,目的是让 Redis 能在之后直接把数据恢复回来。
RDB 最大的特点是:
它保存的是结果,不保存过程。
例如现在 Redis 里有一个键:
counter = 1000000
RDB 并不关心你之前到底执行过一百万次 INCR,它只关心“当前这个键的值是 1000000”。
四、RDB 何时触发
RDB 的生成有两种主要方式。
1. 手动触发
Redis 提供两个常见命令:
SAVE
BGSAVE
它们都能生成 RDB,但行为差异非常大。
SAVE
SAVE 会由 Redis 主进程直接执行持久化。
这意味着:
在生成 RDB 期间,Redis 不能继续处理新的客户端请求。
所以它是阻塞式的,线上几乎不会用。
BGSAVE
BGSAVE 会 fork 一个子进程,由子进程负责把当前数据集写入 RDB 文件。
主进程继续处理客户端请求,所以这是生产环境真正常用的方式。
一句话总结:
SAVE:阻塞主线程,不适合线上BGSAVE:后台生成快照,生产常用
2. 自动触发
Redis 可以通过配置项按“时间 + 修改次数”自动触发快照,例如:
save 900 1
save 300 10
save 60 10000
这三条规则表示:
- 900 秒内至少有 1 次写操作,就触发一次快照
- 300 秒内至少有 10 次写操作,就触发一次快照
- 60 秒内至少有 10000 次写操作,就触发一次快照
它的本质不是定时器单独起作用,而是:
“时间条件 + 脏数据变更次数条件”同时满足时,触发一次 BGSAVE。
五、BGSAVE 的底层流程到底发生了什么
很多人知道 BGSAVE “会 fork 一个子进程”,但真正重要的是后面的细节。
它的核心流程可以抽象成这样:
客户端写入数据
|
v
Redis 决定触发 BGSAVE
|
v
主进程 fork 子进程
|
+-- 子进程:遍历当前内存数据,序列化并写入临时 RDB 文件
|
+-- 主进程:继续处理客户端读写请求
|
v
子进程写完后,用临时文件替换旧的 RDB 文件
这里最关键的是两个点:fork 和 Copy-On-Write。
1. 为什么一定要 fork
如果 Redis 主进程自己去遍历整个内存并写磁盘,那么持久化期间就没法继续提供服务。
而 fork 出子进程后:
- 子进程拿到的是生成瞬间的内存视图
- 主进程继续响应客户端请求
于是 Redis 就能做到:
“快照在后台生成,在线请求继续处理。”
2. Copy-On-Write 是什么
fork 并不意味着立刻把整个内存复制一份。
操作系统会采用 Copy-On-Write,写时复制:
- 刚
fork完时,父子进程共享同一批物理内存页 - 如果主进程后续只是读取数据,不需要额外复制
- 只有当主进程修改某些内存页时,操作系统才会把这些被修改的页复制出来
所以 BGSAVE 的代价不是“瞬间复制整个 Redis 内存”,而是:
fork本身有一次页表复制和暂停成本- 持久化期间如果写流量很大,会触发更多内存页复制,造成额外内存开销
这也是为什么线上做 RDB 时,通常要关注两个风险:
- fork 耗时:实例很大时,
fork可能明显卡顿 - 内存膨胀:快照期间写入越多,Copy-On-Write 额外占用越大
六、RDB 文件生成完成后如何恢复
Redis 启动时如果发现有可用的 RDB 文件,会读取这个文件,把里面的数据重新加载到内存。
它的恢复过程可以理解为:
读取 RDB 文件
|
v
解析文件头、版本、校验信息
|
v
逐个反序列化 key / value / expire time
|
v
重建内存中的数据结构
因为 RDB 记录的是某一刻的完整结果,所以恢复时不需要像 AOF 那样重放大量命令。
这带来一个很重要的优点:
RDB 恢复通常比 AOF 更快。
七、RDB 的优点和代价
优点
1. 文件紧凑,适合备份
RDB 是压缩后的二进制快照,通常比 AOF 更小,更适合做:
- 冷备份
- 数据归档
- 机器迁移
- 灾难恢复
2. 恢复速度通常更快
因为它直接加载结果,不需要逐条执行写命令。
3. 对运行时写入路径影响相对可控
RDB 不是每次写都落盘,而是按周期生成快照,所以平时写请求路径比较短。
代价
1. 会丢失两次快照之间的数据
如果上一次 RDB 是 10:00,下一次本来打算 10:05 生成,但 10:04:59 机器宕机,那么 10:00 之后的数据都可能丢失。
这就是 RDB 的核心短板:
数据丢失窗口通常按“分钟级”甚至更长来算。
2. fork 和 Copy-On-Write 会带来性能与内存压力
实例越大、写流量越高,这个成本越明显。
3. 快照不够“实时”
RDB 更像备份,不像细粒度日志。
八、AOF 机制:Redis 的“操作日志”是怎么工作的
1. 什么是 AOF
AOF,Append Only File,顾名思义就是“只追加文件”。
Redis 执行写命令后,不是立刻把整个数据集重新写一遍,而是把这次写操作按 Redis 协议追加到 AOF 文件末尾。
例如执行:
SET name redis
INCR counter
EXPIRE session:1 300
AOF 里会追加相应的协议内容。这样当 Redis 重启时,只要把这些命令重新执行一遍,数据就能恢复出来。
所以 AOF 的核心思想是:
“把状态变化过程保存下来。”
九、Redis 写入 AOF 时不是“每条命令都立刻刷盘”
很多人第一次接触 AOF,会误以为流程是:
执行一条写命令 -> 立刻写磁盘 -> 立刻 fsync
如果真这么做,性能会非常差。
Redis 实际上的流程更接近:
客户端写命令到来
|
v
主线程执行命令,修改内存
|
v
把命令内容追加到 AOF 缓冲区
|
v
按配置策略写入内核缓冲区并决定何时 fsync 到磁盘
这里最重要的配置是:
appendonly yes
appendfsync always | everysec | no
1. appendfsync always
每次写命令都尽量执行 fsync。
优点:
- 数据最安全
缺点:
- 吞吐下降明显
- 延迟抖动可能更大
这个模式一般只在极端强调持久化安全的场景使用。
2. appendfsync everysec
每秒做一次 fsync,这是生产里最常见的折中方案。
它意味着:
理论上最多丢失 1 秒左右的数据。
相比 always,性能要友好很多;相比 no,数据安全又明显更好。
3. appendfsync no
不主动控制 fsync 时机,更多交给操作系统决定什么时候把缓冲区刷到磁盘。
优点:
- 性能最好
缺点:
- 数据丢失窗口不可控
所以大多数线上配置里,AOF 常见选择是:
appendonly yes
appendfsync everysec
十、AOF 是如何恢复数据的
Redis 重启时,如果开启了 AOF,通常会优先使用 AOF 恢复数据,因为它往往比 RDB 更新。
恢复流程可以抽象成:
打开 AOF 文件
|
v
从头到尾解析 Redis 协议
|
v
依次重新执行每一条写命令
|
v
把数据重新构建到内存中
这也解释了 AOF 的一个天然特点:
AOF 文件越大、命令越多,启动恢复通常越慢。
因为它需要“重放过程”,而不是直接加载结果。
十一、AOF 为什么会越来越大
假设你对同一个 key 连续执行这些命令:
SET counter 1
INCR counter
INCR counter
INCR counter
最终结果只是:
counter = 4
但 AOF 里可能记了 4 条命令。
如果这种更新长期发生,AOF 会越来越大,原因有三个:
- 它记录的是过程,不是最终结果
- 同一个 key 被重复修改,会累计很多历史命令
- 删除、覆盖、过期等操作本身也要写入日志
所以 AOF 需要一个关键机制:
AOF 重写。
十二、BGREWRITEAOF 到底在重写什么
很多人看到“重写”两个字,第一反应是:
是不是把旧 AOF 文件读出来再压缩一遍?
不是。
Redis 的 AOF 重写,本质上做的是:
根据当前内存里的最终数据状态,重新生成一份更短、更干净的新 AOF。
例如,旧 AOF 里可能有:
SET counter 1
INCR counter
INCR counter
INCR counter
重写后完全可能变成:
SET counter 4
也就是说,AOF 重写不是“日志文件文本压缩”,而是:
“用当前数据集,重建一份能够恢复出同样结果的最小命令集。”
十三、BGREWRITEAOF 的执行流程
和 BGSAVE 一样,AOF 重写也不能阻塞线上请求,所以 Redis 使用后台子进程完成。
经典流程可以抽象成这样:
主进程收到 BGREWRITEAOF
|
v
fork 子进程
|
+-- 子进程:根据当前内存数据生成新的临时 AOF 文件
|
+-- 主进程:继续处理客户端请求
|
+-- 正常把新写命令追加到旧 AOF
+-- 同时把重写期间的新写命令记录到重写缓冲区
|
v
子进程完成新 AOF 文件
|
v
主进程把重写缓冲区里的增量命令追加到新 AOF
|
v
用新 AOF 原子替换旧 AOF
这里有两个关键点必须理解。
1. 子进程写的是“新文件”,不是改“旧文件”
如果直接在旧 AOF 上原地改写,会很难保证:
- 文件一致性
- 线上继续写入时的正确性
- 失败后的可恢复性
所以 Redis 的策略始终是:
先生成新临时文件,成功后再替换旧文件。
这是很多高可靠系统都爱用的套路。
2. 为什么主进程还要维护一个“重写缓冲区”
因为子进程开始重写时,它看到的是 fork 那一刻的内存快照。
但重写期间,主进程还在继续接收新的写请求。
如果这些新增写入不额外记录下来,就会出现问题:
- 新 AOF 只反映了
fork时刻的数据 fork之后发生的新写操作会丢失
所以 Redis 的做法是:
- 主进程正常服务
- 新写命令继续写入旧 AOF,保证持久化不中断
- 同时把这段增量写入记录到 AOF 重写缓冲区
- 等子进程的新文件写完,再把这段增量补到新文件末尾
这样替换之后,新 AOF 才是完整的。
十四、自动 AOF 重写是如何触发的
Redis 可以根据 AOF 文件膨胀比例自动触发重写,常见配置如下:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
它的含义可以简单理解为:
- AOF 文件至少达到
64MB - 且当前大小相对上一次重写后膨胀了
100%
满足条件后,Redis 会在后台触发一次 BGREWRITEAOF。
这样做的目的是避免 AOF 无限增长。
十五、AOF 的优点和代价
优点
1. 数据安全性通常比 RDB 更好
尤其在 appendfsync everysec 下,最多通常只会丢 1 秒左右的数据。
2. 日志语义更清晰
AOF 记录的是写命令序列,所以从原理上更接近数据库的 WAL 思想。
3. 可读性比 RDB 更强
AOF 本质上是协议化命令流,理解和排查通常比纯二进制快照更直观。
代价
1. 文件通常更大
因为它记过程,天然比只记结果更容易膨胀。
2. 恢复通常更慢
因为需要重放写命令。
3. 运行期开销更持续
每次写请求都要走 AOF 追加路径,并按策略刷盘。
4. 重写期间同样有 fork 和 Copy-On-Write 成本
这点和 BGSAVE 类似。
十六、RDB 和 AOF 到底怎么选
先给结论,再看原因。
| 维度 | RDB | AOF |
|---|---|---|
| 持久化方式 | 周期性快照 | 追加写命令日志 |
| 数据丢失窗口 | 通常更大 | 通常更小 |
| 文件体积 | 更紧凑 | 往往更大 |
| 恢复速度 | 通常更快 | 通常更慢 |
| 对运行时写路径影响 | 平时较小,集中在快照时 | 每次写都有追加成本 |
| 典型用途 | 备份、迁移、快速恢复 | 提高数据安全性 |
什么时候更偏向 RDB
- Redis 更像缓存,数据可从别处重建
- 更关注恢复速度和备份文件体积
- 能接受分钟级数据丢失
什么时候更偏向 AOF
- Redis 承担重要业务数据
- 更关注宕机时的数据安全
- 能接受一定的磁盘和写入性能成本
生产里更常见的做法
开启 AOF,同时保留 RDB。
原因很简单:
- AOF 提供更小的数据丢失窗口
- RDB 提供更快的冷恢复和更适合备份的快照文件
如果两者都开启,Redis 启动恢复时通常会优先使用 AOF,因为它通常更“新”。
十七、为什么还会有“混合持久化”
如果你看到配置:
aof-use-rdb-preamble yes
这就是常说的混合持久化思路。
它解决的问题很实际:
- 纯 AOF 恢复慢
- 纯 RDB 丢数据窗口大
混合持久化的核心做法是:
在 AOF 重写时,前半段直接用 RDB 方式写入当前数据快照,后半段再追加增量 AOF 命令。
你可以把它理解成:
新 AOF 文件 = 一份紧凑快照 + 快照之后的增量命令
这样做有两个直接好处:
- 前半段用类似 RDB 的紧凑格式,文件更小、加载更快
- 后半段保留增量命令,仍然能保证较好的数据新鲜度
所以混合持久化本质上是:
把 RDB 的“恢复快”和 AOF 的“丢数据少”尽量组合起来。
十八、把三套机制串起来理解一次
到这里,可以把 Redis 持久化整体串成一个统一模型:
1. 平时写请求到来
- Redis 先修改内存
- 如果开启 AOF,则把写命令追加到 AOF 缓冲区
- 再按
appendfsync策略决定何时刷盘
2. 到了快照时机
- Redis 触发
BGSAVE - 子进程把当前内存做成 RDB 快照
- 主进程继续服务请求
3. AOF 太大时
- Redis 触发
BGREWRITEAOF - 子进程按当前数据集生成新的、更短的 AOF
- 主进程把重写期间的增量写请求补到新文件
- 最终替换旧 AOF
4. Redis 重启时
- 如果开启了 AOF,通常优先用 AOF 恢复
- 否则使用 RDB 恢复
- 如果启用了混合持久化,恢复时会先加载快照部分,再回放增量命令
十九、生产环境里几个非常实际的注意点
1. 不要在线上手动执行 SAVE
它会阻塞 Redis 主进程,容易把请求延迟直接打爆。线上应优先使用 BGSAVE。
2. 评估 fork 成本,不要只看内存大小
实例越大、写流量越高,BGSAVE 和 BGREWRITEAOF 的额外成本越明显。尤其是高写入场景下,Copy-On-Write 可能导致内存峰值上涨。
如果业务写入高峰非常明显,还要尽量避免把快照或 AOF 重写压在同一个峰值窗口里,否则延迟抖动和内存波动都会更难看。
3. appendfsync everysec 往往是更现实的平衡点
always 太重,no 风险太大,everysec 是很多业务场景下更合理的默认值。
4. 持久化不是高可用的替代品
即使开了 AOF 和 RDB,也不代表你拥有完整的高可用能力。
持久化解决的是:
- 单机重启后的数据恢复
- 磁盘级别的数据落地
但主从切换、故障自动转移、跨机房容灾,仍然要靠:
- 主从复制
- Sentinel
- Cluster
5. 定期校验和备份持久化文件
RDB 和 AOF 不是“生成了就万事大吉”。
真正线上要考虑:
- 持久化文件是否能正常恢复
- 备份链路是否可靠
- 文件损坏后如何校验和修复
实践里通常会用到类似工具:
redis-check-rdbredis-check-aof
二十、最容易混淆的几个点
1. RDB 不是“实时备份”
它是时间点快照,不是连续日志。
2. AOF 不是“每条命令都马上安全落盘”
是否真正刷到磁盘,要看 appendfsync 策略。
3. AOF 重写不是修改旧文件
它是基于当前数据集生成一个新文件,再替换旧文件。
4. BGSAVE / BGREWRITEAOF 都不是“没有成本”
它们虽然不阻塞整个服务流程,但都依赖 fork,而 fork 与 Copy-On-Write 在大实例下都可能带来明显开销。
二十一、最后做一个总结
如果只记一句话,那就是:
RDB 保存的是“某个时刻的数据结果”,AOF 保存的是“达到这个结果的写入过程”。
进一步展开:
RDB像拍快照,恢复快、文件小,但数据丢失窗口更大AOF像记流水账,数据更安全,但文件更大、恢复更慢BGSAVE和BGREWRITEAOF都依赖fork + Copy-On-Write- AOF 重写不是压缩旧日志,而是用当前数据集重建更短的新日志
- 真正的生产实践通常不是单选,而是
AOF + RDB组合,必要时再启用混合持久化
所以在工程上,Redis 持久化从来不是一道“背概念”的题,而是一道标准的权衡题:
你到底更在意恢复速度、磁盘体积、运行时开销,还是宕机时的数据损失窗口?
只有把这几个维度一起放进来,RDB 和 AOF 的取舍才会真正清楚。