This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/Redis/04-RDB持久化.md
T

229 lines
7.2 KiB
Markdown
Raw Normal View History

2026-05-17 00:06:11 +08:00
---
tags: [Redis, 缓存, 持久化, RDB]
create time: 2026-05-15 18:12
---
# RDB 持久化
## 概述
> [!SUMMARY] RDB 是什么?
> RDB(Redis Database)是 Redis 最早的持久化机制,通过在特定时间点将内存中的数据集写入磁盘形成**快照文件(.rdb)**,实现数据持久化。
### 核心特点
| 特性 | 说明 |
|------|------|
| ⚡ 恢复极快 | 二进制文件直接加载,避免命令回放 |
| 📦 文件紧凑 | 压缩存储,远小于等量 AOF 日志 |
| 🔧 架构简单 | 无额外复杂逻辑,适合备份和迁移 |
| ⚠️ 数据窗口丢失 | 两次快照之间的写操作不可恢复 |
### 何时使用
- **大数据集冷备**:定期全量备份到 S3 / OSS
- **灾难恢复**:配合 CI/CD 快速重建实例
- **大规模数据场景**:恢复速度优先于毫秒级数据一致性
> [!QUESTION] 为什么 RDB 恢复比 AOF 快?
> RDB 是直接加载二进制快照到内存,AOF 则需要逐条回放命令——数据量越大差距越明显。
## 触发机制
### 手动触发
```bash
# 当前线程执行,阻塞主线程 ❌
SAVE
# 主进程 fork 子进程在后台完成(推荐)
BGSAVE
# 查看上次 BGSAVE 状态
LASTSAVE
```
| 命令 | 行为 | 影响 |
|------|------|------|
| `SAVE` | 同步保存,主线程阻塞 | **生产环境禁用!** |
| `BGSAVE` | fork 子进程异步保存 | 主线程继续服务,但触发 fork 瞬间短暂停顿 |
| `BGREWRITEAOF` | AOF rewrite | 与 BGSAVE 可同时运行(不同子进程) |
### 自动触发——条件持久化
通过 `redis.conf` 配置:
```conf
save 900 1 # 900 秒内至少有 1 个 key 被修改 → 触发快照
save 300 10 # 300 秒内至少有 10 个 key 被修改
save 60 10000 # 60 秒内至少有 10000 个 key 被修改
```
> [!QUESTION] 为什么要三档条件?
> - **低活跃度**:900s/1key 保证最终一致
> - **中活跃度**:300s/10key 平衡频率和数据量
> - **高活跃度**:60s/10000key 高频大量变更时快速落盘
```bash
# 运行时动态修改
CONFIG SET save "900 1\n300 10\n60 10000"
# 临时关闭自动快照(谨慎!)
CONFIG SET save ""
```
> [!WARNING] CONFIG SET 不持久
> `CONFIG SET` 仅在内存中生效,重启后失效。修改 `redis.conf` 并 reload 才是持久化的方式。
### 其他触发场景
| 场景 | 说明 |
|------|------|
| **shutdown 保存** | `SHUTDOWN` / `SHUTDOWN NOSAVE` 会在关闭前执行一次完整 RDB 保存(除非显式传 NOSAVE) |
| **AOF rewrite** | `BGREWRITEAOF` 重写 AOF 时同样会 fork 子进程生成 RDB preamble(开启混合持久化时) |
| **主从同步** | 新从节点首次全量同步时,主节点会自动触发 BGSAVE 并将快照传给从节点 |
> [!TIP] 避免频繁触发
> 多个触发条件同时满足时,Redis 会去重,仅执行一次 BGSAVE。若已有 BGSAVE 在执行,后续的 SAVE 请求将被忽略。
## RDB 文件结构
Redis 7.0+ 引入了新格式,更紧凑且支持增量复制:
```mermaid
flowchart TD
H1["Header"] --> H2["Magic: REDIS"]
H1 --> H3["RDB Version"]
H1 --> H4["Reserved / CRC64"]
D1["DB Number<br/>1 byte"] --> D2["Key Type<br/>1 byte"]
D2 --> D3["Encoded Key + Value"]
E1["EOF Marker"] --> E2["CRC64 Checksum<br/>8 bytes"]
H1 & H2 & H3 & H4 ==> "Dataset Section"
D1 & D2 & D3 ==> "Dataset Section"
D3 -.-> "... N keys ..."
E1 & E2 ==> "Footer"
```
## Fork 过程详解
```mermaid
sequenceDiagram
participant Server as Redis Server
participant Parent as 主进程
child ForkGroup as Forked Child
participant Child as 写时复制子进程
participant Disk as 磁盘 (.rdb)
end
Server->>Parent: BGSAVE 指令
Parent->>Server: OK(立即返回)
Parent->>Child: fork() 创建子进程
Note over Child: 子进程独享父进程<br/>内存副本(写时复制 COW)
Child->>Disk: 遍历所有 key → 序列化
Note over Child: 主进程仍可读写<br/>COW 机制按需拷贝脏页
Child->>Parent: 生成临时 .rdb.tmp
Parent->>Disk: RENAME 原子替换
Parent->>Server: BGSAVE 完成通知
```
> [!WARNING] COW 内存消耗
> fork 后主进程若持续写入大值,会导致 COW 副本膨胀。例如你有一个 1GB 的值被频繁修改,fork 后每次写都可能多拷贝一页(4KB)。高峰期建议关闭自动 RDB,仅依赖 AOF。
### TTL / 过期键处理
> [!QUESTION] RDB 快照会保存已过期的 key 吗?
> **不会。** Redis 在 fork 子进程前会先执行一次主动过期扫描,清理掉所有已超时的 key。
具体流程:
1. `BGSAVE` 触发时,主线程先将当前所有已过期 key 从内存中删除
2. 子进程 fork 完成后遍历剩余 key → 序列化到 .rdb 文件
3. 因此:**RDB 文件中不会出现已过期键**
```mermaid
flowchart LR
A["BGSAVE 触发"] --> B["主线程: 主动过期<br/>清理超时 Key"]
B --> C["Fork 子进程"]
C --> D["子进程: 遍历剩余数据<br/>序列化到 .rdb"]
D --> E[".rdb 无过期键"]
style E fill:"#c4e8b0"
```
> [!NOTE] 过期策略差异
> 虽然 RDB 不保存过期键,但 AOF 可能包含过期命令(如之前的 SET k v EX 100)。AOF 重写时会主动过滤过期键,恢复时也只会加载有效数据。两者优先保证启动时的数据一致性。
## 恢复流程
```bash
# 1. 停止 Redis
redis-cli SHUTDOWN NOSAVE
# 2. 把 .rdb 放到 redis_dir(由 dir 配置决定)
cp dump.rdb /var/lib/redis/
# 3. 启动 Redis
redis-server --daemonize yes
# 4. 验证数据恢复
redis-cli DBSIZE
redis-cli KEYS "*"
```
## RDB vs AOF 对比
| 维度 | RDB | AOF |
|------|-----|-----|
| 数据安全性 | 两次快照间数据丢失 | 每秒/每次可配置,接近零丢失 |
| 恢复速度 | ⚡ 极快(直接加载二进制文件) | 🐢 较慢(需要回放日志) |
| 文件大小 | ✅ 小(紧凑压缩) | ❌ 大(文本命令日志) |
| CPU 开销 | 低(偶尔 fork) | 高(持续追加写入) |
| 适用场景 | 备份、灾难恢复、大数据集冷备 | 对可用性要求高的在线服务 |
| 重启耗时 | 秒级 | 取决于日志长度(可 pre-fork) |
## Redis 7 混合持久化
结合 RDB + AOF 的优点:
```conf
aof-use-rdb-preamble yes
```
开启后,AOF rewrite 不再只追加命令日志,而是先写入一份 RDB 快照,再追加后续命令:
```mermaid
flowchart LR
T1["传统 AOF 重建<br/>逐条命令回放"] -->|"O(N) 慢"| R1["重建耗时: 长"]
H1["RDB 二进制快照<br/>全量快速还原"] -->|"秒级加载"| R2["重建耗时: 短"]
H2["AOF 增量日志<br/>仅保留变更后操作"] -->|"精确到毫秒"| R2
style R1 fill:"#f9d0c4"
style R2 fill:"#c4e8b0"
```
> [!TIP] 混合持久化几乎是无脑开启的配置——启动快 + 不丢数据。
## 运维要点
```bash
# 监控 RDB 相关指标
INFO persistence
# rdb_last_bgsave_status: OK
# rdb_last_save_time: 1715000000 ← 最后成功时间戳
# 检查文件大小
ls -lh /var/lib/redis/dump.rdb
# 压缩备份(已内置 LZ4 压缩,通常不需要额外压缩)
tar czf redis-backup.tar.gz dump.rdb
```
## 关联笔记
- [[hhs/Redis/05-AOF持久化]] — AOF 持久化机制
- [[hhs/Redis/06-主从与哨兵]] — Sentinel 故障转移流程
- [[hhs/Redis/09-运维调优]] — 备份策略与大 Key 治理