Redis 持久化机制详解:RDB 与 AOF 的原理、配置与选型
Redis 是内存数据库,数据断电即失。持久化机制是 Redis 从"缓存"走向"存储"的关键。本文深入拆解 RDB 快照和 AOF 日志两种持久化方式的底层原理、触发机制、恢复流程,并给出生产环境的选型建议。
一、为什么需要持久化?
Redis 的所有数据都存储在内存中,读写速度极快,但内存是易失性的------进程退出、服务器宕机、重启都会导致数据全部丢失。
持久化的作用就是:把内存中的数据以某种形式保存到磁盘上,重启时再从磁盘加载回内存,从而保证数据不丢。
Redis 提供了两种持久化方式:
| 方式 | 全称 | 核心思路 |
|---|---|---|
| RDB | Redis Database | 定时对内存数据拍快照,生成二进制 dump 文件 |
| AOF | Append Only File | 记录每条写命令到日志文件,重启时重放命令恢复数据 |
两种方式可以同时开启,互不冲突。
二、RDB 持久化
2.1 什么是 RDB?
RDB 是 Redis 的默认持久化方式 。它会在指定的时间间隔内,对内存中的数据生成一个快照(snapshot) ,以二进制格式保存到磁盘上的 dump.rdb 文件中。
内存数据 ─fork子进程──> 遍历数据 ──> 写入临时文件 ──> 原子替换 dump.rdb
2.2 RDB 的触发方式
自动触发(save 配置)
在 redis.conf 中配置 save 规则:
conf
save 900 1 # 900秒内至少有1个key被修改,触发快照
save 300 10 # 300秒内至少有10个key被修改,触发快照
save 60 10000 # 60秒内至少有10000个key被修改,触发快照
Redis 主进程会持续统计"距离上次快照以来被修改的 key 数量",一旦满足任意一条规则,就触发一次 RDB 快照。
手动触发
bash
# 阻塞式:主进程直接执行快照,期间无法响应任何命令
SAVE
# 非阻塞式:fork 子进程执行快照,主进程继续处理命令(推荐)
BGSAVE
其他自动触发场景
- 执行
SHUTDOWN命令时,Redis 会自动执行一次 RDB - 主从复制时,从节点首次同步会触发主节点生成 RDB
2.3 RDB 的执行流程(BGSAVE)
1. 主进程收到 BGSAVE 命令
2. fork() 创建子进程(Copy-on-Write 机制)
3. 子进程遍历内存数据,写入临时文件 temp-xxx.rdb
4. 写入完成后,用临时文件原子替换 dump.rdb
5. 子进程退出,向主进程发送信号
6. 主进程更新统计信息
关键点:Copy-on-Write(写时复制)
fork() 创建子进程时,并不会立即复制全部内存数据,而是让父子进程共享同一块物理内存 。只有当主进程对某页内存进行写操作时,操作系统才会把那一页复制一份给子进程。
fork 瞬间:父子进程共享内存页
│
├── 主进程写 key "name" → 操作系统复制该页 → 子进程看到的仍是旧值
│
└── 子进程读 key "name" → 读到的是 fork 时刻的快照数据
这意味着:
- RDB 文件中的数据是 fork 时刻的数据快照,一致性有保障
- 快照期间主进程可以继续处理写命令,但会产生额外的内存开销(被修改的内存页需要复制)
2.4 RDB 文件结构
RDB 文件是二进制格式,包含以下部分:
+--------+--------+--------+--------+--------+--------+
| REDIS | 版本 | 数据库0 | 数据库1 | ... | EOF |
| 魔数 | 号 | 的键值对 | 的键值对 | | 校验 |
+--------+--------+--------+--------+--------+--------+
5字节 4字节 可变长度 可变长度 1字节
- 魔数 :
REDIS(5字节),标识这是 RDB 文件 - 版本号 :如
0011(4字节),表示 RDB 格式版本 - 数据库数据 :每个数据库以
0xFE+ 数据库编号开头,后面跟着键值对 - EOF :
0xFF(1字节),标识文件结束 - 校验和:文件末尾的 CRC64 校验值
2.5 RDB 的优缺点
| 优点 | 缺点 |
|---|---|
| 文件紧凑,适合备份和灾难恢复 | 可能丢失最后一次快照之后的数据(最多丢失几分钟) |
| 恢复速度极快(直接加载二进制文件) | fork 子进程时,数据量大可能导致主进程短暂阻塞 |
| 子进程负责 IO,主进程性能影响小 | 频繁 fork 会消耗 CPU 和内存(Copy-on-Write 开销) |
| 适合大规模数据的全量备份 | 不同版本的 RDB 文件格式可能不兼容 |
三、AOF 持久化
3.1 什么是 AOF?
AOF(Append Only File)持久化以日志形式 记录每一条写命令,Redis 重启时通过重新执行这些命令来恢复数据。
SET name "张三" → 追加到 appendonly.aof
HSET user:1 age 25 → 追加到 appendonly.aof
DEL temp_key → 追加到 appendonly.aof
3.2 AOF 的三种写入策略
AOF 的写入策略由 appendfsync 配置项控制:
| 策略 | 配置值 | 行为 | 安全性 | 性能 |
|---|---|---|---|---|
| always | appendfsync always |
每条命令都立即 fsync 到磁盘 | 最高,最多丢1条命令 | 最慢 |
| everysec | appendfsync everysec |
每秒 fsync 一次(默认) | 较高,最多丢1秒数据 | 较快 |
| no | appendfsync no |
由操作系统决定何时 fsync | 最低,丢数据量不可控 | 最快 |
生产环境推荐使用 everysec,在性能和安全性之间取得最佳平衡。
3.3 AOF 的执行流程
1. 客户端发送写命令(如 SET name "张三")
2. 主进程执行命令,更新内存数据
3. 将命令追加到 AOF 缓冲区(aof_buf)
4. 根据 appendfsync 策略,将缓冲区内容写入/同步到磁盘
AOF 缓冲区(aof_buf) 是内存中的一块区域,写命令先追加到这里,再由后台线程根据策略刷盘。
3.4 AOF 重写机制(BGREWRITEAOF)
为什么需要重写?
AOF 文件会不断增大。比如对一个 key 执行 100 次 INCR,AOF 文件会记录 100 条命令,但实际上只需要记录最终值即可。
# 重写前(100条命令)
INCR counter
INCR counter
INCR counter
...(共100条)
# 重写后(1条命令)
SET counter 100
重写流程
1. 主进程收到 BGREWRITEAOF 命令
2. fork() 创建子进程
3. 子进程遍历当前内存数据,为每个 key 生成一条"最小命令"
4. 写入临时 AOF 文件
5. 原子替换旧 AOF 文件
重写期间的写命令处理:
重写过程中,主进程仍在处理新的写命令。这些命令需要:
- 追加到当前的 AOF 文件(保证 AOF 完整性)
- 同时写入一个重写缓冲区(aof_rewrite_buf)
子进程完成重写后,主进程会把重写缓冲区中的增量命令追加到新的 AOF 文件末尾,确保数据不丢。
┌── 当前 AOF 文件
写命令 ──> aof_buf ─┤
── aof_rewrite_buf(增量缓冲)
│
▼
追加到新 AOF 文件末尾
自动重写触发条件
conf
auto-aof-rewrite-percentage 100 # AOF 文件比上次重写后增长 100% 时触发
auto-aof-rewrite-min-size 64mb # AOF 文件至少 64MB 才触发重写
两个条件同时满足才会触发自动重写。
3.5 AOF 文件结构
AOF 文件是纯文本格式,每一行是一条 Redis 命令:
*2
$6
SELECT
$1
0
*3
$3
SET
$4
name
$6
张三
这是 Redis 通信协议(RESP)格式:
*2表示有 2 个参数$6表示下一个参数长度为 6 字节- 实际内容跟在后面
3.6 AOF 的优缺点
| 优点 | 缺点 |
|---|---|
| 数据安全性高,最多丢1秒数据(everysec) | 文件体积通常比 RDB 大 |
| 可读性强,可以直接查看和编辑 | 恢复速度慢(需要逐条执行命令) |
| 追加写入,磁盘顺序写性能高 | 重写时 fork 子进程有开销 |
| 命令格式兼容,不同版本 Redis 都能读取 | 相同数据的多次修改会累积记录 |
四、RDB vs AOF 全面对比
| 对比维度 | RDB | AOF |
|---|---|---|
| 数据安全性 | 可能丢失几分钟数据 | 最多丢失1秒数据(everysec) |
| 恢复速度 | 快(直接加载二进制) | 慢(逐条重放命令) |
| 文件体积 | 小(紧凑二进制) | 大(文本命令日志) |
| 性能影响 | fork 时短暂阻塞 | 持续追加写入,影响较小 |
| 可读性 | 二进制,不可读 | 文本,可直接查看编辑 |
| 适用场景 | 全量备份、灾难恢复 | 数据安全性要求高的场景 |
五、生产环境配置建议
5.1 混合持久化(推荐)
Redis 4.0+ 支持混合持久化,结合 RDB 和 AOF 的优点:
conf
aof-use-rdb-preamble yes # AOF 重写时使用 RDB 格式作为前缀
开启后,AOF 重写生成的文件格式为:
+------------------+------------------+
| RDB 二进制数据 | AOF 增量命令 |
| (快速加载部分) | (精确恢复部分) |
+------------------+------------------+
- 前半部分是 RDB 格式的二进制数据,加载速度极快
- 后半部分是 AOF 格式的增量命令,保证数据完整性
- 恢复时先加载 RDB 部分,再重放 AOF 增量部分
5.2 推荐配置
conf
# RDB 配置
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir /var/lib/redis
# AOF 配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-use-rdb-preamble yes # 混合持久化
# 其他
rdbcompression yes # RDB 文件压缩
rdbchecksum yes # RDB 文件校验
5.3 不同场景的选型
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 缓存场景(数据可从 DB 重建) | 关闭持久化 | 追求极致性能,数据丢了无所谓 |
| 读多写少(配置数据、字典数据) | 仅 RDB | 数据变化少,RDB 快照足够 |
| 写多读少(日志、计数器) | RDB + AOF | 兼顾恢复速度和数据安全 |
| 金融/订单(数据不能丢) | RDB + AOF(always) | 最高安全性,牺牲部分性能 |
| 大规模数据(GB 级别) | RDB 为主 + AOF 辅助 | RDB 恢复快,AOF 兜底 |
六、常见问题与排查
6.1 RDB 保存失败
(error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk.
原因:磁盘空间不足、权限问题、内存不足导致 fork 失败。
解决:
bash
# 检查磁盘空间
df -h
# 检查 Redis 日志
tail -f /var/log/redis/redis-server.log
# 临时允许写入(不推荐生产使用)
config set stop-writes-on-bgsave-error no
6.2 AOF 文件损坏
# 使用 redis-check-aof 修复
redis-check-aof --fix appendonly.aof
6.3 恢复时选择哪个文件?
如果 RDB 和 AOF 同时开启,Redis 重启时优先加载 AOF 文件,因为 AOF 的数据通常更完整。
启动流程:
1. 检查 AOF 文件是否存在且开启 → 加载 AOF
2. 否则检查 RDB 文件 → 加载 RDB
3. 都没有 → 空数据库启动
七、总结
| 核心要点 | 说明 |
|---|---|
| RDB 是快照,AOF 是日志 | RDB 定时拍照片,AOF 持续记流水 |
| RDB 恢复快但可能丢数据 | 适合备份和灾难恢复 |
| AOF 数据安全但恢复慢 | 适合对数据完整性要求高的场景 |
| 混合持久化是最佳实践 | RDB 前缀加速加载 + AOF 增量保证完整 |
| 生产环境建议两者都开 | RDB 做定期备份,AOF 做实时保护 |