Redis 持久化机制:RDB 与 AOF¶
1. 引入:为什么需要持久化?¶
Redis 是内存数据库,进程重启后数据会丢失。持久化机制将内存数据保存到磁盘,重启后可以恢复。
不了解持久化会导致的线上问题: - 生产环境只开 RDB,Redis 宕机后丢失最近几分钟的数据 - AOF 文件无限增长,磁盘被打满 - 重启后恢复时间过长,影响业务
2. 两种持久化方式概览¶
flowchart LR
subgraph RDB 快照
R1[触发条件\n手动/自动/shutdown] --> R2[fork 子进程]
R2 --> R3[生成 .rdb 文件\n二进制压缩格式]
R3 --> R4[优点: 文件小/恢复快\n缺点: 可能丢失最后一次快照后的数据]
end
subgraph AOF 追加日志
A1[每条写命令] --> A2[追加到 AOF 文件]
A2 --> A3[fsync 策略\nalways/everysec/no]
A3 --> A4[优点: 数据安全性高\n缺点: 文件大/恢复慢]
end
3. RDB(Redis Database Snapshot)¶
3.1 工作原理¶
RDB 是在某个时间点对 Redis 内存数据做全量快照,生成一个二进制压缩文件(.rdb)。
触发方式:
手动触发:SAVE(阻塞主线程)/ BGSAVE(fork 子进程,推荐)
自动触发:配置 save 规则,如 save 900 1(900秒内有1次写操作则触发)
关闭时触发:shutdown 命令默认触发 BGSAVE
3.2 fork + Copy-On-Write 机制¶
sequenceDiagram
participant Main as 主进程
participant Child as 子进程
participant Disk as 磁盘
Main->>Child: fork() 创建子进程
Note over Main,Child: 共享同一份内存(Copy-On-Write)
Main->>Main: 继续处理客户端请求
Child->>Disk: 将内存数据写入 .rdb 文件
Note over Main: 若主进程修改数据,才复制对应内存页
Child-->>Main: 写入完成,替换旧 .rdb 文件
Copy-On-Write(写时复制):fork 后父子进程共享内存页,只有当某页被修改时才复制该页。这样 fork 的开销很小,子进程看到的是 fork 时刻的内存快照。
⚠️ 注意:如果 Redis 内存很大(如 10GB),fork 时需要复制页表,可能造成几毫秒到几秒的停顿(
latency)。这是 RDB 的主要性能隐患。
3.3 RDB 配置¶
# redis.conf 配置示例
save 900 1 # 900秒内至少1次写操作,触发 BGSAVE
save 300 10 # 300秒内至少10次写操作,触发 BGSAVE
save 60 10000 # 60秒内至少10000次写操作,触发 BGSAVE
dbfilename dump.rdb # RDB 文件名
dir /var/lib/redis # RDB 文件存储目录
rdbcompression yes # 是否压缩(LZF算法)
3.4 RDB 优缺点¶
| 优点 | 缺点 |
|---|---|
| 文件紧凑,体积小,适合备份和传输 | 可能丢失最后一次快照后的数据(分钟级) |
| 恢复速度快(直接加载二进制数据) | fork 时可能造成短暂停顿 |
| 对 Redis 性能影响小(子进程处理) | 不适合对数据安全性要求高的场景 |
4. AOF(Append Only File)¶
4.1 工作原理¶
AOF 将每条写命令以文本格式追加到 AOF 文件中,重启时重放所有命令来恢复数据。
写命令执行流程:
1. 客户端发送写命令(如 SET key value)
2. Redis 执行命令,修改内存数据
3. 将命令追加到 AOF 缓冲区(aof_buf)
4. 根据 fsync 策略,将缓冲区数据刷到磁盘
4.2 三种 fsync 策略¶
| 策略 | 触发时机 | 数据安全性 | 性能影响 | 适用场景 |
|---|---|---|---|---|
always |
每条命令都 fsync | 最高(最多丢失1条命令) | 最差(每次都写磁盘) | 金融等极高安全要求 |
everysec(推荐) |
每秒 fsync 一次 | 高(最多丢失1秒数据) | 较小(后台线程处理) | 大多数业务场景 |
no |
由操作系统决定 | 低(可能丢失较多数据) | 最好 | 不推荐生产使用 |
4.3 AOF 重写(Rewrite)¶
问题:AOF 文件会不断增长(记录所有历史命令),如 INCR counter 执行了 1000 次,AOF 中有 1000 条记录,但实际只需要 SET counter 1000 一条。
解决方案:AOF 重写,将内存中的当前数据状态转换为最少的命令集合,生成新的 AOF 文件。
sequenceDiagram
participant Main as 主进程
participant Child as 子进程
participant AOF as 旧 AOF 文件
participant NewAOF as 新 AOF 文件
Main->>Child: fork() 触发 AOF 重写
Main->>AOF: 继续追加新命令到旧 AOF
Main->>Main: 同时将新命令写入 AOF 重写缓冲区
Child->>NewAOF: 根据当前内存数据生成新 AOF
Child-->>Main: 重写完成
Main->>NewAOF: 将 AOF 重写缓冲区的命令追加到新 AOF
Main->>AOF: 用新 AOF 替换旧 AOF
AOF 重写配置:
auto-aof-rewrite-percentage 100 # AOF 文件比上次重写后增长 100% 时触发
auto-aof-rewrite-min-size 64mb # AOF 文件至少 64MB 才触发(防止小文件频繁重写)
4.4 AOF 优缺点¶
| 优点 | 缺点 |
|---|---|
| 数据安全性高(最多丢失1秒数据) | 文件体积大(文本格式,未压缩) |
| 文件可读,便于排查问题 | 恢复速度慢(需要重放所有命令) |
| 支持 AOF 重写压缩文件 | 对性能有持续影响(写磁盘) |
5. 混合持久化(Redis 4.0+)¶
5.1 为什么需要混合持久化?¶
| 方案 | 问题 |
|---|---|
| 纯 RDB | 数据安全性低,可能丢失几分钟数据 |
| 纯 AOF | 恢复速度慢,大数据量时重放命令耗时很长 |
| 混合持久化 | ✅ 兼顾恢复速度和数据安全性 |
5.2 混合持久化原理¶
AOF 文件结构:
┌─────────────────────────────────────────────────────┐
│ RDB 格式的全量数据(文件头) │
│ (AOF 重写时,将当前内存数据以 RDB 格式写入) │
├─────────────────────────────────────────────────────┤
│ AOF 格式的增量命令(文件尾) │
│ (重写完成后,新产生的写命令以 AOF 格式追加) │
└─────────────────────────────────────────────────────┘
恢复流程: 1. 加载文件头的 RDB 数据(快速恢复全量数据) 2. 重放文件尾的 AOF 增量命令(补全最新数据)
5.3 开启混合持久化¶
6. 持久化方案选择指南¶
flowchart TD
A[选择持久化方案] --> B{对数据安全性要求高?}
B -->|是| C{能接受恢复慢?}
B -->|否| D[只开 RDB\n定期备份即可]
C -->|是| E[开启 AOF\nfsync=everysec]
C -->|否| F[开启混合持久化\naof-use-rdb-preamble yes]
F --> G[✅ 推荐:大多数生产环境]
| 场景 | 推荐方案 |
|---|---|
| 纯缓存,数据丢失可接受 | 只开 RDB,或不开持久化 |
| 需要数据安全,但恢复速度不敏感 | 只开 AOF(everysec) |
| 需要数据安全,且恢复速度要快 | 混合持久化(推荐) |
| 数据量极大,重启恢复时间敏感 | 混合持久化 + 主从复制 |
7. 工作中常见错误¶
❌ 错误:生产环境只开启 RDB,Redis 宕机后丢失大量数据
✅ 正确:开启混合持久化(aof-use-rdb-preamble yes),同时配置合理的 AOF fsync 策略
❌ 错误:AOF 文件无限增长,未配置 AOF 重写,磁盘被打满
✅ 正确:配置 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 触发自动重写
❌ 错误:Redis 内存很大时,BGSAVE 导致长时间停顿
✅ 正确:监控 fork 耗时(INFO stats 中的 latest_fork_usec),控制 Redis 单实例内存不超过 10GB
❌ 错误:在从节点上开启 RDB,主节点不开持久化
✅ 正确:主节点也应开启持久化,否则主节点重启后数据为空,会将空数据同步给从节点
8. 面试高频问题¶
Q:RDB 和 AOF 哪个更好?
没有绝对的好坏,取决于业务需求。对数据安全性要求高用 AOF,对恢复速度要求高用 RDB,生产环境推荐混合持久化,兼顾两者优点。
Q:AOF 重写期间,新的写命令怎么处理?
主进程 fork 子进程做重写,同时将新命令写入旧 AOF 文件和AOF 重写缓冲区。重写完成后,将缓冲区的命令追加到新 AOF 文件,再替换旧文件。这样保证了重写期间数据不丢失。
Q:Redis 重启后,RDB 和 AOF 哪个优先?
AOF 优先。因为 AOF 数据更完整(更新更及时)。只有在 AOF 关闭时,才使用 RDB 恢复。