0. 前言
redis作为一个高效内存缓存/存储的企业级中间件,需要具备可靠的容灾能力,保证线上功能的可用性。这篇文章,让我们简单、快速地了解AOF和RDB日志,是怎么做容灾的。
1. 怎么做容灾?
首先考虑如果redis宕机了,需要快速恢复,怎么处理?
首先容易想到,我们需要一个机制,把每次的操作记录都保存下来
2. AOF日志是什么
AOF日志是redis的写后日志,用于记录每条命令的操作内容。比如是set key value,那么AOF日志就会记录set key value。
2.1 怎么设计AOF写入流程?
仔细深挖,redis本身也是一种B端的技术产品,从产品架构的角度思考,redis的核心卖点在于高性能,不需要像数据库那么强的一致性。
所以redis的各种组件设计,都满足几点: 一、设计目标:性能优先,减少阻塞 二、持久化策略:保证数据安全与性能尽量平衡 三、架构特性:不影响写入流程的,都走异步化与非阻塞设计
AOF写日志本身也是一个耗时、耗性能的操作。所以对AOF写入流程的设计,会有几个选择:
2.1.1. 写入时机------在每次操作前,还是后写入AOF日志?
如果在每次操作前写入AOF日志,就需要对redis命令做检查,确保是正确的,如果失败,还需要回滚。
如果是在每次操作后写入AOF日志,那么redis命令执行后,AOF日志写入失败,那么redis命令执行的结果,就可能丢失,不用保证强一致性。
最终选型上使用了写后日志,两个原因:
- redis由于没有强一致性需求,写后避免了复杂的事务操作
- 写后日志避免了IO冲突,不影响主线程的写入,可以异步执行。
2.1.2 写入方式------同步、异步(定时写入、操作系统写入)
总结一下,实际过程的写入方式,会先由主线程 -> Redis数据 -> AOF缓冲区 -> 磁盘,其中对磁盘的写入方式分为三种:
Redis 的 AOF 支持三种 fsync
策略(控制日志写入磁盘的时机):
策略 | 描述 | 性能影响 | 数据安全 |
---|---|---|---|
always |
每条命令执行后同步 fsync (类似写前日志),但会严重降低性能。 |
高延迟 | 最高 |
everysec (默认) |
每秒执行一次 fsync ,命令执行与日志写入异步解耦,性能损失极小。 |
低延迟 | 较高 |
no |
由操作系统决定何时从缓冲区写入磁盘,性能最佳(仅受限于磁盘吞吐量)。 | 最低 | 最低 |
好了,现在我们解决了AOF日志的写入问题,可以顺利的把操作记录保存下来。但随着操作记录日渐增多,AOF日志会不断变大,导致两个问题:
- 在做数据恢复时,AOF日志会非常慢
- 文件系统对文件大小有限制,AOF会大量占用磁盘空间
- 如果文件太大,追加命令记录,效率会变低(本质是磁盘寻址太久导致,由于文件大,内容分散在磁盘各个区域,需要从头扫描到尾)
这时候怎么解决呢? 会想到,对同一个数据,AOF可能会产生多次不同的操作,而我们并不关注历史所有的操作记录,只需要保存最新的操作内容即可。
这样,我们就可以通过合并操作记录,来压缩AOF日志的空间大小。这也叫做AOF日志重写。
2.2 AOF日志重写
AOF日志的重写,目的就是为了保留最新的数据状态,其操作就是合并操作记录。

