Redis提供了两种主要的持久化方式:RDB快照和AOF日志。RDB是在指定时间点对内存数据做全量快照,而AOF则是以追加写命令的形式记录每一次修改操作。相比RDB,AOF可以在更细粒度上保证数据不丢失,默认情况下每秒刷盘一次,最多只会丢失一秒内的写入。理解AOF的日志格式、重写过程和刷盘策略,有助于在性能和数据安全之间做出合理选择。

一、AOF日志的记录过程与文件格式
AOF的全称是Append Only File,它的核心工作方式是在Redis每次执行写命令之后,把该命令以Redis序列化协议(RESP)的格式追加到AOF文件末尾。例如执行SET username alice这条命令,Redis不会直接保存username和alice这两个值,而是把整条命令原封不动地写到文件里。这样做的好处是恢复过程非常直观:只要按顺序重新执行文件中的所有命令,就能重建出宕机前的内存数据。
AOF文件的内容是可读的纯文本,采用RESP协议编码。每一条命令由若干部分组成,以*开头表示参数个数,接着用$开头表示每个参数的字节长度,然后才是参数本身。下面是一个简单的AOF文件片段,展示了一条SET命令的存储格式:
*3 $3 SET $8 username $5 alice
实际写入文件时,每一行末尾都会带上\r\n回车换行符,上面示例为了阅读方便省略了这些符号。可以看到,AOF日志不是二进制格式,因此可以直接用文本编辑器打开查看,这是它相对于RDB的一大优势。当Redis启动时,如果开启了AOF持久化,它会读取AOF文件并逐条回放命令来恢复数据。回放的过程是串行的,因此AOF文件中的命令顺序必须与真实执行顺序一致,Redis通过单线程模型天然保证了这一点。
除了记录写命令,Redis还提供了appendonly yes配置项来开启AOF。如果不开启,Redis只会使用RDB快照。在redis.conf配置文件中,可以通过appendfilename指定AOF文件名,默认是appendonly.aof。每次写命令执行完成后,命令会先被写入内存中的AOF缓冲区,等待后续的刷盘操作。这个缓冲区的存在是为了减少磁盘I/O次数,但同时也引入了数据丢失的风险,具体的刷盘时机由appendfsync参数控制。
二、AOF重写机制与自动触发条件
随着写入命令不断增多,AOF文件会越来越大。如果不对其进行压缩,不仅会占用大量磁盘空间,还会导致Redis重启时恢复时间变长。为了解决这个问题,Redis引入了AOF重写(Rewrite)机制。重写的本质不是去读取旧AOF文件进行整理,而是根据当前内存中的数据状态,重新生成一份最小的命令集合。
举个例子,假设在运行过程中用户对同一个key执行了多次写操作,比如先SET counter 1,再INCR counter,然后INCR counter,最终counter的值是3。旧AOF文件中会记录三条命令,而重写后的新AOF文件只需要一条SET counter 3就能表达相同的数据状态。这样文件体积就能大幅下降,恢复时也只需要执行更少的命令。
Redis执行AOF重写时会采用子进程来完成,主进程仍然可以继续处理客户端请求。这是通过操作系统的写时复制(Copy On Write)机制实现的:子进程创建时共享父进程的内存数据,当父进程需要修改某个内存页时,操作系统会复制该页,从而保证子进程看到的始终是重写开始那一刻的数据快照。在子进程重写期间,主进程接收到的新写命令会同时写入旧AOF缓冲区和重写增量缓冲区。子进程完成重写后,主进程把增量缓冲区中的命令追加到新AOF文件末尾,然后原子地替换旧文件。
重写可以手动触发,也可以自动触发。手动触发只需在Redis客户端执行BGREWRITEAOF命令。自动触发则需要配置两个参数:auto-aof-rewrite-percentage和auto-aof-rewrite-min-size。下面是一个典型的配置示例:
auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
这段配置的含义是:当AOF文件大小超过上一次重写后大小的100%(也就是翻倍),并且当前AOF文件大小大于64MB时,Redis会自动触发一次后台重写。例如上次重写后AOF文件为80MB,当它增长到160MB时就会触发重写。auto-aof-rewrite-min-size的作用是避免文件还很小的时候频繁触发重写,因为重写本身也会消耗CPU和内存资源。
三、AOF的fsync策略与性能权衡
写命令从内存缓冲区刷到磁盘的时机,直接决定了AOF的持久化强度。Redis通过appendfsync参数提供了三种策略:always、everysec和no。每种策略在数据安全性和写入性能之间有不同的取舍。
always模式要求Redis每执行完一条写命令,就立即调用fsync把缓冲区数据同步到磁盘。这种模式下数据最安全,即使发生宕机也几乎不会丢失任何已确认的写命令。但缺点是磁盘I/O开销非常大,因为每条命令都要等待物理磁盘写入完成,吞吐量会明显下降。一般只有在数据绝对不能丢失的极端场景下才会使用。
everysec模式是Redis的默认设置,它每秒执行一次fsync。主线程不会阻塞在磁盘同步上,而是由后台线程负责刷盘。这样即使发生宕机,最多只会丢失一秒内的写入数据。在性能上,everysec模式能够提供接近纯内存操作的写入吞吐量,同时保证数据丢失窗口很小,因此成为大多数生产环境的推荐选择。
no模式则完全由操作系统决定何时刷盘,Redis只负责把数据写入内核缓冲区。这种模式性能最好,但数据丢失风险也最高,因为操作系统可能会在缓冲区积累较多数据后才一次性写入磁盘。如果发生断电或系统崩溃,丢失的数据量可能远超一秒。
下面用表格对比三种策略:
| appendfsync | 刷盘时机 | 数据安全性 | 写入性能 |
|---|---|---|---|
| always | 每条命令后同步 | 极高,基本不丢数据 | 低 |
| everysec | 每秒同步一次 | 较高,最多丢一秒数据 | 中高 |
| no | 交给操作系统 | 低,可能丢多秒数据 | 高 |
此外,Redis还提供了一个no-appendfsync-on-rewrite配置项。当该选项设置为yes时,在执行AOF重写期间,Redis会暂时停止对旧AOF文件执行fsync,把磁盘I/O资源优先让给重写过程。这样做可以缩短重写时间,但代价是如果此时发生宕机,可能会丢失更多数据。是否开启需要根据业务对数据丢失的容忍度来决定。
四、AOF文件损坏修复与混合持久化
虽然AOF文件是纯文本格式,但如果在写入过程中发生断电或磁盘故障,文件末尾可能会出现不完整的命令记录,导致Redis启动时无法正常加载AOF。Redis自带了redis-check-aof工具来检测和修复损坏的AOF文件。如果发现AOF文件有错误,可以执行以下命令进行修复:
redis-check-aof --fix appendonly.aof
修复的原理是扫描AOF文件并定位到第一条格式不完整的命令位置,然后丢弃该位置之后的所有内容。也就是说,修复操作会保留最后一条完整命令之前的数据,文件末尾损坏的部分会被截断。执行修复前建议先备份原始AOF文件,以免误操作造成不可逆的数据丢失。
从Redis 4.0开始,还引入了混合持久化方案。通过在配置文件中设置aof-use-rdb-preamble yes,可以在AOF重写时把重写文件的前半部分用RDB二进制快照表示,后半部分继续追加增量AOF命令。这样既能获得RDB快速恢复的优势,又能利用AOF保证数据完整性。混合持久化模式下的AOF文件不再是完全纯文本,开头部分是二进制RDB内容,因此不能直接用文本编辑器查看完整内容,但恢复速度会比纯AOF快很多。
混合持久化的恢复过程也很简单:Redis启动时先读取文件开头的RDB快照快速加载大部分数据,然后再执行后面的AOF增量命令补齐快照之后的修改。这种方式大幅缩短了重启时间,尤其是在数据量很大的场景下,优势更加明显。如果业务对恢复速度有较高要求,同时希望保留AOF级别的数据安全性,开启混合持久化是一个不错的选择。
需要强调的是,无论采用哪种持久化策略,都应该定期对AOF文件进行备份,并在测试环境中演练恢复流程。只有真正经历过恢复演练,才能确保在发生故障时能够快速、准确地找回数据。