Redis 是内存数据库,数据默认存放在内存中。
一旦进程退出或服务器断电,内存数据全部丢失。持久化就是解决这个问题的机制。
Redis 提供两种核心持久化方案:RDB 和 AOF。
Redis 4.0 之后还支持混合持久化。
一、RDB(Redis Database)快照
RDB 是将某一时刻的内存数据生成一个二进制快照文件 (dump.rdb),保存到磁盘上。
1、触发方式
| 方式 | 命令/配置 | 说明 |
|---|---|---|
| 手动触发 | SAVE |
阻塞主进程,直到快照完成。生产环境禁止使用。 |
| 手动触发 | BGSAVE |
主进程 fork 出子进程,由子进程执行快照,主进程继续处理请求。 |
| 自动触发 | save 900 1 |
配置文件中设置:900 秒内至少有 1 次写操作,自动触发 BGSAVE。 |
| 自动触发 | save 300 10 |
300 秒内至少有 10 次写操作。 |
| 自动触发 | save 60 10000 |
60 秒内至少有 10000 次写操作。 |
| 其他触发 | 主从复制、执行 SHUTDOWN、执行 FLUSHALL |
也会触发 RDB 生成。 |
2、执行流程(BGSAVE)
1. 执行 BGSAVE
2. 主进程调用 fork() 创建子进程
├── 主进程:继续处理客户端请求
└── 子进程:读取内存数据,写入临时 RDB 文件
3. 子进程写完临时文件后,原子替换旧的 dump.rdb
3、fork 与 Copy-On-Write
fork() 创建子进程时,操作系统使用写时复制(Copy-On-Write):
-
子进程和父进程共享同一块物理内存页
-
只有当父进程修改某页数据时,操作系统才会复制该页给父进程使用
-
子进程看到的始终是 fork 瞬间的内存快照
这意味着:
-
BGSAVE期间,Redis 的内存占用可能临时翻倍(如果写操作很频繁) -
如果数据修改量大,COW 开销不可忽视
4、RDB 文件结构
RDB 是一个紧凑的二进制文件,包含:
-
文件头(魔数、版本号)
-
数据库选择器
-
键值对数据(经过压缩)
-
校验和
5、RDB 的优缺点
| 优点 | 缺点 |
|---|---|
| 文件紧凑,体积小 | 无法做到实时持久化,两次快照之间可能丢数据 |
| 恢复速度快(直接加载二进制到内存) | BGSAVE 时 fork 子进程有性能开销 |
| 适合备份、全量复制 | 如果数据量大,fork 可能耗时较长,阻塞主进程 |
二、AOF(Append Only File)日志
AOF 将 Redis 执行的每一条写命令 以日志形式追加写入文件。重启时重新执行这些命令即可恢复数据。
1、执行流程
客户端发送写命令: SET key value
↓
Redis 服务端
├── 1. 执行命令,修改内存数据
└── 2. 将命令追加到 AOF 缓冲区(aof_buf)
↓
每隔一段时间(或每次事件循环)将缓冲区写入内核缓冲区
↓
根据 appendfsync 策略,刷入磁盘
2、三种刷盘策略(appendfsync)
| 模式 | 说明 | 数据安全性 | 性能 |
|---|---|---|---|
always |
每次写命令都 fsync 刷盘 | 最高,几乎不丢数据 | 最低,每条命令都写磁盘 |
everysec |
每秒 fsync 一次(默认) | 较好,最多丢 1 秒数据 | 较好,后台线程执行 |
no |
由操作系统决定何时刷盘 | 最低,可能丢几十秒数据 | 最高 |
生产环境推荐 everysec,在性能和安全性之间取得平衡。
3、AOF 重写(Rewrite)
AOF 文件会不断膨胀。例如:
RPUSH list a
RPUSH list b
RPUSH list c
LPOP list
这些命令可以合并为一条 RPUSH list b c,效果相同但日志更小。
AOF 重写就是干这件事:生成一个新的、更精简的 AOF 文件。
重写触发方式
| 方式 | 命令/配置 |
|---|---|
| 手动触发 | BGREWRITEAOF |
| 自动触发 | auto-aof-rewrite-percentage 100 + auto-aof-rewrite-min-size 64mb |
重写流程
1. 触发 BGREWRITEAOF
2. 主进程 fork 子进程
├── 子进程:遍历当前内存数据,生成新的精简 AOF 文件
└── 主进程:继续处理请求,同时将新命令写入 AOF 重写缓冲区
3. 子进程写完新 AOF 文件后,主进程将重写缓冲区的命令追加到新文件末尾
4. 原子替换旧 AOF 文件
4、AOF 的优缺点
| 优点 | 缺点 |
|---|---|
数据安全性高(always 或 everysec) |
文件体积大,是 RDB 的几倍到几十倍 |
| 可读性强(文本格式,可以手动查看/编辑) | 恢复速度慢(要逐条重放命令) |
| 支持实时持久化 | 刷盘操作有性能开销 |
三、RDB vs AOF 对比
| 维度 | RDB | AOF |
|---|---|---|
| 文件格式 | 二进制快照 | 文本日志 |
| 文件体积 | 小 | 大 |
| 恢复速度 | 快(直接加载内存) | 慢(逐条执行命令) |
| 数据安全性 | 可能丢两次快照间的全部数据 | 最多丢 1 秒数据(everysec) |
| 实时性 | 非实时 | 实时(always)或近实时(everysec) |
| 可读性 | 不可读 | 可读可编辑 |
| 对性能影响 | fork 时有短暂开销 | 持续刷盘开销 |
四、混合持久化(Redis 4.0+)
Redis 4.0 引入了混合持久化 ,试图结合 RDB 和 AOF 的优点。
1、原理
在 AOF 重写时:
-
子进程先将当前内存数据以 RDB 格式写入新 AOF 文件的前半部分
-
重写期间产生的新命令,以 AOF 格式追加到文件后半部分
最终文件结构:
+-------------------+-------------------+
| RDB 格式部分 | AOF 格式部分 |
| (内存快照,二进制) | (增量命令,文本) |
+-------------------+-------------------+
2、恢复流程
1. 先加载 RDB 部分(快速恢复大部分数据到内存)
2. 再执行 AOF 部分的增量命令(补全重写期间的数据)
3、开启方式
aof-use-rdb-preamble yes
4、优势
| 方面 | 说明 |
|---|---|
| 恢复速度 | 比纯 AOF 快很多(RDB 部分直接加载) |
| 文件体积 | 比纯 AOF 小(RDB 是二进制压缩的) |
| 数据安全性 | 保留了 AOF 的增量特性,最多丢少量数据 |
五、生产环境配置建议
方案一:RDB + AOF 混合(推荐)
bash
# 开启 AOF
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
# 开启 RDB(作为备份和快速恢复手段)
save 900 1
save 300 10
save 60 10000
# 开启混合持久化
aof-use-rdb-preamble yes
逻辑:
-
AOF(everysec)保证数据安全,最多丢 1 秒
-
RDB 作为额外的备份副本,用于灾难恢复
-
AOF 重写时使用混合格式,兼顾恢复速度和文件大小
方案二:纯 RDB(数据可接受丢失的场景)
appendonly no
save 900 1
save 300 10
save 60 10000
适用于缓存场景,数据可以从数据库重建,追求极致性能。
方案三:纯 AOF(数据绝不能丢的场景)
appendonly yes
appendfsync always
适用于金融交易等场景,每条命令都刷盘,性能牺牲最大。
六、持久化相关的常见问题
1. fork 阻塞问题
BGSAVE 和 BGREWRITEAOF 都需要 fork()。如果 Redis 实例内存很大(几十 GB),fork 操作可能耗时几百毫秒甚至几秒,期间主进程阻塞。
缓解方案:
-
控制单实例内存大小(建议不超过 10GB)
-
使用 Redis 集群分片,降低单个节点的数据量
2. AOF 重写时的磁盘 IO 压力
重写期间,子进程大量写盘,可能和主进程的 fsync 竞争磁盘 IO。
缓解方案:
-
将 AOF 文件放在独立的磁盘或 SSD 上
-
调整
no-appendfsync-on-rewrite yes:重写期间暂停fsync,牺牲一点安全性换取性能
3. 持久化文件损坏
如果 AOF 文件末尾损坏(如写了一半断电),Redis 提供了修复工具:
redis-check-aof --fix appendonly.aof
RDB 文件损坏则较难修复,通常依赖备份副本。
七、总结
| 机制 | 一句话概括 |
|---|---|
| RDB | 定时拍内存快照,恢复快、文件小,但可能丢数据 |
| AOF | 记录每条写命令,数据安全、可实时持久化,但文件大、恢复慢 |
| 混合 | AOF 重写时前半段用 RDB、后半段用 AOF,兼顾两者优点 |
核心选择逻辑:
-
能丢数据 → RDB
-
不能丢数据 → AOF(everysec)
-
又要快又要安全 → RDB + AOF 混合