---
tags: [Redis, 缓存, 持久化, RDB]
create time: 2026-05-15 18:12
---
# RDB 持久化
## 概述
> [!QUESTION] Redis 的数据都存在内存里,那服务器一断电,数据岂不是全没了?
> 没错——这就是**持久化**存在的意义。Redis 提供两种持久化方案:**RDB** 和 **AOF**,本文先讲 RDB。
> [!SUMMARY] 一句话理解 RDB
> RDB(Redis Database)就是在**某个时间点**,把 Redis 内存中的**全部数据**"拍一张照片"存到磁盘上——这个"照片"就是一个 `.rdb` **快照文件**。
>
> 你可以把它想象成**游戏存档**:打 Boss 之前你手动存了一次档,万一翻车了可以读档重来。但代价是——**存档之后到翻车之间**的操作全丢了。
### 核心特点
| 特性 | 说明 | 类比 |
|------|------|------|
| ⚡ 恢复极快 | 二进制文件直接加载到内存,跳过命令回放 | 像从压缩包解压,比照着菜谱重新做菜快得多 |
| 📦 文件紧凑 | 使用 LZF 压缩,远小于等量 AOF 日志 | 一张照片的信息量 > 千言万语的描述 |
| 🔧 架构简单 | 无额外复杂逻辑,一个文件搞定备份/迁移 | "复制-粘贴"级别的简单 |
| ⚠️ 数据窗口丢失 | 两次快照之间的写操作不可恢复 | 存档点之后的操作无法回溯 |
### 何时使用
- **大数据集冷备**:定期全量备份到 S3 / OSS,作为最后一道防线
- **灾难恢复**:服务器挂了?把 `.rdb` 文件丢到新机器上,秒级恢复
- **大规模数据场景**:当你的 Redis 存了 50GB 数据,AOF 回放可能要几十分钟,RDB 几秒搞定
- **主从复制**:新从节点首次全量同步时,主节点会自动触发 RDB 快照传给从节点
> [!QUESTION] 为什么 RDB 恢复比 AOF 快?
> 打个比方:RDB 就像**拍了一张照片**,加载时直接"看照片"还原场景;AOF 则像**记了一本流水账**("先加了张三,再删了李四,又改了王五……"),恢复时需要从头到尾把账本念一遍——数据量越大,"念账本"的时间就越长。
## 触发机制
### 手动触发
```bash
# 当前线程执行,阻塞主线程 ❌ 生产环境禁用!
SAVE
# 主进程 fork 子进程在后台完成(✅ 推荐)
BGSAVE
# 查看上次 BGSAVE 成功的时间戳
LASTSAVE
```
| 命令 | 行为 | 适用场景 | 风险 |
|------|------|----------|------|
| `SAVE` | 同步阻塞,直到快照完成 | 紧急调试,或确认实例无流量时 | **主线程完全卡死**——所有客户端请求排队等待 |
| `BGSAVE` | fork 子进程异步完成 | 生产环境日常备份 | fork 瞬间有极短暂的阻塞(通常毫秒级) |
> [!QUESTION] 为什么 SAVE 危险但 Redis 还要提供它?
> 因为 `SAVE` 可以在 **BGSAVE 正在执行时** 强制同步保存(BGSAVE 期间再次 BGSAVE 会被拒绝)。这是一个极端场景的"保底"手段。
### 自动触发——条件持久化
通过 `redis.conf` 配置多条 `save` 规则,Redis 用一个**"脏键计数器"(dirty counter)** 来追踪:
```conf
save 900 1 # 900 秒内至少有 1 个 key 被修改 → 触发快照
save 300 10 # 300 秒内至少有 10 个 key 被修改
save 60 10000 # 60 秒内至少有 10000 个 key 被修改
```
> [!INFO] 底层是怎么工作的?
> Redis 内部维护两个计数器:
> 1. **`dirty` 计数器**:自上次 BGSAVE 成功以来,总共被修改的 key 数量(每次写操作 +1)
> 2. **`lastsave` 时间戳**:上次成功完成 BGSAVE 的 Unix 时间戳
>
> 每隔 100ms(`serverCron` 定时任务),Redis 会检查:**当前时间 - lastsave ≥ 阈值时间 && dirty ≥ 阈值次数**,两个条件**同时满足**才会触发 BGSAVE。
>
> 所以 `save 900 1` 的意思是:"距离上次快照超过 900 秒**并且**期间有至少 1 个 key 被修改过"。如果 900 秒内没有任何写操作,即使规则匹配了也不会触发——因为没有新数据需要保存。
> [!QUESTION] 为什么要设三档而不是一档?
> 这是一个**渐进式频率**设计:
> - **低活跃度**(900s/1key):写入很少时,15 分钟存一次就够了——即使丢了 15 分钟的数据,也几乎没什么损失
> - **中活跃度**(300s/10key):中等写入压力时,5 分钟存一次,平衡了 I/O 开销和数据安全
> - **高活跃度**(60s/10000key):大量写入时,1 分钟存一次——虽然 fork 更频繁了,但此时丢 1 分钟的数据损失也更大
>
> 本质上是"**数据越重要、变更越频繁,保存就越勤**"的策略。
```bash
# 运行时动态修改(覆盖所有规则)
CONFIG SET save "900 1 300 10 60 10000"
# 临时关闭自动快照(谨慎!相当于禁用 RDB 自动触发)
CONFIG SET save ""
```
> [!WARNING] CONFIG SET 不持久化
> `CONFIG SET` 仅在内存中生效,Redis 重启后会恢复 `redis.conf` 的配置。若要永久生效,必须修改 `redis.conf` 并执行 `CONFIG REWRITE` 或手动重启。
### 其他自动触发场景
除了 `save` 规则外,以下场景也会自动触发 RDB:
| 场景 | 说明 | 为什么需要? |
|------|------|-------------|
| **正常关闭** | 执行 `SHUTDOWN` 时自动触发一次 BGSAVE,保证关闭前数据落盘 | 优雅关闭 ≠ 数据丢失 |
| **主从全量同步** | 新从节点首次连接主节点时,主节点自动触发 BGSAVE 并将快照传给从节点 | 从节点需要一份完整的数据副本 |
| **AOF 重写** | 开启混合持久化后,`BGREWRITEAOF` 会先写 RDB 快照作为基线 | 后面"混合持久化"章节详解 |
> [!TIP] Redis 如何避免"撞车"?
> Redis 同一时刻**只会有一个子进程在做持久化**。如果 BGSAVE 正在执行,此时再触发 SAVE/BGSAVE:
> - `BGSAVE` → 直接返回错误 `ERR Background save already in progress`
> - `SAVE` → 会等待 BGSAVE 完成后再执行
>
> 这个设计避免了多个子进程争抢 I/O 资源。
## RDB 文件结构
可以把 `.rdb` 文件想象成一个**快递包裹**:有快递单号(Header)、多个包裹(DB 里的数据)、包裹拆完的标记(EOF)、以及签收校验码(Footer)。
```mermaid
flowchart TD
subgraph header["1 HEADER 文件头"]
A["Magic: REDIS07 + Version"]
B["Auxiliary Fields: redis-ver, ctime, used-mem"]
C["DB Selector: FE 00(选择 DB 0)"]
A --> B --> C
end
subgraph data["2 DATA 数据段(每个非空 DB 重复一次)"]
D["Key Type: 1 byte(String=0, List=1, Set=2 ...)"]
E["Expire Info: 可选, TTL 时间戳"]
F["Encoded Key + Value: 直接序列化底层数据结构"]
D --> E --> F
F -.-> G["... 重复 N 个 Key-Value 对 ..."]
end
subgraph footer["3 FOOTER 文件尾"]
H["EOF Marker: 0xFF(数据结束)"]
I["CRC64 Checksum: 8 bytes(完整性校验)"]
H --> I
end
header --> data --> footer
```
### 各段含义详解
**1️⃣ Header(文件头)**
- **Magic 字符串**:以 `REDIS` 开头 + 4 字节版本号(如 `REDIS0009`),用于标识"这是一个 RDB 文件"
- **辅助字段(Auxiliary Fields)**:记录 Redis 版本、创建时间、内存占用等元信息——**不是数据本身,只是"快递单信息"**
- **DB Selector**:`FE` + DB 编号,告诉你"接下来的数据属于哪个 DB"(默认只有 DB 0 有数据,所以通常只出现一次)
**2️⃣ Data(数据段)**
每个 key-value 对的存储格式:
| 字段 | 大小 | 说明 |
|------|------|------|
| **Key Type** | 1 byte | 值的类型标记:String=0, List=1, Set=2, ZSet=3, Hash=4 … |
| **Expire Time** | 0 或 8 bytes | 如果 key 设置了过期时间,会附带一个毫秒级时间戳 |
| **Key** | 变长 | key 名(字符串编码) |
| **Value** | 变长 | **直接序列化底层数据结构**(ziplist、intset、skiplist 等),这是 RDB 文件紧凑的关键 |
**3️⃣ Footer(文件尾)**
- **EOF 标记**:`0xFF`,一个字节,宣告"数据到这里结束了"
- **CRC64 校验和**:8 字节,Redis 加载文件时用它校验完整性——如果校验失败,说明文件损坏,加载直接报错
> [!QUESTION] 为什么 RDB 文件比 AOF 小很多?
> 核心在于存储方式不同。举个例子:
>
> 假设你用 `HSET user:1 name "Alice" age 25 city "Beijing"` 存了一个 Hash:
> - **AOF 记录的是**:这条命令字符串本身 → `*8\r\n$5\r\nHSET\r\n$6\r\nuser:1\r\n$4\r\nname\r\n...\` 约 **120+ 字节**
> - **RDB 记录的是**:Hash 类型标记 + ziplist 编码的紧凑字节流 → 约 **30-40 字节**
>
> 差距来自:RDB 不记录"怎么做的",只记录"结果是什么"——就像一张照片远比描述照片的文字更紧凑。
## 关键配置参数
```conf
# ---- RDB 核心参数 ----
dbfilename dump.rdb # 快照文件名(建议保留默认)
dir /var/lib/redis # 工作目录——RDB/AOF 文件都存放在这里
rdbcompression yes # 是否使用 LZF 压缩字符串
rdbchecksum yes # 是否在文件末尾写入 CRC64 校验
rdb-save-incremental-fsync yes # 写入磁盘时是否增量 fsync
rdb-del-sync-files no # 无盘复制时,是否删除用于同步的临时 RDB(Redis 7+)
```
### 参数逐个解释
| 参数 | 推荐值 | 为什么? |
|------|--------|----------|
| `dbfilename` | `dump.rdb` | 保持默认即可。如果一台机器跑多个 Redis 实例,建议用端口号区分,如 `dump-6379.rdb` |
| `dir` | `/var/lib/redis` | 快照文件存放目录。**必须确保该目录有足够磁盘空间**,且 Redis 进程有写权限 |
| `rdbcompression` | `yes` | 开启 LZF 压缩可减少 40%-60% 的磁盘占用,代价是写入时多消耗一点 CPU。**绝大多数场景推荐开启** |
| `rdbchecksum` | `yes` | 开启后在文件末尾写入 CRC64 校验码,加载时会验证完整性。**关闭可略微提升加载速度,但不值得冒险** |
| `rdb-save-incremental-fsync` | `yes` | 开启后,子进程每写入 32MB 就执行一次 `fsync`,避免一次性刷盘造成 I/O 尖峰。对 SSD 尤其友好 |
> [!QUESTION] rdbcompression 关闭能提升多少性能?
> 压缩的 CPU 开销其实很小(LZF 是轻量压缩算法),瓶颈通常在磁盘 I/O 而非 CPU。关闭压缩后文件变大 → 写入时间更长 → 可能反而更慢。所以**除非你有明确的 CPU profiling 数据证明压缩是瓶颈,否则不要关**。
## Fork 过程详解
BGSAVE 的核心操作是 `fork()`——这是 Linux 的系统调用,能以**极低的成本**创建一份进程副本。整个流程如下:
```mermaid
sequenceDiagram
participant Client as 客户端
participant Main as 主进程
participant Child as 子进程
participant Disk as 磁盘
Client->>Main: 发送 BGSAVE 命令
Main->>Main: 创建子进程(fork)
Note over Main: fork 返回 0(子进程视角)
fork 返回 pid(主进程视角)
Main->>Client: 返回 OK(立即响应)
par 主进程继续服务
Main->>Main: 正常处理读写请求
and 子进程执行快照
Child->>Child: 遍历所有 key-value
Child->>Disk: 序列化写入 dump.rdb.tmp
Note over Child,Disk: 写入过程中如果主进程修改了数据
内核通过 COW 机制保护子进程视图
Child->>Main: 通知:快照完成
end
Main->>Disk: rename dump.rdb.tmp -> dump.rdb(原子替换)
Main->>Main: 更新 lastsave 时间戳, 重置 dirty 计数器
```
> [!TIP] 为什么 fork 这么快?
> `fork()` 并不会真的复制整个内存。Linux 使用**"写时复制"(Copy-On-Write, COW)**——fork 之后,父子进程**共享同一份物理内存页**,只在某一方**写入时**才真正拷贝那一页。所以 fork 一个 10GB 的 Redis 实例,实际瞬间开销只是创建页表(通常几十 MB),远小于 10GB。
### 深入理解 COW(Copy-On-Write)
> [!INFO] 图书馆类比
> 想象一个图书馆(物理内存),里面有很多书(内存页)。
> - **fork 之前**:只有主进程一个人在看书
> - **fork 之后**:子进程也进了图书馆,两人**看同一本书**,不需要额外复印——这就是"共享"
> - **主进程想在书上做笔记(写入)**:图书馆员说"不行,这本书要保护",于是**复印了一份**给主进程,子进程继续看原版——这就是"写时复制"
> - **如果主进程不写**:从头到尾只有那一本书,零额外开销
```mermaid
flowchart TD
F["Fork 完成"] --> S["父子进程共享
所有物理内存页"]
S --> W{"主进程收到
写请求?"}
W -->|"否"| R["子进程继续读取原页
(零拷贝,极快)"]
W -->|"是"| C["内核复制该内存页
(Copy-On-Write)"]
C --> N["主进程写入副本页
子进程仍读原页"]
R & N --> D["子进程完成序列化"]
D --> DONE["RDB 文件生成完成"]
```
> [!QUESTION] BGSAVE 期间 Redis 内存会翻倍吗?
> **不会翻倍,但最坏情况接近翻倍。**
>
> fork 本身**不复制任何内存页**。只有当主进程**实际写入**某一页时,那一页才会被 COW 拷贝。两种极端情况:
>
> | 场景 | 额外内存开销 |
> |------|-------------|
> | BGSAVE 期间主进程**几乎不写入** | ≈ 0(只共享,不拷贝) |
> | BGSAVE 期间主进程**全量写入** | ≈ 接近翻倍(每一页都被拷贝) |
> | 实际生产环境 | 通常 **10%-30%**(只有被修改的 key 对应的页才会 COW) |
> [!WARNING] fork 失败的常见原因
> fork 需要内核分配页表空间,如果系统内存不足会失败。关键运维参数:
> - **`vm.overcommit_memory = 1`**:告诉内核"允许过度提交",这是 Redis 官方推荐的 Linux 设置
> - **`vm.overcommit_ratio`**:默认 50,配合 overcommit_memory=2 使用
>
> 如果你看到 `Can't save in background: fork: Cannot allocate memory`,大概率是这个参数没设置对。
### TTL / 过期键处理
> [!QUESTION] RDB 快照会保存已过期的 key 吗?
> **不会。** 这是一个容易误解的点——很多人以为快照是"拍照片",会原封不动地保存内存里所有东西。但实际上,Redis 在"拍照"之前会先"打扫房间"。
具体流程:
1. `BGSAVE` 触发时,主线程**先执行一次主动过期扫描**,把所有已超时的 key 从内存中删除
2. 删除完成后,才 fork 子进程
3. 子进程遍历剩余的 key → 序列化写入 `.rdb` 文件
4. 结论:**`.rdb` 文件中只有有效数据,不包含过期键**
```mermaid
flowchart LR
A["BGSAVE 触发"] --> B["主线程: 主动过期扫描
清理所有已超时 Key"]
B --> C["Fork 子进程"]
C --> D["子进程: 遍历有效数据
序列化写入 .rdb"]
D --> E["RDB 文件中无过期键"]
style E fill:#c4e8b0
```
> [!NOTE] 一个细节:RDB 加载时也会二次检查
> 即使某个 key 在快照时还没过期,但等到恢复加载时已经过期了——Redis 在加载 RDB 文件时会**再次检查 TTL**,过期的 key 不会被加载到内存。这保证了无论什么时候恢复,数据都是"新鲜"的。
## 恢复流程
Redis 启动时会**自动检测** `dir` 目录下的 `.rdb` 文件并加载——你不需要手动执行任何"恢复命令"。
### 场景一:正常重启(最常见)
```bash
# 直接重启即可,Redis 自动加载 dump.rdb
redis-server /path/to/redis.conf
```
Redis 启动日志中你会看到类似:
```
* DB loaded from disk: 0.052 seconds # ← 52ms 加载完成
```
### 场景二:从备份恢复(灾难恢复)
当服务器故障需要从备份 `.rdb` 恢复时:
```bash
# 1. 停止当前 Redis(如果还在运行)
redis-cli SHUTDOWN NOSAVE # NOSAVE = 不再生成新快照,直接关闭
# 2. 把备份的 .rdb 文件放到 Redis 工作目录
cp /backup/dump.rdb /var/lib/redis/dump.rdb
# 3. 启动 Redis,它会自动加载这个 .rdb
redis-server /path/to/redis.conf
# 4. 验证数据恢复
redis-cli DBSIZE # 检查 key 总数
redis-cli INFO keyspace # 查看各 DB 的 key 数量和过期 key 数量
redis-cli GET some_known_key # 抽样验证已知 key 是否存在
```
### 场景三:迁移数据到新实例
```bash
# 在旧实例上手动触发一次快照
redis-cli BGSAVE
# 等待完成
redis-cli LASTSAVE # 观察时间戳变化
# 复制 .rdb 到新服务器
scp /var/lib/redis/dump.rdb user@new-server:/var/lib/redis/
# 在新服务器上启动 Redis(自动加载)
ssh user@new-server "redis-server /path/to/redis.conf"
```
> [!WARNING] 恢复时的常见坑
> 1. **文件权限**:确保 Redis 进程对 `dir` 目录有**读写权限**(启动时需要读取,运行时需要写入新快照)
> 2. **磁盘空间**:`.rdb` 文件恢复时需要临时解压到内存,确保服务器**可用内存 ≥ RDB 文件大小**
> 3. **同时存在 AOF 和 RDB**:如果 `dir` 下同时有 `dump.rdb` 和 `appendonly.aof`,**Redis 优先加载 AOF**(因为 AOF 数据通常更新)。如果你确实想用 RDB 恢复,需要先移走 AOF 文件
> 4. **版本兼容**:低版本 Redis 不能加载高版本生成的 RDB 文件(向后不兼容)
## RDB vs AOF 对比
| 维度 | RDB | AOF | 为什么? |
|------|-----|-----|----------|
| **数据安全性** | ⚠️ 可能丢失两次快照间的数据 | ✅ 最多丢 1 秒数据(everysec 策略) | RDB 是"定期拍照",AOF 是"实时写日记" |
| **恢复速度** | ⚡ 极快(直接加载二进制文件) | 🐢 较慢(逐条回放命令) | 读一本 100 页的书 vs 回放 100 万条操作日志 |
| **文件大小** | ✅ 小(压缩后的二进制) | ❌ 大(文本命令持续追加) | 照片 vs 逐帧视频——信息密度不同 |
| **CPU 开销** | 低(偶尔 fork) | 高(每条写命令都要追加) | 定期批量 vs 持续实时的天然差异 |
| **对写入性能影响** | 极小(fork 子进程异步完成) | 有影响(每次写都要 append) | BGSAVE 不阻塞主线程,AOF fsync 可能短暂阻塞 |
| **适用场景** | 备份、灾难恢复、主从同步 | 对数据安全要求高的在线服务 | 各取所长 |
> [!QUESTION] 生产环境怎么选?
> **答案是:不要二选一,两个都开。** Redis 4.0+ 引入的混合持久化(下文详解)完美解决了这个矛盾——RDB 保证恢复速度,AOF 保证数据安全。
## Redis 混合持久化(推荐)
### 为什么需要混合持久化?
回顾一下两者的痛点:
| 方案 | 优点 | 痛点 |
|------|------|------|
| RDB | 恢复快、文件小 | 两次快照间的数据可能丢失 |
| AOF | 数据几乎不丢 | 恢复慢、文件大 |
> [!QUESTION] 能不能把两者结合起来——用 RDB 加速恢复 + 用 AOF 保证数据安全?
> 可以!Redis 4.0 引入了**混合持久化(Hybrid Persistence)**,正是为了解决这个矛盾。
### 原理:AOF 文件 = RDB 快照 + 增量日志
开启混合持久化后,AOF **重写**时不再只写命令日志,而是:
```mermaid
flowchart LR
A["AOF 重写触发"] --> B["先写入一份 RDB 快照
(覆盖重写时刻的全量数据)"]
B --> C["再追加重写期间的增量命令"]
C --> D["最终 AOF 文件"]
style D fill:#c4e8b0
```
也就是说,生成的 AOF 文件结构变成了:
```
┌──────────────────────────────────────┐
│ RDB 二进制快照(全量数据基线) │ ← 启动时直接加载,秒级完成
├──────────────────────────────────────┤
│ 增量 AOF 命令(快照之后的写操作) │ ← 逐条回放,保证数据不丢
└──────────────────────────────────────┘
```
> [!INFO] 用"写日记"来类比
> - **纯 RDB** = 定期拍照(恢复快,但照片之间的事不知道)
> - **纯 AOF** = 每天写日记(信息完整,但要从头读到尾才能恢复全部记忆)
> - **混合持久化** = 定期拍照 + 每天写日记。恢复时先看最近的照片(RDB 部分),再读照片之后的日记(AOF 部分)——既快又完整
### 如何开启
```conf
# redis.conf
aof-use-rdb-preamble yes # Redis 4.0+, 默认就是 yes
appendonly yes # 必须开启 AOF
```
### 恢复速度对比
```mermaid
flowchart LR
T1["传统 AOF 恢复
回放数百万条命令"] -->|"O(N) 慢"| R1["耗时: 分钟级别"]
H1["RDB 快照加载
秒级还原全量数据"] -->|"O(1) 快"| R2["耗时: 秒级"]
H2["回放少量增量 AOF
仅重写后的命令"] -->|"极少"| R2
style R1 fill:#f9d0c4
style R2 fill:#c4e8b0
```
> [!TIP] 混合持久化几乎是"无脑开启"的配置
> - 恢复速度 ≈ RDB(因为大部分数据在 RDB 部分,AOF 增量部分很少)
> - 数据安全 ≈ AOF(增量日志保证不丢数据)
> - 开销 = AOF rewrite 的 fork 开销(和纯 AOF 一样)
>
> Redis 5.0+ 已默认开启,建议检查你的配置确认一下。
## 最佳实践
> [!SUMMARY] 核心原则:RDB 是备份手段,不是实时数据保障
> 不要把 RDB 当成"实时安全网"——它的定位是**定期快照 + 灾难恢复兜底**。实时数据安全交给 AOF。
1. **不要只依赖 RDB**:生产环境推荐 `AOF + 混合持久化`,AOF 保证数据安全,RDB 加速恢复
2. **合理设置 save 频率**:默认三档适合大部分场景。写入密集型可适当提高频率,但要注意:
- 频率越高 → fork 越频繁 → CPU 和内存压力越大
- 经验法则:`rdb_last_bgsave_time_sec` 超过 **1 秒**就需要关注
3. **备份策略(自动化)**:
```bash
#!/bin/bash
# redis-backup.sh — 每日定时备份脚本示例
REDIS_CLI="redis-cli"
BACKUP_DIR="/backup/redis"
DATE=$(date +%Y%m%d_%H%M%S)
# 1. 触发一次快照
$REDIS_CLI BGSAVE
# 2. 等待完成(轮询 lastsave)
LAST=$($REDIS_CLI LASTSAVE)
while [ "$($REDIS_CLI LASTSAVE)" == "$LAST" ]; do sleep 1; done
# 3. 拷贝并压缩
cp /var/lib/redis/dump.rdb "$BACKUP_DIR/dump-$DATE.rdb"
gzip "$BACKUP_DIR/dump-$DATE.rdb"
# 4. 保留最近 7 天的备份,删除更早的
find "$BACKUP_DIR" -name "dump-*.rdb.gz" -mtime +7 -delete
echo "Backup completed: dump-$DATE.rdb.gz"
```
4. **监控关键指标**:通过 `INFO persistence` 关注以下字段:
| 指标 | 含义 | 告警阈值建议 |
|------|------|-------------|
| `rdb_last_bgsave_status` | 上次 BGSAVE 是否成功 | ≠ `ok` 立即告警 |
| `rdb_last_bgsave_time_sec` | 上次 BGSAVE 耗时 | > 1s 关注,> 5s 告警 |
| `rdb_last_save_time` | 上次成功快照的时间戳 | 距今 > 1h 未触发则排查 |
| `rdb_changes_since_last_save` | 上次快照后的变更 key 数 | 持续增长说明 save 规则可能失效 |
5. **磁盘选择**:RDB 写入对 I/O 有要求,建议使用 SSD 并保持 `rdb-save-incremental-fsync yes`
> [!WARNING] 大 Key 是 RDB 的天敌
> 一个 100MB 的 String key,序列化时子进程写入耗时剧增(单个 key 可能就要写几秒),同时 COW 也会产生大量页面复制。
>
> **排查方法**:`redis-cli --bigkeys` 扫描大 key,发现后优先拆分。详见 [[hhs/Redis/11-运维与性能调优]]。
## 运维速查手册
```bash
# ---- 查看 RDB 状态 ----
INFO persistence
# 重点关注这些字段:
# rdb_last_bgsave_status: ok ← 上次是否成功
# rdb_last_bgsave_time_sec: 2 ← 上次耗时几秒
# rdb_last_save_time: 1715000000 ← 上次成功时间戳(Unix)
# rdb_changes_since_last_save: 1234 ← 自上次快照后的变更 key 数
# ---- 查看快照文件 ----
ls -lh /var/lib/redis/dump.rdb
# -rw-rw---- 1 redis redis 1.2G May 22 03:00 dump.rdb
# ---- 手动触发一次快照并等待完成 ----
redis-cli BGSAVE
# 等待:轮询 LASTSAVE 直到时间戳变化
while [ "$(redis-cli LASTSAVE)" == "$OLD" ]; do sleep 0.5; done
# ---- 检查 RDB 文件是否完整(不启动 Redis 的情况下) ----
# Redis 自带工具,用于检查 .rdb 文件的完整性
redis-check-rdb /var/lib/redis/dump.rdb
# 输出 OK 则文件完整,否则会告诉你哪里损坏了
```
## 关联笔记
- [[hhs/Redis/05-AOF持久化]] — AOF 持久化机制
- [[hhs/Redis/06-主从与哨兵]] — Sentinel 故障转移流程
- [[hhs/Redis/11-运维与性能调优]] — 备份策略与大 Key 治理