Files
autumn-recruitment/03.Redis/core/RDB与AOF持久化.md
T

144 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [redis, rdb-aof, persistence, fork, snapshot]
create time: 2026-08-08 18:43
update time: 2026-08-08 18:43
---
# RDB 与 AOF 持久化
## 概述
Redis 作为内存数据库,崩溃重启后需要可靠的数据恢复机制。RDB(快照)和 AOF(追加日志)是 Redis 提供的两种持久化方案,各有优劣。Redis 4.0 之后又引入了混合持久化(Hybrid Persistence),结合了两者之长。理解它们的机制和取舍,是在生产环境选型的基础。
## 核心原理
### RDB — 快照机制
RDB(Redis Database)在指定时间间隔内将内存中的数据集写入磁盘,生成一个压缩的二进制文件(dump.rdb)。
**fork 子进程快照流程:**
```mermaid
sequenceDiagram
participant Client as 客户端
participant Master as Redis主进程
participant Child as fork子进程
participant Disk as 磁盘
Client->>Master: SAVE / BGSAVE
Master->>Master: 执行 bgsave
Master->>Master: fork 创建子进程
Note over Master: 父进程继续处理请求
Master->>Child: 子进程独占写内存
Child->>Child: 遍历内存数据结构
Child->>Child: 写入临时 RDB 文件
Child->>Disk: mv 替换 dump.rdb
Child-->>Master: SIGCHLD 通知完成
```
关键细节:
- **COW(Copy-On-Write)**:fork 时父子进程共享内存页,子进程读数据,父进程写数据。当某页被修改时操作系统复制该页给子进程使用。这意味着 RDB 过程中内存峰值 = 当前内存 + fork 瞬间的增量变化量。
- **save vs bgsave**:`SAVE` 阻塞所有客户端等待完成(生产环境禁用);`BGSAVE` 后台异步执行。
- **自动触发**:通过 `save <seconds> <changes>` 配置自动触发(如 `save 900 1` 表示 900 秒内有至少 1 个 key 被修改则触发)。
- **RDB 压缩**:RDB 文件本身是无损压缩的二进制格式,不同版本间不兼容(不能跨版本还原)。
> [!WARNING]
> RDB 每次全量快照,两次快照之间的数据在崩溃时会丢失。不适合对数据完整性要求极高的场景。
### AOF — 追加日志
AOF(Append Only File)以日志形式记录每个写操作,重启时重放日志恢复数据。
**appendfsync 三种策略:**
| 策略 | fsync 频率 | 性能 | 数据安全性 |
|------|-----------|------|----------|
| always | 每条命令一次 | 最差(约等于MySQL innodb_flush_log_at_trx_commit=1) | 最高,零丢失 |
| everysec(默认) | 每秒一次 | 良好(1ms 系统调用) | 丢 1 秒数据 |
| no | OS 决定 | 最佳 | 完全取决于 OS 刷新节奏 |
**AOF 重写(Rewrite)原理:**
AOF 文件会随时间越来越大。重写不是简单地删除旧文件再写新内容,而是:
1. 父进程 fork 子进程
2. 子进程遍历内存数据,将每个 key 用 `SET key value` 命令序列化(而非读取旧 AOF)
3. 父进程同时将新写命令写入 `aof_rewrite_buffer`
4. 子进程完成后将新数据和缓冲区合并写入临时文件
5. 原子替换原 AOF 文件
> [!NOTE]
> AOF 重写期间,旧 AOF 保持不变直到新文件准备好,保证不会丢失任何已确认的数据。重写后的 AOF 可能比原来的小很多(去除了已过期 key 的旧记录)。
### 混合持久化
Redis 4.0+ 引入的混合持久化 = RDB 全量快照 + AOF 增量日志:
```mermaid
graph LR
A[BGSTART] --> B[fork 子进程]
B --> C["写 RDB 部分<br/>(全部内存快照)"]
B --> D["父进程收集<br/>AOF 增量命令"]
C --> E["合并为临时文件"]
D --> E
E --> F["原子替换 AOF 文件"]
```
优势:
- 启动速度远快于纯 AOF(RDB 部分只需加载一次全量快照)
- 数据安全性接近 pure AOF everysec(RDB 部分保证了到 fork 时刻的完整状态)
### 选型对比
| 维度 | RDB | AOF(everysec) | 混合持久化 |
|------|-----|----------------|----------|
| 数据丢失风险 | 高(丢最近快照期间的数据) | 低(最多丢 1s) | 极低 |
| 启动恢复速度 | 快(单个文件) | 慢(逐条重放) | 较快 |
| 磁盘占用 | 小(压缩二进制) | 大(文本指令) | 中等 |
| CPU 开销 | 高(定期全量拷贝) | 低(持续追加) | 中 |
| 适用场景 | 容灾备份、可接受短暂丢数据 | 对数据一致性要求高 | 兼顾速度与安全的推荐方案 |
## 代码示例
Go 中使用 Redigo 触发和管理持久化:
```go
conn, _ := redis.Dial("tcp", "localhost:6379")
defer conn.Close()
// 手动触发 BGSAVE(生产环境通常交给监控定时触发)
res, _ := redis.String(conn.Do("BGSAVE"))
// res == "Background saving started"
// 查看上次 BGSAVE 的状态
info, _ := redis.StringMap(conn.Do("INFO", "persistent"))
fmt.Println(info["last_bgsave_status"]) // "ok" or "err"
```
```bash
# Redis CLI 检查 AOF 状态
127.0.0.1:6379> CONFIG GET appendonly
1) "appendonly"
2) "yes" # yes=开启, no=关闭
# 动态开启 AOF 重写
127.0.0.1:6379> CONFIG SET auto-aof-rewrite-percentage 100
OK
```
## 实践场景
1. **默认推荐**:开启混合持久化(`aof-use-rdb-preamble yes`),既保证数据安全又控制启动时间。大多数互联网公司的标准配置。
2. **冷备策略**:每天凌晨做一次 `SAVE`(或 `BGSAVE`),将 dump.rdb 上传到 OSS/S3。注意 `SAVE` 是阻塞命令,不要在高峰期执行。
3. **迁移场景**:从一个 Redis 集群迁到另一个,用 `BGSAVE` 拿到最新快照后复制文件到新实例,恢复速度远超 AOF 重放。
4. **大数据量陷阱**:当内存超过 10GB 时,fork 导致 COW 成本剧增。考虑增大 AOF 重写阈值或将业务拆分到多实例,避免单次 fork 过大的内存开销。
## 关联笔记
- [[03.Redis/core/Redis 五大核心数据结构]]
- [[03.Redis/core/集群与哨兵机制]]
- [[03.Redis/strategies/多级缓存架构设计]]