Redis 持久化机制详解:RDB vs AOF vs 混合持久化
Redis 是内存数据库,数据存储在内存中,一旦服务器宕机,内存中的数据就会丢失。为了保证数据安全,Redis 提供了 RDB 快照和 AOF 日志两种持久化方式,Redis 4.0 后还引入了 混合持久化。本文将深入剖析三种持久化机制的原理、优缺点和最佳实践。
📖 目录
为什么需要持久化?
Redis 的困境
┌─────────────────────────────────┐
│ Redis Server (进程) │
│ │
│ ┌─────────────────────┐ │
│ │ 内存数据 (RAM) │ │
│ │ • user:1001 │ │
│ │ • user:1002 │ │
│ │ • ... │ │
│ └─────────────────────┘ │
│ ↓ │
│ 💥 服务器宕机/重启 │
│ ↓ │
│ ❌ 数据全部丢失 │
└─────────────────────────────────┘
持久化的价值
- ✅ 数据安全:防止数据丢失
- ✅ 灾难恢复:服务器宕机后快速恢复
- ✅ 数据迁移:方便数据备份和迁移
- ✅ 主从复制:RDB 文件用于全量同步
持久化机制概览
Redis 提供了三种持久化方式:
| 持久化方式 | 持久化内容 | 文件格式 | 恢复速度 | 数据完整性 | 适用场景 |
|---|---|---|---|---|---|
| RDB | 内存快照 | 二进制 | 快 | 可能丢失 | 全量备份、主从同步 |
| AOF | 操作日志 | 文本 | 慢 | 更完整 | 数据安全要求高 |
| 混合 | RDB+AOF | 二进制+文本 | 较快 | 完整 | 兼顾性能和安全 |
工作流程图:
┌──────────────────────────────────────────────┐
│ Redis Server (运行中) │
├──────────────────────────────────────────────┤
│ 客户端写命令 → 执行命令 → 修改内存数据 │
│ ↓ │
│ ┌─────┴─────┐ │
│ ↓ ↓ │
│ ┌────────┐ ┌────────┐ │
│ │ RDB │ │ AOF │ │
│ │ 快照 │ │ 日志 │ │
│ └────┬───┘ └───┬────┘ │
│ ↓ ↓ │
│ dump.rdb appendonly.aof │
└──────────────────────────────────────────────┘
3.1 RDB 快照持久化
RDB 工作原理
RDB(Redis DataBase)是将某个时间点的内存数据以快照的形式保存到磁盘文件中。
核心思想:
时间 T1: 内存数据 → 拷贝一份 → 写入磁盘 (dump.rdb)
时间 T2: 继续处理写请求,不影响快照
RDB 文件示例:
$ file dump.rdb
dump.rdb: data
$ hexdump -C dump.rdb | head
00000000 52 45 44 49 53 30 30 30 39 fa 09 72 65 64 69 73 |REDIS0009..redis|
00000010 2d 76 65 72 05 37 2e 30 2e 30 fa 0a 72 65 64 69 |-ver.7.0.0..redi|
SAVE vs BGSAVE
Redis 提供两种生成 RDB 文件的命令:
1️⃣ SAVE 命令(同步,阻塞)
127.0.0.1:6379> SAVE
OK # 阻塞直到 RDB 文件生成完成
执行流程:
┌─────────────────────────────────┐
│ Redis 主进程 │
├─────────────────────────────────┤
│ 1. 收到 SAVE 命令 │
│ 2. ⛔ 停止处理所有客户端请求 │
│ 3. 遍历所有数据库 │
│ 4. 写入 dump.rdb 文件 │
│ 5. ✅ 恢复处理客户端请求 │
└─────────────────────────────────┘
# 期间所有命令都会被阻塞
# 适合关闭服务器前手动备份
2️⃣ BGSAVE 命令(异步,非阻塞)
127.0.0.1:6379> BGSAVE
Background saving started
执行流程:
┌─────────────────────────────────┐
│ Redis 主进程 │
├─────────────────────────────────┤
│ 1. 收到 BGSAVE 命令 │
│ 2. fork() 创建子进程 │
│ 3. ✅ 继续处理客户端请求 │
└─────────────┬───────────────────┘
│
↓
┌─────────────────────────────────┐
│ Redis 子进程 │
├─────────────────────────────────┤
│ 1. 遍历内存数据 │
│ 2. 写入临时文件 temp-xxx.rdb │
│ 3. 重命名为 dump.rdb │
│ 4. 退出 │
└─────────────────────────────────┘
SAVE vs BGSAVE 对比:
| 特性 | SAVE | BGSAVE |
|---|---|---|
| 阻塞 | 是(全程阻塞) | 否(fork 时短暂阻塞) |
| 内存占用 | 无额外占用 | 子进程占用(COW) |
| 性能影响 | 大(停止服务) | 小(正常服务) |
| 适用场景 | 关闭服务前 | 在线备份 |
COW 写时复制机制
BGSAVE 使用 Copy-On-Write(写时复制) 技术优化性能。
工作原理
1. fork() 创建子进程
┌──────────────┐ ┌──────────────┐
│ 父进程 │ │ 子进程 │
│ (Redis主进程)│ │ (BGSAVE) │
└──────┬───────┘ └──────┬───────┘
│ │
└──────┬───────────────┘
↓
┌──────────────┐
│ 内存数据 │
│ (共享物理内存)│
└──────────────┘
2. 父进程修改数据时,才复制该页
┌──────────────┐ ┌──────────────┐
│ 父进程 │ │ 子进程 │
│ (修改key1) │ │ (读取key1) │
└──────┬───────┘ └──────┬───────┘
│ │
↓ ↓
┌──────────────┐ ┌──────────────┐
│ 新内存页 │ │ 原内存页 │
│ key1=new │ │ key1=old │
└──────────────┘ └──────────────┘
示例:
# 1. 内存数据:10GB
# 2. BGSAVE fork 子进程
# 3. 期间只修改了 100MB 数据
# 4. 实际只复制 100MB 内存页
# 5. 总内存占用:10GB + 100MB
# 而不是:10GB + 10GB(全量复制)
COW 优势:
- ✅ 快速 fork:不需要复制全部内存
- ✅ 节省内存:只复制修改的页
- ✅ 不影响性能:父进程正常工作
注意事项:
# 最坏情况:全部数据都被修改
# 内存占用:原数据 + 修改后的数据(接近 2 倍)
# 建议:
# 1. 物理内存 >= Redis 使用内存 * 2
# 2. 避免在 BGSAVE 期间大量写入
RDB 文件格式
┌─────────────────────────────────────────────┐
│ RDB 文件结构 │
├─────────────────────────────────────────────┤
│ REDIS │ 版本 │ 辅助 │ 数据库 │ EOF │ 校验 │
│ (5B) │ (4B) │ 字段 │ 数据 │(1B) │(8B) │
└─────────────────────────────────────────────┘
详细结构:
┌────────────────┐
│ "REDIS0009" │ 魔数 + 版本号(Redis 7.0)
├────────────────┤
│ AUX 字段 │ redis-ver: 7.0.0
│ │ redis-bits: 64
│ │ ctime: 时间戳
│ │ used-mem: 内存占用
├────────────────┤
│ SELECT DB 0 │ 选择数据库 0
├────────────────┤
│ RESIZE DB │ 数据库大小信息
│ • hash_size │ 键空间大小
│ • expires_size│ 过期键数量
├────────────────┤
│ KEY-VALUE 数据 │
│ • type │ 对象类型
│ • key │ 键
│ • value │ 值
│ • expiretime │ 过期时间(可选)
├────────────────┤
│ SELECT DB 1 │ 数据库 1(如果有)
│ ... │
├────────────────┤
│ EOF (0xFF) │ 文件结束标志
├────────────────┤
│ CRC64 校验和 │ 数据完整性校验
└────────────────┘
KEY-VALUE 编码示例:
String 类型:
┌──────┬─────────┬───────────┐
│ type │ key │ value │
│ 0 │ "name" │ "Redis" │
└──────┴─────────┴───────────┘
List 类型:
┌──────┬─────────┬───────────────────┐
│ type │ key │ value │
│ 1 │ "list" │ ["a", "b", "c"] │
└──────┴─────────┴───────────────────┘
带过期时间:
┌────────────┬──────┬─────────┬───────────┐
│ expiretime │ type │ key │ value │
│ 1698307200 │ 0 │ "temp" │ "value" │
└────────────┴──────┴─────────┴───────────┘
触发机制
RDB 生成有多种触发方式:
1️⃣ 手动触发
# 同步保存(阻塞)
SAVE
# 异步保存(非阻塞)
BGSAVE
# 查看最后一次保存时间
LASTSAVE
2️⃣ 自动触发(配置文件)
# redis.conf
save 900 1 # 900秒内至少1次修改,触发 BGSAVE
save 300 10 # 300秒内至少10次修改
save 60 10000 # 60秒内至少10000次修改
# 满足任一条件即触发
# 多个 save 配置是"或"的关系
工作原理:
// Redis 定时任务检查
void serverCron() {
// 检查是否满足 save 条件
for (int i = 0; i < server.saveparamslen; i++) {
struct saveparam *sp = server.saveparams + i;
// 距离上次保存的秒数
time_t elapsed = server.unixtime - server.lastsave;
// 自上次保存后的修改次数
if (elapsed >= sp->seconds && server.dirty >= sp->changes) {
// 触发 BGSAVE
rdbSaveBackground();
}
}
}
3️⃣ 关闭服务器触发
# SHUTDOWN 命令会自动执行 SAVE
127.0.0.1:6379> SHUTDOWN
# 自动保存 RDB 文件后退出
# 等价于
127.0.0.1:6379> SAVE
127.0.0.1:6379> QUIT
4️⃣ 主从复制触发
# 从节点连接主节点时
# 主节点自动执行 BGSAVE 生成 RDB 文件
# 然后发送给从节点进行全量同步
# 主节点
INFO replication
# replication:
# ...
# rdb_bgsave_in_progress:1 ← 正在生成 RDB
5️⃣ FLUSHALL 触发
# 清空所有数据库
127.0.0.1:6379> FLUSHALL
# 会触发 RDB 保存(保存空数据库)
# 可以配置不保存
# redis.conf
save "" # 禁用自动保存
优缺点分析
✅ 优点
1. 恢复速度快
# RDB 是二进制紧凑格式
# 加载速度远快于 AOF
# 10GB 数据恢复时间对比
RDB: 约 1 分钟
AOF: 约 30 分钟
2. 文件紧凑
# RDB 文件小
dump.rdb: 100 MB
# AOF 文件大(即使重写后)
appendonly.aof: 300 MB
3. 性能影响小
# BGSAVE 使用子进程
# 不影响主进程处理命令
# 只在 fork 时短暂阻塞(毫秒级)
4. 适合灾难恢复
# 可以定期备份 RDB 文件
# 保留多个时间点的快照
/data/redis/dump-20251026-120000.rdb
/data/redis/dump-20251026-180000.rdb
/data/redis/dump-20251027-000000.rdb
❌ 缺点
1. 数据丢失风险
# 场景:每 5 分钟保存一次 RDB
save 300 1
# 时间线
12:00:00 → 生成 RDB
12:04:59 → 服务器宕机 💥
# 丢失 5 分钟的数据(12:00 ~ 12:05)
# 适合对数据丢失容忍度高的场景
2. fork 可能阻塞
# 大内存实例 fork 耗时较长
# 10GB 数据 fork 可能需要 200-500ms
# 高并发场景下,会造成短暂卡顿
3. 文件恢复时间长(大数据量)
# 虽然比 AOF 快,但大数据量仍需时间
# 100GB 数据可能需要 10+ 分钟
4. CPU 密集
# 生成 RDB 需要遍历所有数据
# CPU 使用率较高
# 可能影响其他应用
3.2 AOF 追加文件持久化
AOF 工作原理
AOF(Append Only File)通过保存 Redis 服务器执行的写命令来记录数据库状态。
核心思想:
客户端写命令 → 记录到 AOF 文件 → 重启时重新执行 → 恢复数据
AOF 文件示例:
$ cat appendonly.aof
*3
$3
SET
$4
name
$5
Redis
*3
$4
HSET
$9
user:1001
$4
name
$5
Alice
*2
$3
DEL
$4
temp
AOF 写入流程
AOF 持久化分为三个步骤:
┌──────────────────────────────────────────────┐
│ 1. 命令追加 (append) │
│ 客户端命令 → AOF 缓冲区 │
├──────────────────────────────────────────────┤
│ 2. 文件写入 (write) │
│ AOF 缓冲区 → OS 缓冲区 │
├──────────────────────────────────────────────┤
│ 3. 文件同步 (fsync) │
│ OS 缓冲区 → 磁盘 │
└──────────────────────────────────────────────┘
详细流程
// 1. 命令追加
void feedAppendOnlyFile(struct redisCommand *cmd, int dictid,
robj **argv, int argc) {
// 将命令转换为 RESP 协议格式
sds buf = catAppendOnlyGenericCommand(argv, argc);
// 追加到 AOF 缓冲区
server.aof_buf = sdscatlen(server.aof_buf, buf, sdslen(buf));
}
// 2. 文件写入(每次事件循环)
void flushAppendOnlyFile(int force) {
// 将 AOF 缓冲区写入 OS 缓冲区
nwritten = write(server.aof_fd, server.aof_buf, sdslen(server.aof_buf));
// 清空 AOF 缓冲区
sdsclear(server.aof_buf);
// 根据 fsync 策略同步到磁盘
if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
aof_fsync(server.aof_fd); // 立即同步
}
}
示例:
# 客户端执行
SET key1 "value1"
# 1. 命令追加到 AOF 缓冲区
server.aof_buf = "*3\r\n$3\r\nSET\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n"
# 2. 写入 OS 缓冲区
write(aof_fd, server.aof_buf, len)
# 3. fsync 同步到磁盘(根据策略)
fsync(aof_fd)
fsync 策略
AOF 提供 3 种 fsync 策略,控制何时将数据从 OS 缓冲区同步到磁盘:
# redis.conf
appendfsync always # 每次写命令都 fsync
appendfsync everysec # 每秒 fsync 一次(默认)
appendfsync no # 由操作系统决定(通常 30 秒)
1️⃣ always(最安全,性能最差)
每个写命令:
客户端命令 → AOF缓冲区 → OS缓冲区 → 💾 磁盘
↑
每次都 fsync
# 优点:数据最安全,最多丢失 1 条命令
# 缺点:性能最差(磁盘 IO 频繁)
# 适用:对数据安全要求极高的场景
性能影响:
# 测试(10 万次 SET 操作)
appendfsync always: 约 300 ops/s (磁盘限制)
appendfsync everysec: 约 60000 ops/s
appendfsync no: 约 80000 ops/s
2️⃣ everysec(推荐,平衡性能和安全)
每秒 fsync 一次:
客户端命令 → AOF缓冲区 → OS缓冲区 ─┐
↓
后台线程每秒 fsync
↓
💾 磁盘
# 优点:性能好,数据较安全
# 缺点:最多丢失 1 秒数据
# 适用:大多数场景(默认配置)
实现原理:
// 后台线程每秒执行
void *bioProcessBackgroundJobs(void *arg) {
while(1) {
// 等待 1 秒
sleep(1);
// 执行 fsync
if (server.aof_fsync == AOF_FSYNC_EVERYSEC) {
aof_fsync(server.aof_fd);
}
}
}
3️⃣ no(性能最好,最不安全)
由操作系统决定:
客户端命令 → AOF缓冲区 → OS缓冲区 ─┐
↓
OS 自己决定
(通常 30 秒)
↓
💾 磁盘
# 优点:性能最好
# 缺点:可能丢失 30 秒数据
# 适用:对数据丢失容忍度高的场景
策略对比
| 策略 | 性能 | 数据安全 | 可能丢失 | 适用场景 |
|---|---|---|---|---|
| always | 差 | 最高 | 1 条命令 | 金融、支付 |
| everysec | 好 | 高 | 1 秒数据 | 大多数场景(推荐) |
| no | 最好 | 低 | 30 秒数据 | 对丢失容忍度高 |
AOF 重写机制
AOF 文件会随着写命令不断增大,Redis 提供 AOF 重写机制压缩文件。
为什么需要重写?
# 原始 AOF 文件(冗余命令)
SET key 1
SET key 2
SET key 3
SET key 4
DEL key
LPUSH list a
LPUSH list b
LPUSH list c
# 重写后的 AOF 文件(只保留最终状态)
# key 已删除,不需要记录
LPUSH list c b a # 合并为一条命令
空间节省示例:
# 重写前
appendonly.aof: 500 MB (100 万条命令)
# 重写后
appendonly.aof: 50 MB (只有最终状态)
重写实现
BGREWRITEAOF 命令:
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
工作流程:
┌─────────────────────────────────┐
│ Redis 主进程 │
├─────────────────────────────────┤
│ 1. 收到 BGREWRITEAOF 命令 │
│ 2. fork() 创建子进程 │
│ 3. 继续处理客户端请求 │
│ 4. 新命令写入: │
│ • 旧 AOF 文件 │
│ • AOF 重写缓冲区 │
└──────────┬──────────────────────┘
│
↓
┌─────────────────────────────────┐
│ Redis 子进程 │
├─────────────────────────────────┤
│ 1. 遍历内存数据库 │
│ 2. 生成新的 AOF 文件 │
│ temp-rewriteaof-xxx.aof │
│ 3. 通知父进程完成 │
│ 4. 退出 │
└──────────┬──────────────────────┘
│
↓
┌─────────────────────────────────┐
│ Redis 主进程 │
├─────────────────────────────────┤
│ 1. 将 AOF 重写缓冲区追加到 │
│ 新 AOF 文件 │
│ 2. 重命名新文件为 │
│ appendonly.aof │
│ 3. 关闭旧文件,使用新文件 │
└─────────────────────────────────┘
示例:
# 重写过程
12:00:00 子进程开始重写(遍历内存数据)
12:00:05 主进程收到新写命令 SET key1 100
→ 写入旧 AOF 文件
→ 写入 AOF 重写缓冲区
12:00:10 子进程完成重写(temp-xxx.aof)
12:00:11 主进程将重写缓冲区追加到新文件
12:00:12 重命名新文件,替换旧文件
自动触发重写
# redis.conf
# AOF 文件大小超过上次重写后的 100%,触发重写
auto-aof-rewrite-percentage 100
# AOF 文件最小 64MB 才触发重写(避免频繁重写小文件)
auto-aof-rewrite-min-size 64mb
触发条件:
// 同时满足两个条件
if (server.aof_current_size > server.aof_rewrite_min_size &&
server.aof_current_size > server.aof_rewrite_base_size *
(1 + server.aof_rewrite_perc / 100.0)) {
// 触发 BGREWRITEAOF
}
示例:
# 上次重写后 AOF 文件:100 MB
# 当前 AOF 文件:200 MB
# 200 > 100 * 2 ✅ 触发重写
# 重写后:80 MB
# 下次触发:160 MB
优缺点分析
✅ 优点
1. 数据更安全
# everysec 策略最多丢失 1 秒数据
# always 策略最多丢失 1 条命令
# 远好于 RDB(可能丢失数分钟数据)
2. 可读性好
# AOF 文件是文本格式
$ cat appendonly.aof
*3
$3
SET
$4
name
$5
Redis
# 可以直接查看、修改、分析
3. 误操作可恢复
# 场景:误执行 FLUSHALL
# 1. 停止 Redis
$ redis-cli SHUTDOWN NOSAVE
# 2. 编辑 AOF 文件,删除 FLUSHALL 命令
$ vi appendonly.aof
# 删除最后的 FLUSHALL
# 3. 重启 Redis
$ redis-server redis.conf
# 数据恢复!
4. 自动重写压缩
# 自动压缩 AOF 文件
# 不需要人工干预
❌ 缺点
1. 文件体积大
# 即使重写后,AOF 文件仍比 RDB 大
RDB: 100 MB (二进制紧凑格式)
AOF: 300 MB (文本格式 + 命令冗余)
2. 恢复速度慢
# 需要逐条执行命令
# 大文件恢复耗时长
# 10GB 数据恢复对比
RDB: 约 1 分钟
AOF: 约 30 分钟
3. 性能影响大
# 写入性能(always 策略)
appendfsync always: 约 300 ops/s
RDB: 约 80000 ops/s
# 磁盘 IO 频繁
4. 可能产生 bug
# 历史上 AOF 重写存在 bug
# 导致数据丢失或不一致
# 建议:
# 1. 使用稳定版本
# 2. 同时开启 RDB 和 AOF
3.3 混合持久化
混合持久化原理
Redis 4.0 引入混合持久化,结合 RDB 和 AOF 的优点。
核心思想:
AOF 文件 = RDB 格式数据 + AOF 格式增量命令
┌──────────────────────────────────┐
│ 混合 AOF 文件 │
├──────────────────────────────────┤
│ [RDB 格式] │
│ • 内存快照(二进制) │
│ • 快速加载 │
├──────────────────────────────────┤
│ [AOF 格式] │
│ • 增量写命令(文本) │
│ • 自上次重写后的命令 │
└──────────────────────────────────┘
工作流程:
1. AOF 重写时
┌─────────────────────┐
│ 子进程 │
├─────────────────────┤
│ 遍历内存数据 │
│ ↓ │
│ 以 RDB 格式写入 │ ← 快速、紧凑
│ ↓ │
│ 生成 temp-xxx.aof │
└─────────────────────┘
2. 日常写入
┌─────────────────────┐
│ 主进程 │
├─────────────────────┤
│ 新的写命令 │
│ ↓ │
│ 以 AOF 格式追加 │ ← 增量命令
│ ↓ │
│ appendonly.aof │
└─────────────────────┘
3. 数据恢复
┌─────────────────────┐
│ Redis 启动 │
├─────────────────────┤
│ 1. 加载 RDB 部分 │ ← 快速
│ 2. 执行 AOF 部分 │ ← 完整
└─────────────────────┘
配置与使用
# redis.conf
# 开启 AOF
appendonly yes
# 开启混合持久化(Redis 4.0+)
aof-use-rdb-preamble yes # 默认 yes
查看混合 AOF 文件:
$ hexdump -C appendonly.aof | head
00000000 52 45 44 49 53 30 30 30 39 fa 09 72 65 64 69 73 |REDIS0009..redis|
↑ RDB 魔数
$ tail appendonly.aof
*3
$3
SET
$4
key1
$5
value
优势分析
| 特性 | RDB | AOF | 混合持久化 |
|---|---|---|---|
| 恢复速度 | 快 | 慢 | 较快 ✅ |
| 文件大小 | 小 | 大 | 中 ✅ |
| 数据完整性 | 差 | 好 | 好 ✅ |
| 性能影响 | 小 | 大 | 中 ✅ |
混合持久化优势:
- ✅ 快速恢复:RDB 部分快速加载
- ✅ 数据完整:AOF 部分保证完整性
- ✅ 文件紧凑:RDB 格式比纯 AOF 小
- ✅ 兼容性好:可以关闭混合模式回退到纯 AOF
3.4 持久化策略选择
性能对比
写入性能(10 万次 SET 操作):
| 配置 | QPS | 说明 |
|---|---|---|
| 无持久化 | 80000 | 基准性能 |
| RDB (save 60 10000) | 78000 | 几乎无影响 |
| AOF (everysec) | 60000 | 性能下降 25% |
| AOF (always) | 300 | 性能下降 99%+ |
| RDB + AOF | 60000 | 同 AOF everysec |
文件大小(10GB 数据):
| 持久化方式 | 文件大小 | 压缩率 |
|---|---|---|
| RDB | 8 GB | 最小 |
| AOF(重写后) | 15 GB | 中等 |
| 混合持久化 | 10 GB | 较小 |
恢复速度(10GB 数据):
| 持久化方式 | 恢复时间 |
|---|---|
| RDB | 1-2 分钟 |
| AOF | 20-30 分钟 |
| 混合持久化 | 3-5 分钟 |
数据安全性对比
| 持久化方式 | 可能丢失的数据 | 适用场景 |
|---|---|---|
| 无持久化 | 全部数据 | 纯缓存 |
| RDB (save 300 10) | 5 分钟数据 | 允许丢失部分数据 |
| AOF (everysec) | 1 秒数据 | 一般业务(推荐) |
| AOF (always) | 最多 1 条命令 | 金融、支付 |
| RDB + AOF | 1 秒数据 + 双重保障 | 重要业务 |
| 混合持久化 | 1 秒数据 | 平衡方案(推荐) |
使用场景建议
1️⃣ 纯缓存场景
# 配置
save "" # 禁用 RDB
appendonly no # 禁用 AOF
# 适用
# • 缓存数据,可以从数据库重建
# • 对数据丢失容忍度高
# • 追求极致性能
2️⃣ 一般业务场景(推荐)
# 配置
appendonly yes
aof-use-rdb-preamble yes # 混合持久化
appendfsync everysec # 每秒 fsync
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
save "" # 禁用 RDB(AOF 已足够)
# 适用
# • 大多数业务
# • 平衡性能和安全
# • 最多丢失 1 秒数据
3️⃣ 高可用场景
# 配置(双重保障)
# RDB
save 900 1
save 300 10
save 60 10000
# AOF
appendonly yes
aof-use-rdb-preamble yes
appendfsync everysec
# 适用
# • 重要业务数据
# • 需要双重保障
# • RDB 用于快速恢复,AOF 保证数据完整
4️⃣ 金融/支付场景
# 配置(最高安全)
appendonly yes
appendfsync always # 每条命令都 fsync
aof-use-rdb-preamble yes
# 适用
# • 金融支付
# • 不允许数据丢失
# • 可以接受性能下降
5️⃣ 主从复制场景
# 主节点
save 900 1
save 300 10
appendonly yes
appendfsync everysec
# 从节点(只读)
save "" # 从节点可以不开启持久化
appendonly no # 数据从主节点同步
3.5 数据恢复流程
恢复优先级
Redis 启动时按以下优先级恢复数据:
┌─────────────────────────────────┐
│ Redis 启动 │
└─────────────┬───────────────────┘
↓
AOF 存在?
├─ 是 ───→ 加载 AOF 文件
│ (优先级更高)
└─ 否
↓
RDB 存在?
├─ 是 ───→ 加载 RDB 文件
└─ 否
↓
空数据库启动
原因:
AOF 优先级更高,因为:
1. AOF 数据更完整(最多丢失 1 秒)
2. RDB 可能丢失更多数据(数分钟)
# 如果同时存在 AOF 和 RDB
# Redis 会忽略 RDB,只加载 AOF
恢复步骤
1️⃣ AOF 恢复
# 1. 停止 Redis
$ redis-cli SHUTDOWN
# 2. 检查 AOF 文件
$ redis-check-aof appendonly.aof
AOF analyzed: filename=appendonly.aof, size=1048576, ok_up_to=1048576, ok_up_to_line=10000, diff=0
# 3. 如果文件损坏,修复
$ redis-check-aof --fix appendonly.aof
# 会删除损坏部分后的所有数据
# 4. 启动 Redis
$ redis-server redis.conf
# 自动加载 AOF 文件
2️⃣ RDB 恢复
# 1. 停止 Redis
$ redis-cli SHUTDOWN
# 2. 将 RDB 文件复制到 Redis 数据目录
$ cp /backup/dump.rdb /var/lib/redis/dump.rdb
# 3. 确保文件权限
$ chown redis:redis /var/lib/redis/dump.rdb
# 4. 启动 Redis
$ redis-server redis.conf
# 自动加载 RDB 文件
3️⃣ 混合持久化恢复
# 混合 AOF 文件包含 RDB 和 AOF 两部分
# Redis 自动识别并正确加载
$ redis-server redis.conf
# 1. 识别 AOF 文件是混合格式
# 2. 加载 RDB 部分(快速)
# 3. 执行 AOF 部分(增量命令)
恢复异常处理
AOF 文件损坏
# 错误信息
Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>
# 解决方案
$ redis-check-aof --fix appendonly.aof
# 删除损坏部分后的数据
RDB 文件损坏
# 错误信息
Short read or OOM loading DB. Unrecoverable error, aborting now.
# 解决方案
1. 使用备份的 RDB 文件
2. 或者删除 RDB 文件,从 AOF 恢复
恢复时间过长
# 场景:100GB AOF 文件,恢复需要 1+ 小时
# 优化方案
1. 使用 RDB 文件(如果可接受数据丢失)
2. 使用混合持久化(更快)
3. 增加服务器内存,加快加载速度
持久化最佳实践
1️⃣ 推荐配置(生产环境)
# redis.conf
# ============ 持久化配置 ============
# AOF(推荐开启)
appendonly yes
aof-use-rdb-preamble yes # 混合持久化
appendfsync everysec # 每秒 fsync
appendfilename "appendonly.aof"
# AOF 重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes # 加载截断的 AOF 文件
# RDB(可选,用于备份)
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
# 数据目录
dir /var/lib/redis
2️⃣ 定期备份
# 备份脚本
#!/bin/bash
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR=/backup/redis
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份 RDB 文件
cp /var/lib/redis/dump.rdb $BACKUP_DIR/dump-$DATE.rdb
# 备份 AOF 文件
cp /var/lib/redis/appendonly.aof $BACKUP_DIR/appendonly-$DATE.aof
# 压缩备份
tar -czf $BACKUP_DIR/redis-backup-$DATE.tar.gz $BACKUP_DIR/*-$DATE.*
# 删除 7 天前的备份
find $BACKUP_DIR -name "redis-backup-*.tar.gz" -mtime +7 -delete
echo "Backup completed: $DATE"
定时任务:
# crontab -e
# 每天凌晨 2 点备份
0 2 * * * /opt/scripts/redis-backup.sh
3️⃣ 监控持久化状态
# 查看持久化状态
127.0.0.1:6379> INFO persistence
# Persistence
loading:0
current_cow_size:0
current_cow_size_age:0
current_fork_perc:0.00
current_save_keys_processed:0
current_save_keys_total:0
rdb_changes_since_last_save:100 # 自上次保存后的修改
rdb_bgsave_in_progress:0 # 是否正在 BGSAVE
rdb_last_save_time:1698307200 # 上次保存时间
rdb_last_bgsave_status:ok # 上次 BGSAVE 状态
rdb_last_bgsave_time_sec:5 # 上次 BGSAVE 耗时
aof_enabled:1 # AOF 是否开启
aof_rewrite_in_progress:0 # 是否正在重写
aof_last_rewrite_time_sec:10 # 上次重写耗时
aof_current_size:1048576 # 当前 AOF 文件大小
aof_base_size:524288 # 上次重写后的大小
4️⃣ 性能优化
# 1. 避免 fork 阻塞
# 减少内存使用,加快 fork 速度
maxmemory 8gb
maxmemory-policy allkeys-lru
# 2. 使用 SSD
# AOF 频繁写磁盘,SSD 性能更好
# 3. 避免大实例
# 建议单实例内存 < 10GB
# 大数据量使用集群
# 4. 调整重写阈值
# 避免频繁重写
auto-aof-rewrite-min-size 128mb # 提高到 128MB
# 5. 监控 fork 时间
# 如果 fork 耗时过长,考虑缩小实例
5️⃣ 灾难恢复预案
# 1. 双机备份
# 主服务器 + 从服务器 + 离线备份
# 2. 异地备份
# 定期将备份文件同步到异地
# 3. 恢复演练
# 定期演练数据恢复流程
# 4. 监控告警
# 持久化失败时及时告警
常见问题解答
Q1: RDB 和 AOF 应该选哪个?
A: 推荐同时开启(混合持久化)。
- 仅 RDB:数据丢失风险大
- 仅 AOF:恢复速度慢
- RDB + AOF:兼顾性能和安全(推荐)
Q2: appendfsync 应该选哪个?
A: 大多数场景选 everysec(默认)。
- always:金融/支付场景
- everysec:一般业务(推荐)
- no:纯缓存场景
Q3: fork 导致的阻塞如何优化?
A:
# 1. 减少内存使用
maxmemory 8gb
# 2. 使用更快的 CPU
# fork 是 CPU 密集操作
# 3. 避免大实例
# 建议单实例 < 10GB
# 4. 监控 fork 耗时
INFO stats | grep fork
Q4: AOF 文件过大怎么办?
A: 触发 AOF 重写。
# 手动重写
BGREWRITEAOF
# 调整自动重写阈值
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Q5: 如何选择备份策略?
A:
# 开发环境
save "" # 不持久化
appendonly no
# 生产环境
appendonly yes # AOF
save 900 1 # RDB(用于备份)
# 定期备份到异地存储
总结
本文深入剖析了 Redis 的三种持久化机制:
RDB 快照
- ✅ 优点:恢复快、文件小、性能影响小
- ❌ 缺点:可能丢失数据、fork 阻塞
- 🎯 适用:全量备份、主从同步
AOF 日志
- ✅ 优点:数据安全、可读性好、可恢复误操作
- ❌ 缺点:文件大、恢复慢、性能影响大
- 🎯 适用:数据安全要求高
混合持久化
- ✅ 优点:快速恢复、数据完整、文件紧凑
- ❌ 缺点:需要 Redis 4.0+
- 🎯 适用:生产环境(推荐)
最佳实践
- ✅ 生产环境开启混合持久化
- ✅ 使用
appendfsync everysec - ✅ 定期备份到异地
- ✅ 监控持久化状态
- ✅ 定期演练恢复流程
理解持久化机制,能帮助你:
- ✅ 保证数据安全
- ✅ 优化性能
- ✅ 快速恢复故障
- ✅ 制定合理的备份策略
💡 下一篇预告:《Redis 事件驱动与线程模型:Reactor 模式与 IO 多线程》
敬请期待!