一、为什么需要持久化
Redis 所有数据在内存中,进程退出 / 宕机 / 掉电 = 数据全丢。但很多场景(缓存、会话、排行榜)又要求重启后数据还在,所以 Redis 提供两种持久化机制:
| 方案 | 原理 | 特点 |
|---|---|---|
| RDB | 定期把内存全量快照写磁盘 | 二进制、恢复快、可能丢数据 |
| AOF | 把每条写命令追加到日志 | 文本协议、数据安全、文件大、恢复慢 |
二、RDB 快照
2.1 是什么
在某个时间点,把 Redis 内存中的全量数据 生成一个二进制快照文件(默认 dump.rdb)。重启时加载这个文件恢复数据。
2.2 手动触发:save 与 bgsave
127.0.0.1:6379> SAVE # 主进程同步执行,阻塞所有客户端,直到快照完成
127.0.0.1:6379> BGSAVE # fork 子进程后台执行,主进程继续服务
| 命令 | 执行方式 | 阻塞主进程? | 生产使用 |
|---|---|---|---|
SAVE |
主进程直接写盘 | ✅ 全程阻塞 | ❌ 几乎不用 |
BGSAVE |
fork 子进程写盘 | fork 瞬间短暂阻塞 | ✅ 推荐 |
2.3 fork + 写时复制(COW)原理
父进程(Redis 主进程) 子进程(BGSAVE)
│ │
├─ fork() ──────────────────▶│
│ 共享内存页 │ 拿到当时内存快照
│ │
├─ 继续处理客户端写命令 │
│ 修改某内存页 │
│ ├─ COW 触发: │
│ │ 复制那一页到自己 │
│ │ 子进程仍看旧页 │
│ ▼ ▼
│ 主进程用新页 子进程写旧页到 dump.rdb
▼
- fork 瞬间:父子进程共享同一份内存页(页表指向同一物理页);
- 写时复制(Copy-On-Write):主进程修改某一页时,操作系统才把那一页复制一份,子进程看到的还是旧快照;
- 所以子进程写的是fork 那一刻的一致性快照,主进程期间的新数据不影响快照。
隐藏成本:fork 本身要复制整个内存页表,数据量越大(比如 20GB),fork 阻塞越久------这是 BGSAVE 在大内存下的痛点。
2.4 自动触发
# redis.conf
save 900 1 # 900 秒内至少 1 次修改 → 触发 BGSAVE
save 300 10 # 300 秒内至少 10 次修改 → 触发
save 60 10000 # 60 秒内至少 10000 次修改 → 触发
另外:执行 SHUTDOWN 且配置了 save 规则时,会自动触发一次 RDB。
2.5 工作流程
子进程写临时文件 temp-*.rdb
│
▼ 写完
父进程原子 rename:temp-*.rdb → dump.rdb(替换旧文件)
不直接覆盖 dump.rdb,先写临时文件再 rename------避免写一半断电导致原文件损坏。
2.6 文件位置与校验
dir /var/lib/redis # 快照目录(redis.conf 的 dir 变量)
dbfilename dump.rdb # 快照文件名
文件损坏时用工具检查:
redis-check-rdb /var/lib/redis/dump.rdb
# 路径:/usr/bin/redis-check-rdb(与 redis-server 同目录,本质是同一个二进制的软链接)
2.7 RDB 优缺点
| 优点 | 缺点 |
|---|---|
| 二进制紧凑,恢复快 | 两次快照之间宕机丢数据 |
| fork 子进程,主进程阻塞小 | 大内存 fork 瞬间阻塞明显 |
| 适合冷备份、全量复制 | 不能做任意时间点恢复(PITR) |
三、AOF 追加日志
3.1 是什么
AOF(Append Only File)以 RESP 文本协议记录每一条写命令,重启时重放这些命令重建数据。类比 MySQL 的 binlog。
# 一条 set 命令在 AOF 里长这样(纯文本)
*3
$3
SET
$3
key
$5
value
3.2 开启方式
appendonly yes # 默认 no(Redis 默认只用 RDB)
appendfilename "appendonly.aof"
加粗要点 :同时开 RDB + AOF 时,重启加载优先用 AOF(因为 AOF 数据更全)。
3.3 三种 fsync 策略
在记录的时候,显示保存在aof_buffer缓冲区中,根据刷盘策略的不同,落盘时间有差异。
appendfsync everysec # 默认 everysec
| 策略 | 行为 | 安全性 | 性能 |
|---|---|---|---|
always |
每条命令写盘后立刻 fsync | 最安全,最多丢 0 条 | 最慢 |
everysec |
write 到 OS 缓冲,每秒由后台线程 fsync | 可能丢 1 秒数据 | 折中,推荐 |
no |
只 write,fsync 交给 OS 自己决定 | 可能丢很多 | 最快 |
3.4 AOF 重写(BGREWRITEAOF)
AOF 持续追加,文件会越来越大,而且有大量冗余命令:
set k 111
set k 222
set k 333
→ 重写后只需要:set k 333
127.0.0.1:6379> BGREWRITEAOF # 手动触发
重写不是读旧 AOF 命令合并,而是子进程直接遍历当前内存数据,生成最小命令集。(这一点和RDB快照 相同)
3.5 双 buffer 机制
fork 瞬间:
父进程 ──▶ 子进程(拿内存快照,开始写新 AOF 临时文件)
│
▼
子进程遍历内存写新 AOF
│
fork 之后主进程继续执行新命令:
每条新命令同时写两个 buffer:
├─ aof_buffer → 正常刷旧 AOF 文件(保证宕机时旧 AOF 连续可用)
└─ aof_rewrite_buffer → 等子进程写完后,由父进程追加到新 AOF 尾部
│
▼
子进程写完 → 发信号给父进程
父进程把 rewrite_buffer 内容追加到新 AOF 尾部
原子 rename 新文件替换旧 AOF

