--- 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
1 byte"] --> D2["Key Type
1 byte"] D2 --> D3["Encoded Key + Value"] E1["EOF Marker"] --> E2["CRC64 Checksum
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: 子进程独享父进程
内存副本(写时复制 COW) Child->>Disk: 遍历所有 key → 序列化 Note over Child: 主进程仍可读写
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["主线程: 主动过期
清理超时 Key"] B --> C["Fork 子进程"] C --> D["子进程: 遍历剩余数据
序列化到 .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 重建
逐条命令回放"] -->|"O(N) 慢"| R1["重建耗时: 长"] H1["RDB 二进制快照
全量快速还原"] -->|"秒级加载"| R2["重建耗时: 短"] H2["AOF 增量日志
仅保留变更后操作"] -->|"精确到毫秒"| 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 治理