文章目录
-
- [Redis 持久化:RDB 与 AOF 深度解析](#Redis 持久化:RDB 与 AOF 深度解析)
- 一、前言
- [二、RDB 持久化](#二、RDB 持久化)
- [三、AOF 持久化](#三、AOF 持久化)
-
- [3.1 AOF 是什么](#3.1 AOF 是什么)
- [3.2 开启 AOF](#3.2 开启 AOF)
- [3.3 AOF 的四个工作阶段](#3.3 AOF 的四个工作阶段)
- [3.4 命令写入(append)](#3.4 命令写入(append))
- [3.5 文件同步(sync)](#3.5 文件同步(sync))
- [3.6 重写机制(rewrite)](#3.6 重写机制(rewrite))
-
- [重写后 AOF 变小的三个原因](#重写后 AOF 变小的三个原因)
- 触发方式
- [AOF 重写的完整流程](#AOF 重写的完整流程)
- [3.7 启动时加载 AOF 文件](#3.7 启动时加载 AOF 文件)
- [3.8 AOF 的优缺点](#3.8 AOF 的优缺点)
- [四、RDB 与 AOF 对比及选择建议](#四、RDB 与 AOF 对比及选择建议)
-
- [4.1 对比总结](#4.1 对比总结)
- [4.2 选择建议](#4.2 选择建议)
- [4.3 Redis 启动时的加载优先级](#4.3 Redis 启动时的加载优先级)
- 五、总结
Redis 持久化:RDB 与 AOF 深度解析
一、前言
💬 这一篇讲什么:Redis 的两种持久化机制 ------ RDB 快照和 AOF 日志
🚀 核心内容:
- 为什么 Redis 需要持久化?
- RDB 是什么?怎么触发?bgsave 的流程是什么?
- AOF 是什么?命令写入、文件同步、重写机制的完整流程
- Redis 启动时如何加载持久化文件?
- RDB 和 AOF 各有什么优缺点,如何选择?
Redis 把数据存在内存里,速度极快,但内存有一个致命缺点:断电即失。一旦服务器宕机或 Redis 进程崩溃,所有数据会消失得无影无踪。为了解决这个问题,Redis 提供了两种持久化机制:RDB(快照) 和 AOF(追加日志)。理解这两种机制,是运维好 Redis、保障数据安全的基础。
二、RDB 持久化
2.1 RDB 是什么
RDB(Redis Database)是把 Redis 当前内存中的所有数据以快照的形式写入磁盘文件。可以把它理解为"拍一张照片":把某个时刻 Redis 内存中数据的完整状态保存下来,生成一个紧凑的二进制文件。
RDB 文件默认保存在 /var/lib/redis/dump.rdb。
2.2 手动触发 RDB
手动触发 RDB 有两个命令:save 和 bgsave。
save 命令(不推荐)
bash
127.0.0.1:6379> save
OK
save 命令会阻塞当前 Redis 主进程 ,直到 RDB 文件生成完毕。在此期间,Redis 无法响应任何客户端请求。对于数据量大的实例,这个阻塞可能长达几十秒甚至几分钟,在生产环境中基本不可接受。实际中几乎不使用 save 命令。
bgsave 命令(推荐)
bash
127.0.0.1:6379> bgsave
Background saving started
bgsave 让 Redis fork 出一个子进程,由子进程负责生成 RDB 文件,父进程继续正常处理客户端请求。阻塞只发生在 fork 的那一瞬间,通常非常短暂。
Redis 内部所有涉及 RDB 的操作都采用 bgsave 的方式。
2.3 自动触发 RDB
除了手动触发,Redis 还支持配置自动触发,这才是生产中真正有价值的使用方式:
触发条件一:save 配置项。 在 redis.conf 中配置:
bash
save 900 1 # 900 秒内,至少发生 1 次写操作,触发 RDB
save 300 10 # 300 秒内,至少发生 10 次写操作,触发 RDB
save 60 10000 # 60 秒内,至少发生 10000 次写操作,触发 RDB
满足任意一条规则,Redis 就会自动执行 bgsave。
触发条件二:主从全量复制。 当从节点需要全量同步主节点数据时,主节点会自动执行 bgsave,生成 RDB 文件发送给从节点。
触发条件三:执行 shutdown 命令。 正常关闭 Redis 时,自动执行一次 RDB 持久化,保证数据不丢失。
2.4 bgsave 的完整流程
bgsave 的执行过程如下:
text
1. 客户端执行 bgsave
↓
2. 父进程检查:是否有其他 RDB/AOF 子进程正在运行?
├── 是 → 直接返回,不执行
└── 否 → 继续
↓
3. 父进程执行 fork,创建子进程
(fork 期间父进程短暂阻塞)
↓
4. fork 完成,父进程返回 "Background saving started"
父进程继续响应客户端命令
↓
5. 子进程根据 fork 时的内存快照生成 RDB 文件
(先写临时文件,完成后原子替换旧的 dump.rdb)
↓
6. 子进程发送信号通知父进程,父进程更新统计信息

可以通过以下命令查看 RDB 相关的状态信息:
bash
# 查看最近一次 fork 的耗时(微秒)
127.0.0.1:6379> INFO stats
latest_fork_usec:289
# 查看最后一次成功生成 RDB 的时间(Unix 时间戳)
127.0.0.1:6379> LASTSAVE
(integer) 1700000000
2.5 RDB 文件的处理
保存路径与文件名
RDB 文件的保存位置和文件名在 redis.conf 中配置:
bash
dir /var/lib/redis/ # 保存目录
dbfilename dump.rdb # 文件名
也可以在运行时动态修改:
bash
CONFIG SET dir /new/path
CONFIG SET dbfilename new.rdb
压缩
Redis 默认对 RDB 文件进行 LZF 算法压缩,压缩后的文件体积远小于原始内存大小,非常适合网络传输(比如主从复制时发送给从节点):
bash
CONFIG SET rdbcompression yes # 开启压缩(默认)
虽然压缩会消耗一点 CPU,但显著减少磁盘占用和网络传输量,建议始终开启。
损坏检查
如果 Redis 启动时发现 RDB 文件损坏,会拒绝启动。可以使用 Redis 自带工具检查修复:
bash
redis-check-rdb /var/lib/redis/dump.rdb
2.6 RDB 的优缺点
✅ 优点:
文件紧凑,适合备份和灾备。 RDB 是经过压缩的二进制快照,体积小,可以方便地复制到远程机器或存储系统(如 HDFS)做灾备。比如每 6 小时执行一次 bgsave 并备份 RDB 文件,这是非常常见的运维方案。
恢复速度快。 重启 Redis 时加载 RDB 文件进行数据恢复的速度,远快于 AOF 方式,因为 AOF 需要重放每一条命令。
fork 子进程,对主进程影响小。 持久化由子进程完成,父进程继续处理请求,几乎不影响正常服务。
❌ 缺点:
无法做到实时/秒级持久化。 每次 bgsave 都要 fork 子进程,这是重量级操作,不能频繁执行。如果 Redis 在两次 RDB 之间崩溃,这段时间的数据就会丢失。
版本兼容性风险。 RDB 是二进制格式,不同 Redis 版本之间可能存在兼容性问题。
三、AOF 持久化
3.1 AOF 是什么
AOF(Append Only File)采用了和 RDB 完全不同的思路:不记录数据的状态快照,而是记录每一条写命令。
每次执行写操作(SET、HSET、LPUSH 等),Redis 都会把这条命令追加记录到 AOF 文件中。重启 Redis 时,通过重放 AOF 文件中的所有命令来恢复数据。
AOF 主要解决了 RDB 无法实时持久化的问题,目前已经是 Redis 持久化的主流方式。
3.2 开启 AOF
AOF 默认是关闭 的,需要在 redis.conf 中开启:
bash
appendonly yes # 开启 AOF(默认 no)
appendfilename "appendonly.aof" # AOF 文件名
dir /var/lib/redis/ # 保存目录,和 RDB 共用
3.3 AOF 的四个工作阶段
AOF 的完整工作流程分为四个阶段:
text
写命令执行
↓
1. append:命令追加到 aof_buf(内存缓冲区)
↓
2. sync:缓冲区根据策略同步到 AOF 文件(磁盘)
↓
3. rewrite:AOF 文件定期重写压缩
↓
4. load:Redis 重启时加载 AOF 文件恢复数据

3.4 命令写入(append)
AOF 记录的内容是 RESP(Redis Serialization Protocol)文本协议格式。
例如执行 set hello world 这条命令,在 AOF 缓冲区中追加的内容是:
bash
*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
解析:*3 表示这条命令有 3 个参数(set、hello、world),$3 表示第一个参数长度为 3,以此类推。
Redis 选择文本协议格式的原因:兼容性好、实现简单、具有可读性,用文本编辑器打开 AOF 文件就能直接看懂里面记录了哪些操作。
为什么先写缓冲区,而不是直接写磁盘?
Redis 是单线程模型,如果每次写命令都直接同步到磁盘,性能会从内存读写降级为磁盘 IO,速度差几个数量级。先写入内存缓冲区(aof_buf),再批量同步到磁盘,可以大幅减少磁盘 IO 次数,同时 Redis 可以提供灵活的同步策略供用户选择。
3.5 文件同步(sync)
缓冲区中的数据什么时候真正写到磁盘,由 appendfsync 配置项控制,共有三个选项:
| 配置值 | 同步时机 | 数据安全性 | 性能 |
|---|---|---|---|
always |
每次写命令后立即 fsync | 最高(几乎不丢数据) | 最差 |
everysec |
每秒由独立线程 fsync(默认) | 较高(最多丢失 1 秒数据) | 好 |
no |
由操作系统决定何时 fsync | 最低(丢失数据量不可控) | 最好 |
always :每次写命令都立即调用 fsync 强制刷盘。即使系统故障,最多也只丢失当前这一条命令。但代价是每次写操作都要等待磁盘 IO,在普通机械硬盘上只能支持几百 TPS 的写入。除非是极其重要的数据,否则不建议使用。
everysec(推荐) :命令写入缓冲区后立即返回,每秒由独立同步线程执行一次 fsync。系统故障时理论上最多丢失 1 秒的数据。这是性能和数据安全的最佳平衡点,也是 Redis 的默认配置。
no:命令写入缓冲区后立即返回,具体何时刷盘由操作系统决定(Linux 默认约 30 秒)。性能最好,但数据丢失风险最大,一般不建议使用。
💡 背景补充:操作系统的
write系统调用把数据写入内核页缓冲区后立即返回,不一定写到磁盘;fsync则会强制把数据刷到硬盘,阻塞直到写入完成。两者的区别是always和everysec/no的核心区别所在。
3.6 重写机制(rewrite)
AOF 以追加方式记录每一条写命令,随着时间推移 AOF 文件会越来越大。想象一下:对同一个 key 执行了 1000 次 SET,AOF 里会记录 1000 条命令,但实际上只需要保留最后一条(数据的最终状态)。
AOF 重写就是用来压缩 AOF 文件的:把 Redis 当前内存中的数据重新转化为一批写命令,写入新的 AOF 文件,丢弃所有历史上的冗余命令。
重写后 AOF 变小的三个原因
原因一:过期数据不写入。 已经过期的 key 在重写时不会生成对应的命令。
原因二:无效命令被删除。 比如某个 key 先 SET 后 DEL,这两条命令互相抵消,重写后都不需要;或者某个 key 被多次修改,只保留最终状态。
原因三:多条命令合并为一条。 比如:
bash
# 旧 AOF 中有三条独立命令
LPUSH mylist a
LPUSH mylist b
LPUSH mylist c
# 重写后合并为一条
LPUSH mylist a b c
触发方式
手动触发:
bash
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
自动触发:通过两个配置项共同决定:
bash
auto-aof-rewrite-min-size 64mb # AOF 文件至少达到 64MB 才考虑重写
auto-aof-rewrite-percentage 100 # 当前 AOF 大小比上次重写后增加了 100% 才触发
两个条件必须同时满足才会自动触发重写。
AOF 重写的完整流程
AOF 重写是一个设计非常精巧的过程,需要保证重写期间不丢失任何数据:
text
1. 执行 bgrewriteaof
├── 已有 AOF 重写子进程在运行 → 直接返回,不重复执行
└── 正在执行 bgsave → 等 bgsave 完成后再执行重写
↓
2. 父进程 fork 创建子进程(短暂阻塞)
↓
3. 父进程 fork 后继续正常响应命令:
├── 新写命令 → 写入 aof_buf(继续同步到旧 AOF 文件,保证旧 AOF 机制正确)
└── 同时写入 aof_rewrite_buf(重写缓冲区,专门记录 fork 之后的增量修改)
↓
4. 子进程根据 fork 时的内存快照,将数据转为命令写入新 AOF 文件
↓
5. 子进程完成重写:
a. 子进程发送信号通知父进程
b. 父进程将 aof_rewrite_buf(增量修改)追加到新 AOF 文件
c. 用新 AOF 文件原子替换旧 AOF 文件
↓
6. 后续写命令追加到新 AOF 文件

为什么需要 aof_rewrite_buf?
子进程只有 fork 时的内存快照,fork 之后父进程继续处理新命令,这些新命令产生的数据变更,子进程无从感知。如果不专门记录,这部分增量数据就会丢失。aof_rewrite_buf(重写缓冲区)的作用就是:在子进程重写期间,专门缓存 fork 之后产生的所有新写命令,等子进程完成后由父进程追加到新 AOF 文件末尾,确保数据完整性。
3.7 启动时加载 AOF 文件
Redis 启动时,如果检测到 AOF 文件存在,就会通过"伪客户端"逐条执行 AOF 文件中记录的命令,将所有数据重新加载到内存中。
什么是伪客户端? Redis 在内部创建了一个没有网络连接的虚拟客户端,由它来执行 AOF 中的命令,和真实客户端发送命令的效果完全一致。
加载过程中如果 AOF 文件末尾存在不完整的命令(比如写文件时突然断电),Redis 会尝试修复,并记录日志。可以使用以下工具修复损坏的 AOF 文件:
bash
redis-check-aof --fix /var/lib/redis/appendonly.aof
3.8 AOF 的优缺点
✅ 优点:
数据安全性高。 使用 everysec 配置,最多只丢失 1 秒的数据;使用 always 甚至可以做到几乎不丢数据。相比 RDB 可能丢失几分钟的数据,AOF 的安全性更强。
可读性好,易于理解和分析。 AOF 文件是文本格式,可以直接查看 Redis 历史上执行了哪些写操作,便于人工审查和问题排查。
写入性能高。 AOF 以追加方式写入文件,是顺序 IO,性能比随机 IO 好很多。
❌ 缺点:
文件体积通常远大于 RDB。 即使经过重写压缩,AOF 记录的是命令,RDB 记录的是数据状态,AOF 文件通常比 RDB 大得多。
数据恢复速度慢。 重启时需要重放所有命令,数据量大时恢复耗时远超 RDB 直接加载快照的方式。
历史上存在 AOF Bug。 极个别情况下,AOF 重写后再次加载数据可能出现与原始数据不一致的问题(现在的 Redis 版本已经很少见)。
四、RDB 与 AOF 对比及选择建议
4.1 对比总结
| 对比维度 | RDB | AOF |
|---|---|---|
| 持久化方式 | 全量数据快照 | 记录每条写命令 |
| 数据安全性 | 低(可能丢失几分钟数据) | 高(最多丢失 1 秒,everysec) |
| 文件体积 | 小(压缩二进制) | 大(文本命令) |
| 恢复速度 | 快 | 慢 |
| 对主进程影响 | fork 子进程,影响较小 | fork + 每秒 fsync,影响较小 |
| 可读性 | 差(二进制格式) | 好(文本命令格式) |
| 适合场景 | 冷备、灾备、快速恢复 | 生产主力持久化 |
4.2 选择建议
场景一:对数据丢失不敏感(如缓存场景)
只需要开 RDB,设置合理的 save 策略。Redis 重启后即便丢失几分钟数据,从数据库重新加载缓存即可,完全可接受。
场景二:对数据安全要求较高
只开 AOF,配置 appendfsync everysec。最多丢失 1 秒数据,兼顾了安全性和性能。
场景三:对数据安全要求高,同时希望快速恢复(推荐)
同时开启 RDB 和 AOF。 RDB 作为定期冷备和快速恢复的手段,AOF 保障高频数据安全。Redis 启动时优先加载 AOF 文件 (因为 AOF 数据更完整),只有在没有 AOF 文件的情况下才加载 RDB。这是生产环境最推荐的配置。
bash
# redis.conf 同时开启两种持久化
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 100
4.3 Redis 启动时的加载优先级
text
Redis 启动
↓
检查是否开启了 AOF(appendonly=yes)
├── 是 → 加载 AOF 文件
└── 否 → 加载 RDB 文件
AOF 优先级高于 RDB,因为 AOF 记录了更细粒度的数据变更,通常比 RDB 的数据更新、更完整。
五、总结
✅ RDB:全量快照,文件小、恢复快,但有数据丢失风险,适合冷备和灾备
✅ AOF:命令追加,安全性高,但文件大、恢复慢,是生产主力持久化方式
✅ bgsave:fork 子进程生成 RDB,父进程不阻塞(推荐,几乎所有场景都用 bgsave 而不是 save)
✅ appendfsync everysec:AOF 的最佳实践,每秒一次 fsync,最多丢 1 秒数据
✅ AOF 重写:压缩 AOF 文件,通过 aof_rewrite_buf 解决重写期间的增量数据问题
✅ 生产最佳实践:同时开启 RDB + AOF,启动时优先加载 AOF