两个 buffer 的分工:
| buffer | 作用 |
|---|---|
aof_buffer |
主进程一直用,写旧 AOF 文件,保证重写期间宕机也不丢数据 |
aof_rewrite_buffer |
fork 之后的新命令额外抄一份,等子进程写完后补到新 AOF 尾部 |
3.6 自动重写触发
auto-aof-rewrite-percentage 100 # 当前 AOF 比上次重写后增大 100%(翻倍)
auto-aof-rewrite-min-size 64mb # 且文件大于 64MB 才触发(避免小文件重写)
含义:AOF 文件比上次重写时增长超过 100%,且超过 64MB,自动 BGREWRITEAOF。
四、混合持久化(Redis 4.0+)
AOF 本身是纯文本 RESP。但 4.0 引入了混合持久化:
aof-use-rdb-preamble yes # 4.0+ 默认开启
开启后,AOF 重写时:
新 AOF 文件结构:
┌──────────────────────────┬──────────────────────────┐
│ 前半段:RDB 二进制全量快照 │ 后半段:RESP 文本增量命令 │
└──────────────────────────┴──────────────────────────┘
↑ 加载快 ↑ 数据安全
- 前半段 RDB:二进制格式,重启加载快;
- 后半段 AOF:fork 之后的增量命令,保留秒级数据安全;
- 兼顾 RDB 的恢复速度和 AOF 的数据完整性。
五、RDB vs AOF vs 混合
| 维度 | RDB | AOF | 混合持久化 |
|---|---|---|---|
| 数据格式 | 二进制 | RESP 文本 | 前 RDB 后 AOF |
| 数据安全 | 可能丢几分钟 | everysec 最多丢 1 秒 | 最多丢 1 秒 |
| 恢复速度 | 快 | 慢(重放命令) | 较快 |
| 文件体积 | 小 | 大 | 中等 |
| 适用场景 | 冷备份、主从复制 | 数据安全要求高 | 生产推荐 |
启动加载顺序:AOF 开启 → 优先加载 AOF(或混合文件);否则加载 RDB。