AOF持久化

与全量式的RDB持久化功能不同,AOF提供的是增量式的持久化功能, 这种持久化的核心原理在于:服务器每次执行完写命令之后,都会以 协议文本的方式将被执行的命令追加到AOF文件的末尾。这样一来,服 务器在停机之后,只要重新执行AOF文件中保存的Redis命令,就可以 将数据库恢复至停机之前的状态。

作为例子,表15-3展示了一个Redis服务器生成AOF文件的过程。

image 2025 01 05 22 24 05 559
Figure 1. 表15-3 AOF文件的生成过程
image 2025 01 05 22 24 20 677

从表15-3中可以看到,随着服务器不断地执行命令,被执行的命令也 会不断地被保存到AOF文件中(文件中唯一不是用户执行的命令SELECT 0是服务器根据用户正在使用的数据库号码自动加上的)。这样一来, 即使服务器在T4停机,它也可以在重启时通过重新执行AOF文件包含的 命令来恢复数据。对于表15-3中展示的例子来说,服务器只要重新执 行AOF文件中包含的4个命令,就可以让数据库重新回到停机之前的状 态。

为了方便展示,本书在介绍AOF相关内容时,通常会直接写出被执行的 命令,但是在实际的AOF文件中,命令都是以Redis网络协议的方式保 存的。比如,对于表15-3所示的情况,服务器将创建一个包含代码清 单15-1所示内容的AOF文件(为了能够清晰地辨别各个命令,清单将每 个命令都单独列为一行)。

代码清单15-1 被执行的命令将以协议格式存储在AOF文件中

*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n
*3\r\n$3\r\nSET\r\n$2\r\nk1\r\n$2\r\nv1\r\n
*3\r\n$3\r\nSET\r\n$2\r\nk2\r\n$2\r\nv2\r\n
*5\r\n$5\r\nRPUSH\r\n$3\r\nlst\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n

打开AOF持久化功能

用户可以通过服务器的appendonly选项来决定是否打开AOF持久化功能:

appendonly <value>

如果用户想要开启AOF持久化功能,那么只需要将这个值设置为yes即可:

appendonly yes

反之,如果用户想要关闭 AOF 持久化功能,那么只需要将这个值设置为 no 即可:

appendonly no

当AOF持久化功能处于打开状态时,Redis服务器在默认情况下将创建 一个名为appendonly.aof的文件作为AOF文件。

设置AOF文件的冲洗频率

为了提高程序的写入性能,现代化的操作系统通常会把针对硬盘的多 次写操作优化为一次写操作。具体的做法是,当程序调用write系统调 用对文件进行写入时,系统并不会直接把数据写入硬盘,而是会先将 数据写入位于内存的缓冲区中,等到指定的时限到达或者满足某些写 入条件时,系统才会执行flush系统调用,将缓冲区中的数据冲洗至硬 盘。

这种优化机制虽然提高了程序的性能,但是也给程序的写入操作带来 了不确定性,特别是对于AOF这样的持久化功能来说,AOF文件的冲洗 机制将直接影响AOF持久化的安全性。为了消除上述机制带来的不确定 性,Redis向用户提供了appendfsync选项,以此来控制系统冲洗AOF文 件的频率:

appendfsync<value>

appendfsync选项拥有always、everysec和no 3个值可选,它们代表的意义分别为:

  • always——每执行一个写命令,就对AOF文件执行一次冲洗操作。

  • everysec——每隔1s,就对AOF文件执行一次冲洗操作。

  • no——不主动对AOF文件执行冲洗操作,由操作系统决定何时对AOF进行冲洗。

这3种不同的冲洗策略不仅会直接影响服务器在停机时丢失的数据量,还会影响服务器在运行时的性能:

  • 在使用always值的情况下,服务器在停机时最多只会丢失一个命令 的数据,但使用这种冲洗方式将使Redis服务器的性能降低至传统关系 数据库的水平。

  • 在使用everysec值的情况下,服务器在停机时最多只会丢失1s之内 产生的命令数据,这是一种兼顾性能和安全性的折中方案。

  • 在使用no值的情况下,服务器在停机时将丢失系统最后一次冲洗AOF 文件之后产生的所有命令数据,至于数据量的具体大小则取决于系统 冲洗AOF文件的频率。

因为no策略给可能丢失的数据量带来了不确定性,而always策略对于 安全性的追求又牺牲了服务器的性能,所以Redis使用everysec作为 appendfsync选项的默认值。除非有明确的需求,否则用户不应该随意 修改appendfsync选项的值。

AOF重写

随着服务器不断运行,被执行的命令将变得越来越多,而负责记录这 些命令的AOF文件也会变得越来越大。与此同时,如果服务器曾经对相 同的键执行过多次修改操作,那么AOF文件中还会出现多个冗余命令。

举个例子,对于代码清单15-2所示的AOF文件:

代码清单15-2 包含冗余内容的AOF文件

SELECT 0
SET msg "hello world!"
SET msg "good morning!"
SET msg "happy birthday!"
SADD fruits "apple"
SADD fruits "banana"
SADD fruits "cherry"
SADD fruits "dragon fruit"
SREM fruits "dragon fruit"
SADD fruits "durian"
RPUSH job-queue 10086
RPUSH job-queue 12345
RPUSH job-queue 256512

