RDB:快照持久化原理与配置
凌晨 3 点,监控告警:Redis 所在的服务器宕机了。
你从睡梦中惊醒,第一反应是:数据还在吗?
如果 Redis 开启了 RDB 持久化,那还好——最多丢失几分钟的数据。如果没有……
希望你的老板脾气好。
RDB 是什么?
RDB(Redis Database)是 Redis 的快照持久化机制。
它会在某个时刻,把 Redis 内存中的所有数据都拍一张「快照」,保存到磁盘上。
┌─────────────────────────────────────────────────────────────────┐
│ Redis 内存数据 │
│ │
│ Key1 → "hello" Key2 → [1, 2, 3] Key3 → {name: "redis"} │
│ │
└─────────────────────────────────────────────────────────────────┘
│
│ BGSAVE / SAVE
▼
┌─────────────────────────────────────────────────────────────────┐
│ dump.rdb 文件 │
│ (磁盘上的二进制快照) │
└─────────────────────────────────────────────────────────────────┘恢复时,Redis 读取这个文件,就能还原宕机前的数据状态。
触发 RDB 的方式
1. 自动触发:BGSAVE
Redis 会根据配置自动执行 BGSAVE:
bash
# redis.conf
# 多久执行一次快照,满足以下任一条件即触发
# 900 秒内至少 1 个 key 变化
save 900 1
# 300 秒内至少 10 个 key 变化
save 300 10
# 60 秒内至少 10000 个 key 变化
save 60 10000
# 关闭自动快照
save ""BGSAVE 会 fork 一个子进程来执行快照,主进程继续处理请求,不阻塞。
2. 手动触发
bash
# 阻塞方式(不推荐,会阻塞所有客户端)
redis-cli SAVE
# 非阻塞方式(推荐)
redis-cli BGSAVE3. 其他触发方式
bash
# 主从复制时,主机会自动生成 RDB
# 执行 SHUTDOWN 命令时,会自动执行 SAVE
# 执行 DEBUG SEGFAULT 命令时(用于调试)RDB 的工作原理
COW(Copy-On-Write)机制
RDB 的核心是 Copy-On-Write(写时复制):
┌─────────────────────────────────────────────────────────────────┐
│ 父进程(主进程) │
│ │
│ 数据区域 ──→ [Key1][Key2][Key3]...[KeyN] │
│ │
└─────────────────────────────────────────────────────────────────┘
│
│ fork()
▼
┌─────────────────────────────────────────────────────────────────┐
│ 子进程(RDB 进程) │
│ │
│ 数据区域(共享父进程的内存页) │
│ │
└─────────────────────────────────────────────────────────────────┘
│
│ 主进程修改数据
▼
┌─────────────────────────────────────────────────────────────────┐
│ 父进程(主进程) │
│ │
│ 数据区域 ──→ [Key1'][Key2][Key3]...[KeyN] │
│ ↑ │
│ └── 被修改的页会被复制一份出来(COW) │
│ │
│ 子进程(RDB 进程) │
│ │
│ 数据区域 ──→ [Key1][Key2][Key3]...[KeyN] │
│ ↑ │
│ └── 看到的仍然是修改前的数据 │
│ │
└─────────────────────────────────────────────────────────────────┘子进程 fork 出来后,与父进程共享物理内存页。当父进程要修改某个数据页时,才会真正复制一份给父进程使用。
这样,子进程看到的是 fork 那一瞬间的内存快照,而父进程可以正常处理请求。
RDB 文件结构
生成的 RDB 文件大致结构:
┌─────────────────────────────────────────────────────────────────┐
│ REDIS │ db_version │ database │ EOF │ checksum │
│ 魔数 │ 数据库版本 │ 数据内容 │ 结束符 │ 校验和 │
│ 5字节 │ 9字节 │ 可变 │ 1字节 │ 8字节 │
└─────────────────────────────────────────────────────────────────┘每个 database 部分:
┌─────────────────────────────────────────────────────────────────┐
│ SELECTDB │ db_number │ key_value_pairs │ EXPIRETIME_MS │
│ 1字节 │ 4字节 │ 可变 │ 可变 │
└─────────────────────────────────────────────────────────────────┘RDB 的优缺点
优点
| 优点 | 说明 |
|---|---|
| 恢复速度快 | 只需加载一个文件,二进制格式,解析快 |
| 文件紧凑 | 相比 AOF,RDB 文件更小 |
| 性能影响小 | 子进程完成快照,不阻塞主进程 |
| 适合备份 | 可以保留多个历史版本(配合 cron) |
缺点
| 缺点 | 说明 |
|---|---|
| 可能丢失数据 | 两次快照之间数据会丢失 |
| fork 耗时 | 数据量大时,fork 可能耗时较长(毫秒~秒级) |
| fork 占用内存 | COW 机制导致 fork 期间内存翻倍风险 |
RDB 的配置详解
bash
# redis.conf
# 快照触发条件(可以配置多个)
save 900 1 # 900 秒内有 1 次写操作
save 300 10 # 300 秒内有 10 次写操作
save 60 10000 # 60 秒内有 10000 次写操作
# RDB 文件名
dbfilename dump.rdb
# RDB 文件存放目录
dir /var/lib/redis
# 如果 lastsave 失败,是否停止写入
stop-writes-on-bgsave-error yes
# 是否压缩 RDB 文件(压缩会消耗 CPU)
rdbcompression yes
# 是否校验 RDB 文件
rdbchecksum yes
# 如果从节点开启 RDB,设置为 yes
# 防止从节点执行不必要的快照
replica-serve-stale-data yes生产环境最佳实践
1. 合理配置快照时机
bash
# 场景:业务对数据丢失敏感
save 60 1000 # 最多丢失 1 分钟数据
# 场景:业务对性能敏感,可以接受更多丢失
save 300 10 # 最多丢失 5 分钟数据2. 配合定时任务备份
bash
# 每天凌晨 3 点备份 RDB 文件到远程存储
0 3 * * * cp /var/lib/redis/dump.rdb /backup/redis-$(date +\%Y\%m\%d).rdb3. 监控 fork 耗时
Redis 会在日志中打印 fork 耗时:
* Background saving started by pid 12345
* DB saved on disk如果 fork 耗时超过 1 秒,需要警惕:
- 数据量是否过大
- 是否需要升级服务器内存
- 是否需要优化数据结构
4. 内存翻倍风险
java
/**
* RDB 的 COW 机制可能导致内存翻倍
*
* 场景:
* - Redis 占用 8GB 内存
* - 执行 BGSAVE 时 fork
* - COW 期间,如果父进程修改了 8GB 数据
* - 理论上峰值内存可能达到 16GB
*
* 预防措施:
* - 保证机器有足够的可用内存(建议预留 50%)
* - 使用 redis INFO 查看 used_memory_human
* - 监控 process_resident_memory
*/Java 客户端使用
java
import redis.clients.jedis.Jedis;
import java.io.File;
public class RedisRDBDemo {
public static void main(String[] args) {
try (Jedis jedis = new Jedis("localhost", 6379)) {
// 手动触发 BGSAVE
Process bgSave = jedis.bgSave();
System.out.println("BGSAVE 进程 ID: " + bgSave.pid());
// 获取上次成功保存的时间
long lastSave = jedis.lastSave();
System.out.println("上次 SAVE 时间: " + new java.util.Date(lastSave * 1000));
// 阻塞式 SAVE(不推荐在生产环境使用)
// jedis.save();
}
}
}RDB vs AOF 选择
| 场景 | 推荐 |
|---|---|
| 能容忍分钟级数据丢失 | RDB |
| 数据量巨大,恢复速度要求高 | RDB |
| 数据要求绝对安全,不能丢失 | AOF |
| 需要精细的持久化控制 | AOF |
总结
RDB 是 Redis 持久化的基础机制:
- 原理:fork 子进程 + COW 机制,生成内存快照
- 触发:自动(根据 save 配置)+ 手动(BGSAVE)
- 恢复:读取 dump.rdb 文件即可
- 优点:恢复快、文件紧凑
- 缺点:可能丢失数据
留给你的问题
RDB 文件是二进制格式,如何判断一个 RDB 文件是否完整可用?
提示:关注 RDB 文件的 EOF 标记和 checksum 校验机制。