这时候我们也要想到,AOF重写的任务,是否会拖慢redis主线程? 这里先说答案,不会,因为AOF重写 会起一个单独的子线程去做重写任务。
2.3 AOF异步重写
重写这个任务本身很简单,但既然是异步重写,我们就需要考虑 存量redis数据
和重写期间的增量数据
怎么处理。
对于【存量数据】:
- 首先,redis会fork出一个子线程(bg_rewrite_aof),并做redis数据拷贝
- 子线程对redis数据拷贝重写AOF日志,写入到磁盘;主线程停止
对于【增量数据】: redis会开辟一块AOF重写缓冲区,每次有增量数据,会写入AOF重写缓冲区,在存量数据处理完后,再根据AOF重写缓冲区再处理一遍,即可得到完整的AOF数据。
2.4 AOF总结
综上所述,一个能对Redis数据做持久化,保证数据可靠性的基本容灾日志,就已经设计完成了:
AOF 是 Redis 的写后持久化日志,通过记录写命令实现数据状态持久化,核心设计围绕高性能、数据安全与异步化展开,要点如下:
一、写入机制与策略
-
写后日志:先执行命令修改内存,再记录日志,避免阻塞主线程与复杂回滚。
-
刷盘策略:
always
:每条命令同步刷盘(安全高、性能低)。everysec
(默认):每秒异步刷盘,平衡性能与安全。no
:由系统控制刷盘,性能最佳但数据安全性最低。
二、日志优化:AOF 重写
-
目标 :合并冗余命令(如用最终
SET
覆盖中间操作),压缩日志体积,提升恢复效率。 -
实现:
- 子进程(
bgrewriteaof
)异步重写,避免阻塞主线程。 - 重写期间通过缓冲区记录增量操作,完成后追加至新日志,确保数据完整。
- 子进程(
三、核心特性
- 异步化设计:写入、重写均采用异步或子进程机制,保障 Redis 高吞吐量。
- 数据恢复:按日志顺序重放命令重建数据,可结合 RDB 提升恢复效率。
四、优缺点与适用场景
-
优点:数据安全高(依赖策略)、日志可读性强。
-
缺点:日志可能膨胀(需重写优化)、极端场景可能丢数据。
-
场景:适用于对数据安全要求较高、允许部分性能损耗的业务(如订单、交易记录缓存)。
总结:AOF 通过 "异步日志 + 灵活策略 + 重写优化",在 Redis 的高性能与数据持久化间实现平衡,需根据业务需求配置策略与重写规则。
3.RDB快照
既然有了AOF日志,那我们是不是在redis的数据恢复上,就能高枕无忧了呢?实际上不是的。因为如果操作日志特别多,Redis就会恢复的特别慢,会导致故障恢复时间变长,这是我们难以接受的。
所以想到一个方法,我们不依赖操作记录恢复,而是把内存某个时刻的数据快照保存,用快照进行快速恢复。所以RDB快照就应运而生了。
3.1 怎么做快照
首先Redis既然要保证数据恢复,那需要做【全量数据快照】。
而在做数据快照期间,也需要尽量避免影响主线程,提高redis的运行效率。
Redis提供了两种快照方式:
- save:主线程中执行,阻塞
- bgsave:异步执行,不阻塞
这时候我们就可以用bgsave执行,主线程会fork出一个bgsave的子线程,同时共享主线程的所有内存数据。
3.2 快照期间,数据改动怎么处理?
比较简单的想法是,将待保存的数据全部copy一个副本,我们在副本数据上进行改动。
但是全部copy会比较消耗内存空间,因此考虑,最好能处理到哪个数据,我们临时copy一份处理,同时保留原本的数据做快照。这个时候,Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本(键值对 C')。然后,主线程在这个数据副本上进行修改。同时,bgsave 子进程可以继续把原来的数据(键值对 C)写入 RDB 文件。

3.3 快照的写入方式&时机
从数据恢复的时间上看,快照的时间,其实离redis宕机时间越近越好。如果是定时快照,快照间隔越短,数据恢复的越快。
但是如果快照过于频繁,也会带来开销:
- 快照会频繁写入磁盘,导致有限的磁盘带宽被打满,AOF日志可能没办法落盘,从而阻塞主线程。
- bgsave 子进程需要通过 fork 操作从主线程创建出来,频繁的fork操作,也会导致阻塞主线程。
3.4 快照优化------增量快照
刚才我们了解到,无论如何,快照保存总是存在一定开销,那能否从要保存的数据上考虑呢? 在第一次保存全量数据后,我们其实可以不用每次保存全量的数据,只保存增量数据。
后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。不过这样我们需要额外的开销,去记录到底哪些数据被修改了,哪些数据没有改过。

3.5 总结
快照的方式,虽然可以快速恢复数据,但很难把握好数据快照的时机,总是需要在快照时机、数据准确性之间权衡。那有没有更好的优化方式呢?
4.混合使用 AOF 日志和内存快照(Redis 4.0)
内存快照固然好,但两次快照之间如果宕机,会导致数据恢复缺失,这是不能接受的。
但我们可以考虑结合AOF日志,将快照之间的数据落盘到AOF上,快照后清空AOF日志的内容,这样不仅能充分利用内存快速恢复的优点,也解决了AOF日志过大的问题。