文件中的3组命令分别对msg、fruits和job-queue 3个键进行了多次修 改,但是这些命令对数据库的最终修改效果实际上可以简化为以下4条 命令: ·SELECT 0 ·SET msg"happy birthday!" ·SADD fruits"apple""banana""cherry""durian" ·RPUSH job-queue 1008612345256512

冗余命令的存在不仅增加了AOF文件的体积,并且因为Redis服务器在 停机之后需要通过重新执行AOF文件中保存的命令来恢复数据,所以 AOF文件中的冗余命令越多,恢复数据时耗费的时间也会越多。为了减 少冗余命令,让AOF文件保持“苗条”,并提供数据恢复操作的执行速 度,Redis提供了AOF重写功能,该功能能够生成一个全新的AOF文件, 并且文件中只包含恢复当前数据库所需的尽可能少的命令。

举个例子,如果我们对代码清单15-2所示的AOF文件执行AOF重写操 作,那么Redis将生成代码清单15-3所示的AOF文件,该文件只包含了 上述提到的4个命令:

代码清单15-3 重写之后的AOF文件

SELECT 0
SET msg "happy birthday!"
SADD fruits "apple" "banana" "cherry" "durian"
RPUSH job-queue 10086 12345 256512

用户可以通过执行BGREWRITEAOF命令或者设置相关的配置选项来触发 AOF重写操作,接下来将分别介绍这两种触发方法。

  1. BGREWRITEAOF命令

    用户可以通过执行BGREWRITEAOF命令显式地触发AOF重写操作,该命令是一个无参数命令:

    redis> BGREWRITEAOF
    Background append only file rewriting started

    BGREWRITEAOF命令是一个异步命令,Redis服务器在接收到该命令之后 会创建出一个子进程,由它扫描整个数据库并生成新的AOF文件。当新 的AOF文件生成完毕,子进程就会退出并通知Redis服务器(父进 程),然后Redis服务器就会使用新的AOF文件代替已有的AOF文件,借 此完成整个重写操作。

    关于BGREWRITEAOF还有两点需要注意:首先,如果用户发送 BGREWRITEAOF命令请求时,服务器正在创建RDB文件,那么服务器将把 AOF重写操作延后到RDB文件创建完毕之后再执行,从而避免两个写硬 盘操作同时执行导致机器性能下降;其次,如果服务器在执行重写操 作的过程中,又接收到了新的BGREWRITEAOF命令请求,那么服务器将 返回以下错误:

    redis> BGREWRITEAOF
    (error) ERR Background append only file rewriting already in progress

    其他信息

    • 复杂度:O(N),其中N为Redis服务器所有数据库包含的键值对总数量。

    • 版本要求:BGREWRITEAOF命令从Redis 1.0.0版本开始可用。

  2. AOF重写配置选项

用户除了可以手动执行BGREWRITEAOF命令创建新的AOF文件之外,还可 以通过设置以下两个配置选项让Redis自动触发BGREWRITEAOF命令:

auto-aof-rewrite-min-size <value>
auto-aof-rewrite-percentage <value>

其中auto-aof-rewrite-min-size选项用于设置触发自动AOF文件重写 所需的最小AOF文件体积,当AOF文件的体积小于给定值时,服务器将 不会自动执行BGREWRITEAOF命令。在默认情况下,该选项的值为:

auto-aof-rewrite-min-size 64mb

也就是说,如果AOF文件的体积小于64MB,那么Redis将不会自动执行 BGREWRI-TEAOF命令。

至于另一个选项,它控制的是触发自动AOF文件重写所需的文件体积增 大比例。举个例子,对于该选项的默认值:

auto-aof-rewrite-percentage 100

表示如果当前AOF文件的体积比最后一次AOF文件重写之后的体积增大 了一倍(100%),那么将自动执行一次BGREWRITEAOF命令。如果Redis 服务器刚刚启动,还没有执行过AOF文件重写操作,那么启动服务器时 使用的AOF文件的体积将被用作最后一次AOF文件重写的体积。

举个例子,如果服务器启动时AOF文件的体积为200MB,而auto-aofrewrite-percentage选项的值为100,那么当AOF文件的体积增大至超 过400MB时,服务器就会自动进行一次AOF重写。与此类似,在同样设 置下,如果AOF文件的体积从最后一次重写之后的300MB增大至超过 600MB,那么服务器将再次执行AOF重写操作。

AOF持久化的优缺点

与RDB持久化可能会丢失大量数据相比,AOF持久化的安全性要高得 多:通过使用everysec选项,用户可以将数据丢失的时间窗口限制在 1s之内。

但是与RDB持久化相比,AOF持久化也有相应的缺点:

  • 首先,因为AOF文件存储的是协议文本,所以它的体积会比包含相同 数据、二进制格式的RDB文件要大得多,并且生成AOF文件所需的时间 也会比生成RDB文件所需的时间更长。

  • 其次,因为RDB持久化可以直接通过RDB文件恢复数据库数据,而AOF 持久化则需要通过执行AOF文件中保存的命令来恢复数据库(前者是直 接的数据恢复操作,而后者则是间接的数据恢复操作),所以RDB持久 化的数据恢复速度将比AOF持久化的数据恢复速度快得多,并且数据库 体积越大,这两者之间的差距就会越明显。

  • 最后,因为AOF重写使用的BGREWRITEAOF命令与RDB持久化使用的 BGSAVE命令一样都需要创建子进程,所以在数据库体积较大的情况 下,进行AOF文件重写将占用大量资源,并导致服务器被短暂地阻塞。