前置知识: Redis

RDB快照持久化

8 minAdvanced2026/6/14

Redis RDB快照持久化:save与bgsave机制、写时复制原理、配置优化与恢复流程

1. RDB 概述

RDB(Redis Database)是 Redis 的默认持久化方式,通过在指定时间间隔内对数据集进行快照(snapshot),将内存中的数据以二进制文件形式写入磁盘。生成的 RDB 文件紧凑、体积小,非常适合用于备份和灾难恢复。

RDB 的核心优势:

  • 紧凑高效:RDB 文件是经过压缩的二进制格式,体积远小于 AOF 日志
  • 恢复速度快:直接加载二进制文件,恢复大数据集的速度远快于 AOF
  • 对性能影响小bgsave 由子进程完成,主进程几乎不受影响
  • 适合冷备:可以定期将 RDB 文件归档到远程存储

RDB 的主要不足:

  • 数据丢失风险:非实时持久化,两次快照之间的数据可能丢失
  • fork 开销:数据集很大时,fork() 子进程可能耗时较长
  • 不支持增量:每次都是全量快照,无法记录增量变更

2. save 命令

save 命令以阻塞方式生成 RDB 文件:

SAVE

执行流程:

  1. 主进程直接调用 rdbSave() 函数
  2. 主进程被阻塞,无法响应任何客户端请求
  3. RDB 文件写入完成后,主进程恢复服务

适用场景

  • 数据集很小,阻塞时间可忽略
  • 需要确保快照完成的测试环境
  • Redis 正在关闭时(shutdown 触发 save)

风险:生产环境中数据集较大时,save 可能导致数秒甚至数十秒的阻塞,严重影响可用性。

3. bgsave 命令

bgsave(Background Save)是生产环境推荐的方式,通过 fork() 子进程完成快照:

BGSAVE

执行流程:

  1. 主进程调用 fork() 创建子进程
  2. 子进程调用 rdbSave() 将数据写入临时 RDB 文件
  3. 写入完成后,子进程将临时文件原子性地重命名为正式 RDB 文件
  4. 子进程退出,主进程收到信号
主进程 ──fork()──> 子进程
  │                    │
  │ 继续处理请求        │ rdbSave()
  │                    │ 写入临时文件
  │                    │ rename()
  │<──SIGCHLD──────── │ 退出

  └── 更新 rdb_save_time_last

查看 bgsave 状态

LASTSAVE          # 返回最近一次成功保存的 Unix 时间戳
INFO Persistence   # 查看 rdb 相关统计信息

