作为后端开发,我们每天都在和 Redis 打交道。它凭借内存级别的读写性能,成为了高并发系统的标配。但 Redis 的一切优势,都建立在一个前提之上:数据不能丢。
如果 Redis 服务器突然断电或宕机,内存中的所有数据都会消失。对于电商订单、用户会话、支付记录这些核心数据来说,数据丢失意味着灾难性的后果。为了解决这个问题,Redis 设计了一套完整的持久化机制,将内存中的数据保存到磁盘上,保证数据的持久性。
面试时,Redis 持久化更是 100% 的必考题:
- RDB 和 AOF 有什么区别?各自的优缺点是什么?
bgsave命令的执行流程是什么?为什么不会阻塞主进程?- AOF 的三种刷盘策略有什么区别?线上该怎么选?
- 什么是 AOF 重写?为什么需要重写?
- 什么是混合持久化?为什么说它是现在的最佳实践?
很多人能背出 RDB 和 AOF 的名字,却搞不清它们的底层执行流程;知道混合持久化好,却不知道它好在哪里。这篇文章,我们就从问题本质、底层原理、执行流程、源码级细节、线上最佳实践五个维度,彻底搞懂 Redis 持久化。
一、为什么需要持久化?Redis 的天生缺陷
Redis 是一个基于内存的数据库,所有的数据都存储在内存中。这是它读写性能极高的根本原因,但也带来了一个致命的缺陷:数据易失性。
当服务器断电、重启或进程崩溃时,内存中的所有数据都会被清空。如果没有持久化机制,Redis 就只能作为一个纯缓存使用,不能存储任何需要持久保存的数据。
持久化的本质,就是将内存中的数据异步保存到磁盘上。当 Redis 重启时,可以从磁盘上的持久化文件中恢复数据,从而保证数据不丢失。
Redis 提供了三种独立的持久化方式:
- RDB(Redis Database):全量快照持久化,保存某个时间点的完整数据快照
- AOF(Append Only File):增量日志持久化,保存所有的写操作命令
- 混合持久化:Redis 4.0 引入,结合了 RDB 和 AOF 的优点,是目前的最佳实践
下面我们逐个深入讲解每一种持久化方式的底层原理。
二、RDB 持久化:全量数据快照
RDB 是 Redis 最原始的持久化方式,也是默认开启的持久化方式。它的核心思想是:在某个时间点,将 Redis 内存中的所有数据生成一个二进制快照文件,保存到磁盘上。这个文件默认名为dump.rdb。
1. 触发方式
RDB 的触发分为自动触发和手动触发两种。
自动触发
在 Redis 配置文件redis.conf中,可以通过save参数配置自动触发 RDB 的条件:
# 格式:save <秒数> <修改的key数量> # 表示在指定秒数内,有指定数量的key发生了修改,就自动触发RDB快照 save 900 1 # 900秒(15分钟)内有至少1个key被修改,触发RDB save 300 10 # 300秒(5分钟)内有至少10个key被修改,触发RDB save 60 10000 # 60秒(1分钟)内有至少10000个key被修改,触发RDB只要满足其中任意一个条件,Redis 就会自动执行 RDB 快照。
手动触发
有两个命令可以手动触发 RDB:
save:同步执行RDB 快照。执行过程中会阻塞 Redis 的所有请求,直到快照完成。线上绝对禁止使用,会导致服务不可用。bgsave:异步执行RDB 快照。这是手动触发 RDB 的推荐方式,也是自动触发时 Redis 内部使用的方式。
2.bgsave完整执行流程(面试必问)
bgsave的执行流程是 RDB 的核心,也是面试中最常被问到的知识点。很多人以为bgsave就是开一个子线程去写文件,其实远没有这么简单。
完整的bgsave执行流程分为 7 步:
- 客户端执行
bgsave命令 - Redis 主进程检查是否有正在执行的 RDB 或 AOF 重写子进程,如果有,直接返回错误
- 主进程调用
fork()系统调用,创建一个子进程。fork 过程中主进程会完全阻塞,不能处理任何请求 - fork 成功后,主进程立即恢复服务,继续处理客户端请求
- 子进程遍历内存中的所有数据,生成 RDB 快照文件,写入临时文件
- 子进程完成快照生成后,用临时文件原子替换旧的 RDB 文件
- 子进程退出,向主进程发送信号,通知主进程 RDB 执行完成
3. 核心原理:写时复制(COW)
很多人会问:子进程在生成 RDB 快照的过程中,主进程还在修改数据,怎么保证快照的一致性?
答案就是写时复制(Copy-On-Write,COW)机制。这是操作系统提供的一种进程间内存共享机制。
当主进程 fork 出子进程后,父子进程会共享同一块物理内存。此时,操作系统会将内存页的权限设置为只读。
- 如果主进程只是读取数据,那么父子进程共享同一块内存,不需要任何复制
- 如果主进程要修改某一块内存页的数据,操作系统会先复制一份该内存页的副本,然后主进程修改副本,子进程仍然使用原来的内存页
这样,子进程看到的内存数据,就是 fork 瞬间的完整快照,不会受到主进程后续修改的影响。这就是为什么bgsave可以在不阻塞主进程的情况下,生成一致性的快照。
4. RDB 的优缺点
优点
- 恢复速度极快:RDB 是二进制压缩文件,加载速度比 AOF 快几个数量级,非常适合灾难恢复
- 文件体积小:RDB 是压缩后的二进制格式,占用的磁盘空间远小于 AOF 文件
- 对性能影响小:
bgsave是异步执行的,主进程只在 fork 时短暂阻塞,不影响正常服务 - 备份方便:RDB 文件是一个单一的二进制文件,非常适合定期备份和异地容灾
缺点
- 数据丢失风险大:RDB 是全量快照,两次快照之间的所有数据都会丢失。如果每 5 分钟生成一次快照,那么最多会丢失 5 分钟的数据
- fork 阻塞问题:当 Redis 内存很大时(比如几十 GB),fork 子进程的时间会很长,可能会导致 Redis 毫秒级甚至秒级的阻塞,这对于高并发系统来说是不可接受的
- 不适合实时持久化:频繁生成 RDB 会消耗大量 CPU 和磁盘 IO 资源,影响系统性能
三、AOF 持久化:增量写日志
为了解决 RDB 数据丢失风险大的问题,Redis 引入了 AOF 持久化。AOF 的核心思想是:记录 Redis 所有的写操作命令,以追加的方式写入到磁盘文件中。当 Redis 重启时,通过重新执行 AOF 文件中的所有命令,来恢复数据。
1. 开启与配置
AOF 默认是关闭的,需要在配置文件中手动开启:
# 开启AOF持久化 appendonly yes # AOF文件名 appendfilename "appendonly.aof"2. 核心机制:三种刷盘策略
AOF 的核心是将写命令写入磁盘,但为了性能,操作系统会将写入操作先放到内存缓冲区中,然后再异步刷到磁盘。Redis 提供了三种刷盘策略,通过appendfsync参数配置,这是 AOF 最重要的配置。
| 刷盘策略 | 含义 | 数据安全性 | 性能 | 推荐指数 |
|---|---|---|---|---|
always | 每个写命令执行完成后,立即调用fsync()将数据刷到磁盘 | 最高,最多丢失 1 条命令 | 最低,IO 压力极大 | ⭐ |
everysec | 每秒调用一次fsync(),将缓冲区中的数据刷到磁盘 | 较高,最多丢失 1 秒的数据 | 较高 | ⭐⭐⭐⭐⭐ |
no | 不主动调用fsync(),由操作系统决定什么时候刷盘 | 最低,可能丢失大量数据 | 最高 | ⭐⭐ |
线上推荐使用默认的everysec策略,这是性能和数据安全性的最佳平衡。在绝大多数场景下,丢失 1 秒的数据是可以接受的。
3. AOF 重写机制
随着 Redis 的运行,AOF 文件会越来越大。比如对同一个 key 执行了 100 次set操作,AOF 文件中会记录这 100 条命令,但实际上只需要最后一条命令就能恢复数据。
为了解决 AOF 文件膨胀的问题,Redis 提供了AOF 重写机制。AOF 重写的核心思想是:生成一个新的 AOF 文件,只保留能够恢复当前数据的最小命令集合。
重写的触发方式
- 自动触发:通过两个配置参数控制
# 当AOF文件大小比上一次重写后的大小增长了100%时,自动触发重写 auto-aof-rewrite-percentage 100 # 当AOF文件大小超过64MB时,才会考虑自动重写 auto-aof-rewrite-min-size 64mb - 手动触发:执行
bgrewriteaof命令,异步执行 AOF 重写
AOF 重写完整流程
AOF 重写的流程和bgsave非常相似,也是基于 fork 和写时复制机制:
- 客户端执行
bgrewriteaof命令 - 主进程检查是否有正在执行的 RDB 或 AOF 重写子进程,如果有,直接返回
- 主进程 fork 出一个子进程,fork 过程中主进程阻塞
- fork 成功后,主进程恢复服务,同时将新的写命令同时写入两个缓冲区:
- AOF 缓冲区:用于写入旧的 AOF 文件,保证原有 AOF 的正常工作
- AOF 重写缓冲区:用于记录重写期间的新写命令
- 子进程遍历内存中的所有数据,生成最小命令集合,写入新的 AOF 临时文件
- 子进程完成重写后,通知主进程
- 主进程将 AOF 重写缓冲区中的所有命令追加到新的 AOF 临时文件末尾
- 主进程用新的 AOF 临时文件原子替换旧的 AOF 文件
- 重写完成,后续的写命令继续写入新的 AOF 文件
4. AOF 的优缺点
优点
- 数据安全性高:使用
everysec策略最多丢失 1 秒的数据,远高于 RDB 的安全性 - 文件可读性好:AOF 是纯文本文件,记录了所有的写命令,可以直接查看和修改,方便数据恢复
- 适合实时持久化:AOF 是增量写入,对性能影响小,适合需要实时持久化的场景
- 自动重写:AOF 重写机制可以自动压缩文件体积,避免文件无限膨胀
缺点
- 恢复速度慢:AOF 是文本文件,重启时需要重新执行所有命令,当 AOF 文件很大时,恢复时间会非常长
- 文件体积大:即使经过重写,AOF 文件的体积仍然比 RDB 文件大得多
- 对性能影响比 RDB 大:虽然
everysec策略性能已经很好,但还是比 RDB 略差 - 重写阻塞问题:AOF 重写时也需要 fork 子进程,同样会有短暂的阻塞问题
四、混合持久化:RDB+AOF 的完美结合
RDB 恢复快但数据丢失多,AOF 数据安全但恢复慢。为了结合两者的优点,Redis 4.0 引入了混合持久化。这是目前 Redis 官方推荐的持久化方式,也是绝大多数线上环境的标准配置。
1. 核心原理
混合持久化的核心思想是:AOF 重写时,将当前内存中的数据以 RDB 格式写入 AOF 文件开头,然后将重写期间的写命令以 AOF 格式追加到文件末尾。
这样,新的 AOF 文件就由两部分组成:
- 前半部分:RDB 格式的全量数据快照,体积小,加载速度快
- 后半部分:AOF 格式的增量写命令,数据安全性高
2. 开启与配置
混合持久化默认是开启的,需要在配置文件中配置:
# 开启AOF持久化 appendonly yes # 开启混合持久化 aof-use-rdb-preamble yes3. 执行流程
混合持久化的执行流程和普通 AOF 重写几乎完全一样,唯一的区别是子进程生成新文件的格式:
- 触发 AOF 重写(自动或手动)
- 主进程 fork 出子进程
- 子进程将当前内存中的数据以 RDB 格式写入新的 AOF 临时文件
- 主进程将重写期间的新写命令写入 AOF 重写缓冲区
- 子进程完成 RDB 部分的写入后,通知主进程
- 主进程将重写缓冲区中的命令以 AOF 格式追加到新的 AOF 文件末尾
- 原子替换旧的 AOF 文件,重写完成
4. 混合持久化的优势
混合持久化完美结合了 RDB 和 AOF 的优点:
- 恢复速度极快:前半部分是 RDB 格式,加载速度和纯 RDB 一样快
- 数据安全性高:后半部分是 AOF 格式,最多丢失 1 秒的数据
- 文件体积小:结合了 RDB 的压缩特性,文件体积比纯 AOF 小很多
- 兼容性好:向下兼容纯 AOF 格式,Redis 可以识别并加载混合持久化的 AOF 文件
五、三种持久化方式对比与选择
为了方便大家快速对比和选择,我整理了一张核心对比表:
| 特性 | RDB | AOF | 混合持久化 |
|---|---|---|---|
| 数据安全性 | 低,丢失两次快照之间的数据 | 高,最多丢失 1 秒数据 | 高,最多丢失 1 秒数据 |
| 恢复速度 | 极快 | 慢 | 极快 |
| 文件体积 | 小 | 大 | 中等 |
| 对性能影响 | 小 | 中等 | 中等 |
| 实现复杂度 | 低 | 中等 | 中等 |
| 推荐版本 | 所有版本 | 所有版本 | Redis 4.0+ |
| 适用场景 | 备份、灾难恢复、非核心数据 | 核心数据、实时持久化 | 绝大多数线上场景 |
不同场景的选择建议
- Redis 4.0 及以上版本:优先使用混合持久化,这是目前的最佳实践
- Redis 3.2 及以下版本:如果对数据安全性要求高,使用 AOF;如果可以接受一定的数据丢失,使用 RDB
- 纯缓存场景:如果可以接受数据丢失,并且重启后可以从数据库重建缓存,可以关闭持久化
- 主从架构:建议主库关闭持久化,从库开启持久化,避免主库 fork 阻塞影响线上服务
六、线上最佳实践与避坑指南
1. 永远不要使用save命令
save命令会同步执行 RDB 快照,阻塞整个 Redis 进程,直到快照完成。线上环境绝对禁止使用,所有手动触发都应该使用bgsave和bgrewriteaof。
2. 合理配置 RDB 自动触发条件
不要配置过于频繁的 RDB 快照,否则会导致频繁 fork,影响 Redis 性能。建议根据业务的数据丢失容忍度,配置合理的触发条件,比如:
# 关闭默认的自动触发 save "" # 只保留每天凌晨3点自动生成一次RDB save 86400 13. 控制 Redis 的最大内存
Redis 的内存越大,fork 子进程的时间就越长,阻塞时间也就越长。建议单个 Redis 实例的内存不要超过 16GB,最好控制在 8GB 以内。如果数据量太大,使用 Redis Cluster 分片集群。
4. 主从库持久化分工
主库只负责处理业务请求,不要开启任何持久化。所有的持久化操作都放到从库上执行,这样即使从库 fork 阻塞,也不会影响线上服务。
5. 监控持久化状态
一定要建立完善的监控体系,监控以下指标:
- RDB 和 AOF 的执行时间、执行频率
- RDB 和 AOF 文件的大小、增长速度
- fork 操作的耗时
- 持久化失败的次数
如果发现 fork 耗时过长,或者持久化执行过于频繁,及时调整配置或扩容。
6. 定期备份持久化文件
持久化文件保存在本地磁盘上,如果磁盘损坏,数据还是会丢失。一定要定期将持久化文件备份到异地存储,比如对象存储服务。
七、常见误区纠正
误区:
bgsave完全不会阻塞主进程。纠正:bgsave在 fork 子进程的时候会阻塞主进程,只是阻塞时间很短。当 Redis 内存很大时,阻塞时间会很长。误区:AOF 刷盘设为
always最安全,所以线上应该用这个。纠正:always策略会导致每个写命令都刷盘,性能极差,IO 压力极大,线上绝对不要使用。everysec是性能和安全的最佳平衡。误区:混合持久化就是同时开启 RDB 和 AOF。纠正:混合持久化是 AOF 的一种模式,只需要开启 AOF 和
aof-use-rdb-preamble即可,不需要单独开启 RDB。误区:开启了持久化就不会丢失数据。纠正:任何持久化方式都不能保证 100% 不丢失数据,只能将数据丢失的风险降到最低。
误区:AOF 重写会丢失数据。纠正:AOF 重写不会丢失任何数据。重写期间的新命令会被写入重写缓冲区,最后追加到新的 AOF 文件中。
八、高频面试题解答
问:RDB 和 AOF 有什么区别?答:RDB 是全量二进制快照,恢复快但数据丢失多;AOF 是增量写日志,数据安全但恢复慢。混合持久化结合了两者的优点。
问:
bgsave的执行流程是什么?答:主进程 fork 出一个子进程,子进程基于写时复制机制生成 RDB 快照文件,主进程只在 fork 时短暂阻塞。问:什么是写时复制?它在 Redis 持久化中有什么作用?答:写时复制是操作系统提供的内存共享机制。fork 后父子进程共享内存,只有当主进程修改数据时,才会复制内存页。这样可以保证子进程生成的快照是 fork 瞬间的一致性视图。
问:AOF 的三种刷盘策略有什么区别?答:
always每个命令都刷盘,最安全但性能最差;everysec每秒刷一次盘,性能和安全平衡;no由操作系统决定刷盘时机,性能最好但最不安全。问:为什么需要 AOF 重写?重写的流程是什么?答:AOF 文件会随着运行时间不断膨胀,重写可以生成最小命令集合,压缩文件体积。重写流程和
bgsave类似,fork 子进程生成新的 AOF 文件,同时记录重写期间的新命令,最后合并到新文件中。问:什么是混合持久化?有什么优点?答:混合持久化是 Redis 4.0 引入的,AOF 重写时将当前数据以 RDB 格式写入文件开头,然后追加增量写命令。优点是恢复速度快,数据安全性高,文件体积小。
九、总结
Redis 持久化的本质,是在性能和数据安全之间做权衡。没有完美的持久化方案,只有最适合业务的方案。
- RDB 追求极致的恢复速度和最小的文件体积,牺牲了部分数据安全性
- AOF 追求更高的数据安全性,牺牲了部分恢复速度和性能
- 混合持久化完美结合了两者的优点,是目前的最佳实践
理解了 Redis 持久化的底层原理,你就能根据自己的业务场景,做出合理的配置选择,避免线上的各种坑。同时,这也是面试中考察 Redis 核心能力的重要部分,掌握了这些内容,你就能轻松应对所有 Redis 持久化相关的面试题。