144 lines
5.8 KiB
Markdown
144 lines
5.8 KiB
Markdown
|
|
---
|
|||
|
|
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/多级缓存架构设计]]
|