bgsave 期间的保护机制

  • bgsave 执行期间,再次调用 bgsave 会被拒绝(ERR Background save already in progress
  • save 命令同样会被拒绝,避免产生竞争条件

4. 写时复制(Copy-On-Write)

写时复制(COW)是 bgsave 能够在不阻塞主进程的情况下生成一致性快照的关键机制。

4.1 fork 与内存共享

fork() 创建子进程后,父子进程共享同一块物理内存页。此时内存消耗并不会翻倍,因为页表指向相同的物理帧。

4.2 COW 触发条件

当主进程尝试修改某个共享页时:

  1. 内核检测到该页被标记为只读(共享页)
  2. 触发缺页异常(Page Fault)
  3. 内核复制该页的副本给主进程
  4. 主进程在副本上执行修改
  5. 子进程仍然读取原始页的数据
fork() 后:
  父进程页表 → 物理页 A (只读)
  子进程页表 → 物理页 A (只读)

父进程修改页 A:
  父进程页表 → 物理页 A' (可写) ← 新副本
  子进程页表 → 物理页 A  (只读) ← 原始数据

4.3 内存开销估算

COW 的额外内存开销取决于 bgsave 期间被修改的页面数量:

COW 额外内存修改页数×页大小\text{COW 额外内存} \approx \text{修改页数} \times \text{页大小}

Linux 默认页大小为 4KB。假设 bgsave 耗时 5 秒,每秒写入 10 万个 key,每个 key 平均 100 字节:

修改页数5×100000×100409612207 页\text{修改页数} \approx \frac{5 \times 100000 \times 100}{4096} \approx 12207 \text{ 页}

COW 额外内存12207×4KB48MB\text{COW 额外内存} \approx 12207 \times 4\text{KB} \approx 48\text{MB}

生产建议:预留 Redis 实例内存的 20%~50% 作为 COW 缓冲。

5. 自动触发配置

Redis 通过 save 配置项实现自动触发 RDB 快照:

# 在 redis.conf 中配置
save 900 1      # 900秒内有至少1个key被修改
save 300 10     # 300秒内有至少10个key被修改
save 60 10000   # 60秒内有至少10000个key被修改

触发逻辑:满足任意一个条件即触发 bgsave

# 禁用自动 RDB
save ""

# 运行时修改
CONFIG SET save "900 1 300 10 60 10000"

5.1 自动触发条件评估

Redis 每秒执行一次 serverCron(),检查是否满足 save 条件:

  1. 遍历所有 save 配置项
  2. 计算自上次成功保存以来的时间间隔和修改 key 数量
  3. 如果满足任一条件,触发 bgsave

6. RDB 文件格式

RDB 文件采用二进制格式,结构如下:

┌──────────┬──────────┬───────────┬──────────┬──────────┐
│  REDIS   │ version  │ databases │  EOF     │ checksum │
│  魔数    │ 版本号   │ 数据库数据 │ 结束标记 │  校验和  │
└──────────┴──────────┴───────────┴──────────┴──────────┘

各数据库区域结构:

┌─────────────┬──────────────────┬──────────────────┐
│ SELECTDB    │   key-value对    │   key-value对    │
│ 数据库编号   │  (带过期时间)     │  (无过期时间)     │
└─────────────┴──────────────────┴──────────────────┘

编码优化

  • 小整数使用变长编码(1/2/4 字节)
  • 短字符串使用嵌入编码(len + data)
  • LZF 压缩长字符串
  • 整数集合(intset)直接存储
  • 压缩列表(ziplist/listpack)直接存储

7. RDB 文件恢复

7.1 自动恢复

Redis 启动时自动检测 RDB 文件:

  1. 读取 dirdbfilename 配置
  2. 打开 RDB 文件,校验 checksum
  3. 逐个加载 key-value 到内存
  4. 加载完成后开始接受客户端请求
# 配置 RDB 文件路径
dir /var/lib/redis
dbfilename dump.rdb

7.2 RDB 文件校验

# 使用 redis-check-rdb 工具
redis-check-rdb dump.rdb

7.3 恢复注意事项

  • RDB 文件加载期间 Redis 处于阻塞状态
  • 如果 RDB 文件损坏,Redis 可能拒绝启动
  • 可以使用 redis-check-rdb --fix 尝试修复

8. 配置优化

8.1 关键配置项

# RDB 文件名
dbfilename dump.rdb

# 存储目录
dir /var/lib/redis

# 是否压缩(LZF)
rdbcompression yes

# 是否使用 CRC64 校验
rdbchecksum yes

# bgsave 失败时是否停止写入
stop-writes-on-bgsave-error yes

# 自动触发条件
save 900 1
save 300 10
save 60 10000

8.2 性能调优建议

场景save 配置说明
高可用(允许少量丢失)save 60 10000减少快照频率,降低 I/O 压力
数据安全(尽量少丢失)save 60 1增加快照频率,增加 I/O 开销
纯缓存(可丢失)save ""禁用 RDB,最大化性能
大数据集适当放宽间隔减少 fork 开销

8.3 大内存实例优化

当 Redis 实例使用内存超过 10GB 时:

  • fork() 耗时可能超过 1 秒
  • COW 可能占用数 GB 额外内存
  • 建议拆分为多个小实例,或使用 AOF 替代
  • 考虑开启 lazyfree-lazy-eviction 减少主线程阻塞

9. RDB 与 AOF 对比

特性RDBAOF
持久化方式定时快照实时追加日志
数据安全性可能丢失数分钟数据最多丢失 1 秒数据
文件体积小(压缩二进制)大(文本日志)
恢复速度
性能影响fork 开销写入开销
适用场景备份、灾难恢复数据安全优先