Redis持久化
Redis持久化
Redis 的持久化机制本质是:把内存数据在某个时机“落盘”到磁盘,保证重启后数据不丢失。核心有两种方案:
- RDB(快照)
- AOF(追加日志)
以及它们的组合使用。
一、RDB(Redis DataBase 快照)
1. 核心思想
在某一时刻,把内存中的全量数据生成一个快照文件(.rdb),直接写入磁盘。
类似:数据库“拍照存档”
2. 触发方式
(1)自动触发(配置)
1 | save 900 1 # 900秒内有1次修改就触发 |
(2)手动触发
1 | SAVE # 同步阻塞(不推荐) |
3. 执行流程(重点⭐)
当执行 BGSAVE:
- Redis 主进程调用 fork()
- 创建子进程(复制当前内存数据)
- 子进程负责写 RDB 文件
- 主进程继续处理请求
利用的是:
- 写时复制(Copy-On-Write)机制
4. 优点
- 文件体积小(压缩存储)
- 恢复速度快(直接加载快照)
- 对性能影响小(子进程执行)
5. 缺点
- 数据可能丢失(最后一次快照之后的数据)
- fork 时内存开销大
- 不适合高实时性要求场景
6. 适用场景
- 数据允许少量丢失
- 更看重恢复速度
- 用于备份 / 灾难恢复
二、AOF(Append Only File)
1. 核心思想
把每一条写操作命令记录下来,按顺序追加到日志文件中。
类似:数据库“操作日志”
2. 记录内容示例
1 | SET name hansen |
3. 同步策略(fsync)⚠️关键点
AOF 的核心在于:什么时候写入磁盘
三种策略:
| 策略 | 说明 | 性能 | 安全性 |
|---|---|---|---|
| always | 每次写都刷盘 | ❌ 最慢 | ✅ 最安全 |
| everysec(默认) | 每秒刷盘 | ⚖️ 平衡 | ⚖️ |
| no | 由OS决定 | ✅ 最快 | ❌ 最不安全 |
4. 执行流程
写命令执行流程:
1 | 客户端写请求 |
5. AOF 重写(Rewrite)⭐
为什么需要?
AOF 会越来越大,比如:
1 | SET a 1 |
👉 实际只需要:
1 | SET a 3 |
重写机制:
Redis 会:
- fork 子进程
- 根据当前内存数据生成“最简命令集”
- 写入新的 AOF 文件
- 替换旧文件
6. 优点
- 数据更安全(最多丢1秒)
- 可读性强(日志)
- 支持重写压缩
7. 缺点
- 文件比 RDB 大
- 恢复速度慢(需要重放命令)
- 写入性能略低
8. 适用场景
- 对数据一致性要求高
- 不希望数据丢失
三、RDB vs AOF 对比
| 维度 | RDB | AOF |
|---|---|---|
| 数据完整性 | ❌ 可能丢数据 | ✅ 更安全 |
| 文件大小 | ✅ 小 | ❌ 大 |
| 恢复速度 | ✅ 快 | ❌ 慢 |
| 性能影响 | ✅ 小 | ❌ 稍大 |
| 可读性 | ❌ 二进制 | ✅ 文本 |
四、混合持久化(推荐)
Redis 4.0 引入:
RDB + AOF 混合模式
机制
AOF 重写时:
- 前半部分:RDB 快照
- 后半部分:增量 AOF
优势
- 启动速度快(RDB)
- 数据更安全(AOF)
- 文件更小
开启方式
1 | aof-use-rdb-preamble yes |
五、Redis 启动加载顺序
当 Redis 重启时:
- 优先加载 AOF
- 如果没有 AOF → 使用 RDB
原因:AOF 数据更完整
六、面试总结(高频模板)
可以这样回答:
Redis 提供两种持久化机制:RDB 和 AOF。
RDB 是通过生成数据快照来持久化,适合做备份,恢复快但可能丢数据;
AOF 是通过记录写命令日志来持久化,数据更安全但文件更大、恢复较慢。AOF 提供三种刷盘策略(always、everysec、no),通常使用 everysec,在性能和安全之间做平衡。
此外 Redis 4.0 引入了混合持久化,结合了 RDB 和 AOF 的优点,既保证恢复速度又提高数据安全性。
在实际生产中,一般会同时开启 RDB 和 AOF。
七、fork + Copy-On-Write 原理(🔥高频)
fork + Copy-On-Write(写时复制) = 创建子进程时不复制内存,只有发生写操作时才真正复制数据页
1. fork 原理(进程复制)
在 Linux 中,fork() 会创建一个子进程:
1 | pid_t pid = fork(); |
fork 后的特点:
-
子进程 ≈ 父进程的“副本”
-
共享:
- 代码段
- 数据段
- 堆、栈
-
逻辑上独立,物理上共享内存页(关键)
2. Copy-On-Write(写时复制)机制
核心思想
不立即复制内存,而是“延迟复制”
工作机制
fork 刚发生时:
1 | 父进程 ----\ |
- 两个进程指向同一份内存页
- 内存页被标记为:只读
当发生写操作时:
- 情况1:父进程修改数据
1 | 父进程写数据 → 触发缺页异常 |
- 情况2:子进程修改数据(少见)
同理也会复制一份
图示总结:
1 | 初始: |
关键点总结
- ✅ 读操作:共享内存
- ❗ 写操作:触发复制
- ✅ 粒度:页级(通常4KB)
3. 一句话总结(八股版)
fork 创建子进程时不会复制内存,而是通过 Copy-On-Write 共享内存页,只有在发生写操作时才复制对应页,从而大幅降低持久化的开销,但在高写入场景下可能导致内存暴涨。
八、AOF 重写期间如何保证数据一致
AOF 重写(BGREWRITEAOF)流程:
- fork 子进程
- 子进程根据当前内存生成新的 AOF 文件
- 主进程继续处理写请求
问题来了:
1 | fork 之后产生的新写操作 |
1. 解决核心:双缓冲 + 重写缓冲区
Redis 使用的是:
AOF buffer + AOF rewrite buffer(重写缓冲区)
2. 完整流程(重点⭐)
1. fork 发生
- 主进程 → 正常处理请求
- 子进程 → 开始生成新的 AOF 文件(基于旧数据)
2. 主进程处理写请求(关键)
当有新写操作:
1 | SET key value |
Redis 会做 两件事:
写入 AOF buffer(正常流程)
用于旧 AOF 文件
同时写入 rewrite buffer(关键)
用于“补偿”新 AOF 文件
3. 子进程完成重写
- 生成一个新的 AOF 文件(只包含旧数据的最简命令)
4. 最关键一步:数据合并
子进程完成后:
- 主进程把 rewrite buffer 中的数据
- 追加到 新 AOF 文件末尾
这样:
1 | 新 AOF = 重写生成的数据 + 重写期间的增量数据 |
5. 原子替换
最后:
- 用新 AOF 文件替换旧 AOF(rename 原子操作)
数据一致性保证完成
3. 流程图总结
1 | fork |
4. 关键设计点总结 ⭐
1. 为什么需要 rewrite buffer?
因为:
- 子进程基于旧数据
- 无法感知新写入
2. 为什么写两份?
| buffer | 作用 |
|---|---|
| AOF buffer | 保障旧 AOF 正常写入 |
| rewrite buffer | 保障新 AOF 不丢数据 |
5. 面试标准回答模板
在 AOF 重写期间,Redis 通过维护一个 AOF 重写缓冲区来保证数据一致性。
fork 子进程后,子进程基于当前内存生成新的 AOF 文件,而主进程继续处理写请求。
对于重写期间的新写操作,Redis 会同时写入 AOF buffer 和 AOF rewrite buffer。
当子进程完成重写后,主进程会将 rewrite buffer 中的增量数据追加到新 AOF 文件末尾,最后通过原子 rename 替换旧文件,从而保证数据不丢失且一致。
