Redis 是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多。但是一旦进程退出,Redis 的数据就会丢失。
为了解决这个问题,Redis 提供了 RDB 和 AOF 两种持久化方案,将内存中的数据保存到磁盘中,避免数据丢失。RDB的介绍在这篇文章中《Redis RDB 持久化详解》,今天我们来看一下 AOF 相关的原理。
AOF( append only file )持久化以独立日志的方式记录每次写命令,并在 Redis 重启时在重新执行 AOF 文件中的命令以达到恢复数据的目的。AOF 的主要作用是解决数据持久化的实时性。
RDB 和 AOFantirez 在《Redis 持久化解密》一文中讲述了 RDB 和 AOF 各自的优缺点:
RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据备份。非常适合备份,全量复制等场景。比如每6小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复。
Redis 加载 RDB 恢复数据远远快于 AOF 的方式
RDB 方式数据没办法做到实时持久化,而 AOF 方式可以做到。
下面,我们就来了解一下 AOF 是如何做到实时持久化的。
AOF 持久化的实现如上图所示,AOF 持久化功能的实现可以分为命令追加( append )、文件写入( write )、文件同步( sync )、文件重写(rewrite)和重启加载(load)。其流程如下:
所有的写命令会追加到 AOF 缓冲中。
AOF 缓冲区根据对应的策略向硬盘进行同步操作。
随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
命令追加当 AOF 持久化功能处于打开状态时,Redis 在执行完一个写命令之后,会以协议格式(也就是RESP,即 Redis 客户端和服务器交互的通信协议 )将被执行的写命令追加到 Redis 服务端维护的 AOF 缓冲区末尾。
比如说 SET mykey myvalue 这条命令就以如下格式记录到 AOF 缓冲中。
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
Redis 协议格式本文不再赘述,AOF之所以直接采用文本协议格式,是因为所有写入命令都要进行追加操作,直接采用协议格式,避免了二次处理开销。
文件写入和同步Redis 每次结束一个事件循环之前,它都会调用 flushAppendOnlyFile 函数,判断是否需要将 AOF 缓存区中的内容写入和同步到 AOF 文件中。
flushAppendOnlyFile 函数的行为由 redis.conf 配置中的 appendfsync 选项的值来决定。该选项有三个可选值,分别是 always、 everysec 和 no:
always:Redis 在每个事件循环都要将 AOF 缓冲区中的所有内容写入到 AOF 文件,并且同步 AOF 文件,所以 always 的效率是 appendfsync 选项三个值当中最差的一个,但从安全性来说,也是最安全的。当发生故障停机时,AOF 持久化也只会丢失一个事件循环中所产生的命令数据。
everysec:Redis 在每个事件循环都要将 AOF 缓冲区中的所有内容写入到 AOF 文件中,并且每隔一秒就要在子线程中对 AOF 文件进行一次同步。从效率上看,该模式足够快。当发生故障停机时,只会丢失一秒钟的命令数据。
no:Redis 在每一个事件循环都要将 AOF 缓冲区中的所有内容写入到 AOF 文件。而 AOF 文件的同步由操作系统控制。这种模式下速度最快,但是同步的时间间隔较长,出现故障时可能会丢失较多数据。
Linux 系统下 write 操作会触发延迟写( delayed write )机制。Linux 在内核提供页缓存区用来提供硬盘 IO 性能。 write 操作在写入系统缓冲区之后直接返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或者达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
而 fsync 针对单个文件操作,对其进行强制硬盘同步, fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。
appendfsync的三个值代表着三种不同的调用 fsync的策略。调用 fsync周期越频繁,读写效率就越差,但是相应的安全性越高,发生宕机时丢失的数据越少。
有关 Linux 的I/O和各个系统调用的作用如下图所示。具体内容可以查看《聊聊 Linux I/O》一文。
AOF 数据恢复