Skip to content

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 BGSAVE

3. 其他触发方式

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).rdb

3. 监控 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 校验机制。

基于 VitePress 构建