Skip to content
文章大纲

Redis 与传统数据库的一个主要区别在于,Redis 把所有数据存储在内存中,而传统数据库通常将数据的索引存储在内存,并把实际的数据存储在磁盘中。虽然 Redis 的数据存储使得用户能以极快的速度读写服务器的数据,但由于内存属于易失存储器,内存所记录的所有数据在系统断电后就会丢失,为了解决此问题 Redis 向用户提供了持久化功能,持久化功能可以把内存中存储的数据以文件形式存储到磁盘中,而服务器也可以根据这些文件在系统重启之后进行数据恢复。为了满足不同的持久化需求,Redis 提供了 RDB 持久化、AOF 持久化和 RDB-AOF 混合持久化等多种持久化方式,如果不需要持久化功能,也可以完全关闭持久化功能,让服务器处于无持久化状态。Redis 默认以 RDB 持久化作为持久化功能。

1.RDB 持久化

RDB 持久化是 Redis 默认使用的持久化功能,该功能可以创建出一个经过压缩的二进制文件,该文件包含了服务器在各个数据库中存储的键值对数据等信息。RDB 持久化产生的文件均以.rdb 后缀结尾,其中 RDB 表示 Redis DataBase(Redis 数据库)。

1.1 创建 RDB 文件方式

Redis 提供了多种创建 RDB 文件的方式:

  • 通过 SAVE 命令手动创建 RDB 文件。
  • 通过 BGSAVE 命令手动创建 RDB 文件。
  • 通过 Redis 配置文件设置 save 选项让服务器在满足指定条件时自动执行 BGSAVE 命令。

1.1.1 SAVE 命令创建 RDB 文件

执行 SAVE 命令创建 RDB 文件会阻塞服务器,它会要求 Redis 服务器以同步方式创建一个记录了服务器当前所有数据库数据的 RDB 文件。SAVE 是一个无参命令,它在创建 RDB 文件成功时将返回 OK 作为结果。

接收到 SAVE 命令的 Redis 服务器将遍历数据库包含的所有数据库,并将各个数据库包含的键值对全部记录到 RDB 文件中。在 SAVE 命名执行期间,Redis 服务器将会阻塞,直到 RDB 文件创建完毕为止。如果 Redis 服务器在执行 SAVE 命令是已经拥有了响应的 RDB 文件,那么服务器将使用新创建的 RDB 文件代替已有的 RDB 文件。SAVE 命令复杂度为 O(N),其中 N 为 Redis 服务器所有数据包含的键值对总数量。执行 save 命令 Redis-server 输出日志如下图:

1.1.2 BGSAVE 命令创建 RDB 文件

执行 BGSAVE 命令会以非阻塞的方式创建 RDB 文件。因为执行 SAVE 命令时会阻塞整个服务器,用户在使用 SAVE 命令创建 RDB 文件期间 Redis 将无法为其他客户端提供服务。为了解决此问题,Redis 提供 SAVE 命令的异步版本 BGSAVE 命令,它与 SAVE 命令不同之处在于 BGSAVE 不会直接使用 Redis 服务器进程创建 RDB 文件,而是分配一个子进程创建 RDB 文件。当 Redis 服务器接收到 BGSAVE 命令时将执行一下操作:

  • 创建一个子进程。
  • 子进程执行 BGSAVE 命令,创建新的 RDB 文件。
  • RDB 文件创建完毕之后子子进程退出并通知 Redis 服务器进程(父进程)新的 RDB 文件已创建完毕。
  • Redis 服务器进程使用新创建的 RDB 文件替换已有 RDB 文件。

因为 BGSAVE 命令创建 RDB 文件的操作是有子进程以异步方式进行的,所以当用户在客户端执行此命令时,服务器将立即向客户端返回 OK,然后才会在后台开始具体的 RDB 文件创建操作。

因为 BGSAVE 命令是以异步方式执行的,所以 Redis 服务器在 BGSAVE 命令执行期间仍然可以继续处理其他客户端发送的命令请求。不过需要注意的是,虽然 BGSAVE 命令不会像 SAVE 命令那样一直阻塞 Redis 服务器,但由于执行 BGSAVE 命令需要创建子进程,所以父进程占用的内存数量越大,创建子进程这一操作耗费的时间也会越长,因此 Redis 服务器在执行 BGSAVE 命令时,仍然可能会由于创建子进程而被短暂地阻塞。BGSAVE 命令复杂度为 O(N),其中 N 为 Redis 服务器所有数据包含的键值对总数量

1.1.3 通过 Redis 配置文件配置选项自动创建 RDB 文件

除了可以使用 SAVE 命令和 BGSAVE 命令手动创建 RDB 文件之外,还可以通过配置 Redis 配置文件设置 save 选项,让 Redis 服务器在满足指定条件自动执行 BGSAVE 命令:

shell
save <seconds> <changes>

save 选项接收 seconds 和 changes 两个参数,seconds 用于指定触发持久化操作所需要的时长,changes 用于指定触发持久化操作所需的修改次数。简单来说,如果 Redis 在 seconds 秒内,对其包含的各个数据库总共执行了 changes 次修改,那么服务器会自动执行一次 BGSAVE 命令。例如下面的配置:

shell
save 60 10000

上面配置表示服务器在 60 秒内至少执行了 10000 次时,服务器就会自动执行一次 BGSAVE 命令。Redis 允许用户向服务器提供多条 save 选项,当给选项中的任意一个条件被满足时,服务器就会自动执行一次 BGSAVE 命令。例如下面的命令:

shell
save 6000 10000
save 600 1000
save 60 10

当以上任意一个条件被满足时,服务器就会自动执行一此 BGSAVE 命令:

  • 在 6000 秒内(100min),服务器对数据库执行了至少 10000 此修改。
  • 在 600 秒内(10min)服务器对数据库执行了至少 1000 次修改。
  • 在 60 秒内服务器对象数据库执行了至少 10 修改。

注意:为了避免由于同时使用多个触发条件而导致服务器过于频繁地执行 BGSAVE 命令,Redis 服务器在每次成功创建 RDB 文件之后,负责自动触发 BGSAVE 命令的时间计数器以及修改次数计数器都会被清零并重新开始计数:无论这个 RDB 文件是由自动触发的 BGSAVE 命令创建的,还是由用户执行的 SAVE 命令或 BGSAVE 命令创建的,都是如此。

RDB 持久化是 Redis 默认使用的持久化方式,如果用户在启动 Redis 服务器时,既没有显式地关闭 RDB 持久化功能,也没有启用 AOF 持久化功能,那么 Redis 默认将使用以下 save 选项进行 RDB 持久化:

shell
save 60 10000
save 300 100
save 3600 1

如果想关闭默认的 RDB 持久化行为,让 Redis 服务器处于完全无持久状态,那么可以在 Redis 配置文件提供如下配置:

shell
save ""

使用上面的选项 Redis 服务器将不会再进行默认的 RDB 持久化,处于无持久状态的服务器在关机之后将丢失关机之前存储的所有数据,这种服务器可以用作单纯的内存缓存服务器。 SAVE 命令在创建 RDB 文件期间会阻塞 Redis 服务器,如果需要在创建 RDB 文件期间的同时让 Redis 服务器继续为其他客户端服务,那么只能选择 BGSAVE 命令来创建 RDB 文件。因为 SAVE 命令无须创建子进程,它不会因为创建子进程而消耗额外的内存,所以在维护离线的 Redis 服务器时,使用 SAVE 命令能够比使用 BGSAVE 命令更快地完成创建 RDB 文件的工作。

1.2 RDB 的文件结构

RDB 总体结构分为 7 个部分,如下图:

  • RDB 文件标识符:文件最开头的部分为 RDB 文件标识符,这个标识符的内容为"REDIS"这 5 个字符。Redis 服务器在尝试载入 RDB 文件的时候,可以通过这个标识符快速地判断该文件是否为真正的 RDB 文件。
  • 版本号:跟在 RDB 文件标识符之后的是 RDB 文件的版本号,这个版本号是一个字符串格式的数字,长度为 4 个字符。目前最新的 RDB 文件版本为第 9 版,因此 RDB 文件的版本号将为字符串"0009"。不同版本的 RDB 文件在结构上都会有一些不同,总的来说,新版 RDB 文件都会在旧版 RDB 文件的基础上添加更多信息,因此 RDB 文件的版本越新,RDB 文件的结构就越复杂。关于 RDB 文件,需要说明的另外一点是新版 Redis 服务器总是能够向下兼容旧版 Redis 服务器生成的 RDB 文件。比如,生成第 9 版 RDB 文件的 Redis 5.0 既能够正常读入由 Redis 4.0 生成的第 8 版 RDB 文件,也能够读入由 Redis 3.2 生成的第 7 版 RDB 文件,甚至更旧版本的 RDB 文件也是可以的。与此相反,如果 Redis 服务器生成的是较旧版本的 RDB 文件,那么它是无法读入更新版本的 RDB 文件的。比如,生成第 8 版 RDB 文件的 Redis 4.0 就不能读入由 Redis 5.0 生成的第 9 版 RDB 文件。
  • 设备附加信息:RDB 文件的设备附加信息部分记录了生成 RDB 文件的 Redis 服务器及其所在平台的信息,比如服务器的版本号、宿主机器的架构、创建 RDB 文件时的时间戳、服务器占用的内存数量等。
  • 数据库数据:RDB 文件的数据库数据部分记录了 Redis 服务器存储的 0 个或任意多个数据库的数据,当这个部分包含多数个数据库的数据时,各个数据库的数据将按照数据库号码从小到大进行排列,比如,0 号数据库的数据将排在最前面,紧接着是 1 号数据库的数据,然后是 2 号数据库的数据,以此类推,例如下图。
  • Lua 脚本缓存:如果 Redis 服务器启用了复制功能,那么服务器将在 RDB 文件的 Lua 脚本缓存部分保存所有已被缓存的 Lua 脚本。这样一来,从服务器在载入 RDB 文件完成数据同步之后,就可以继续执行主服务器发来的 EVALSHA 命令了。
  • EOF:RDB 文件的 EOF 部分用于标识 RDB 正文内容的末尾,它的实际值为二进制值 0xFF。当 Redis 服务器读取到 EOF 的时候,它知道 RDB 文件的正文部分已经全部读取完毕了。CRC64 校验和 RDB 文件的末尾是一个以无符号 64 位整数表示的 CRC64 校验和,比如 5097628732947693614。Redis 服务器在读入 RDB 文件时会通过这个校验和来快速地检查 RDB 文件是否有出错或者损坏的情况出现。

1.2.1 数据库信息结构

上面说到 RDB 文件的数据库部分包含了任意多个数据库的数据,其中每个数据都由数据库号码、键值对总数量、带有过期时间的键值对数量、键值对数据部分。如下图:

  • 数据库号码:以数字的形式记录了数据的号码,例如 0。Redis 服务器在加载 RDB 文件时,会根据这个号码切换到对应的数据库,从而确保键值对会被载入正确的数据中。
  • 第二和第三部分:RDB 文件会使用两个数字,分别记录数据库包含的键值对总数量以及数据库中带有过期时间的键值对数量。Redis 服务器将根据这两个数字,以尽可能优化的方式创建数据库的内部数据结构。
  • RDB 文件将以无序方式记录数据库包含的所有键值对。具体来说,数据库中的每个键值对都会被划分为最多 5 个部分:

每个键值对开头的第一部分记录的是可能存在的过期时间,这是一个毫秒级精度的 UNIX 时间戳。 之后的 LRU 信息或者 LFU 信息分别用于实现可选的 LRU 算法或者 LFU 算法,并且因为 Redis 只能选择一种键淘汰算法,所以这两项信息将不会同时出现,最多只会出现其中一种。至于最后三个部分则分别记录了键值对的类型(比如字符串、列表、散列等)以及键和值。

1.2.2 RDB 文件载入流程

首先,当 Redis 服务器启动时,它会在工作目录中查找是否有 RDB 文件出现,如果有就打开它,然后读取文件的内容并执行以下载入操作:

  • 检查文件开头的标识符是否为"REDIS",如果是则继续执行后续的载入操作,不是则抛出错误并终止载入操作。
  • 检查文件的 RDB 版本号,以此来判断当前 Redis 服务器能否读取这一版本的 RDB 文件。
  • 根据文件中记录的设备附加信息,执行相应的操作和设置。
  • 检查文件的数据库数据部分是否为空,如果不为空就执行以下子操作:
    • 根据文件记录的数据库号码,切换至正确的数据库。
    • 根据文件记录的键值对总数量以及带有过期时间的键值对数量,设置数据库底层数据结构。
    • 一个接一个地载入文件记录的所有键值对数据,并在数据库中重建这些键值对。
  • 如果服务器启用了复制功能,那么将之前缓存的 Lua 脚本重新载入缓存中。
  • 遇到 EOF 标识,确认 RDB 正文已经全部读取完毕。
  • 载入 RDB 文件末尾记录的 CRC64 校验和,把它与载入数据期间计算出的 CRC64 校验和进行对比,以此来判断被载入的数据是否完好无损。
  • RDB 文件载入完毕,服务器开始接受客户端请求。

1.3 RDB 会造成数据丢失

RDB 文件记录的是服务器在开始创建文件的那一刻,服务器中包含的所有键值对数据,这种数据持久化方式通常被称为时间点快照(point-in-time snapshot)。时间点快照持久化的一个特点是,系统在停机时将丢失最后一次成功实施持久化之后的所有数据。对于一个只使用 RDB 持久化的 Redis 服务器来说,服务器停机时丢失的数据量将取决于最后一次成功执行的 RDB 持久化操作,以及该操作开始执行的时间。 因为 Redis 允许使用 SAVE 和 BGSAVE 这两种命令来执行 RDB 持久化操作,所以接下来将分别分析这两个命令在遭遇故障停机时的表现。

1.3.1 SAVE 命令停机情况

因为 SAVE 命令是一个同步操作,它的开始和结束都位于同一个原子时间之内,所以如果用户使用 SAVE 命令进行持久化,那么服务器在停机时将丢失最后一次成功执行 SAVE 命令之后产生的所有数据。

时间事件
T0服务器开始运行
T1服务器执行 set k1 v1
T2服务器执行 set k2 v2
T3服务器执行 SAVE 命令,成功创建 RDB 文件
T4服务器执行 set k3 v3
T5服务器执行 set k4 v4
T6服务器执行 SAVE 命令,成功创建 RDB 文件
T7服务器执行 set k5 v5
T8服务器执行 set k6 v6
T9服务器停机

因为服务器最后一次成功执行 SAVE 命令是在 T6,所以服务器创建的 RDB 文件包含 k1 至 k4 的数据在内的数据,因为最后一次执行 SAVE 的时间点是 T6,而 T9 时间点出现了停机,当服务器重启时 T7、T8 所操作的数据(k5 和 k6)将会丢失,而 k1 至 k4 的数据将会被恢复。

1.3.2 BGSAVE 命令停机情况

因为 BGSAVE 命令是一个异步命令,它的开始和结束并不位于同一个原子时间之内,所以如果用户使用 BGSAVE 命令进行持久化,那么服务器在停机时丢失的数据量将取决于最后一次成功执行的 BGSAVE 命令的开始时间。

时间事件
T0服务器开始运行
T1服务器执行 set k1 v1
T2服务器执行 set k2 v2
T3服务器执行 BGSAVE 命令,开始创建 RDB 文件
T4服务器执行 set k3 v3
T5RDB 文件创建完毕
T6服务器执行 set k4 v4
T7服务器执行 BGSAVE 命令,开始创建 RDB 文件
T8服务器执行 set k5 v5
T9服务器执行 set k6 v6
T10服务器停机

因为 T7 创建的新 RDB 文件尚未完成,所以服务器在停机后将使用 T5 成功创建的 RDB 文件进行数据恢复。虽然服务器现有的 RDB 文件是在 T5 成功创建的,但由于这个文件是 T3 开始创建的,所以只包含了 T3 之前的数据,即 k1 和 k2 的数据,其余数据将会被丢失。

2.AOF 持久化

与全量式的 RDB 持久方式不同,AOF 提供的是增量式的持久化功能,这种持久化的核心原理在于:服务器每次执行完命令后,都会以协议文件的方式将被执行的命令追加到 AOF 文件的末尾。这样一来,服务器在停机后,只要重新执行 AOF 文件中保存的 Redis 命令,就可以将数据库恢复至停机之前的状态。AOF 持久化很像 MySQL 的 biglog(mysql 的二进制日志),对比 RDB 持久化以某个周期创建整个 RDB 文件,AOF 持久化将要执行的命令追加到协议文件效率更高,所需的计算资源和内存资源更少,但由于 AOF 持久化是日志的形式追加到协议文件,相比较 RDB 持久化所产生的 RDB 文件体积会更大。AOF 文件的生成过程如下:

时间事件AOF 文件记录的命令
T0服务器启动(空白)
T1服务器执行命令 set k1 v1select 0
set k1 v1
T2服务器执行命令 set k2 v2select 0
set k1 v1
set k2 v2
T3服务器执行命令 rpush lst a b cselect 0
set k1 v1
set k2 v2
rpush lst a b c
T4服务器停机select 0
set k1 v1
set k2 v2
rpush lst a b c

从上面例子可以看出,随着服务器不断执行命令,被执行的命令也会不断被保存在 AOF 文件中(例子中 select 0 切换到用户正在使用的数据库的号码,这样在还原 AOF 文件时就知道还原到那个数据库了)。即使服务器在 T4 阶段停机,服务器在重启时通过重新执行 AOF 文件包含的命令来恢复数据。对于上面的例子来说服务器只需执行 AOF 文件中包含的 4 个命令即可让数据库重复回到停机之前的状态。为了理解 AOF 文件,上面例子都是直接写出被执行的命令,但在实际的 AOF 文件中,被执行的命令是以 Redis 网络协议的方式保存的。

2.1 AOF 配置

打开 AOF 持久化功能只需在 Redis 的配置文件通过配置 appendonly 选项为 yes,如果需要关闭 AOF 持久化设置 appendonly 选项为 no 即可。当 AOF 持久化功能处于打开状态时,Redis 服务器在默认情况下将会创建一个名为 appendonly.aof 文件作为 AOF 文件。

shell
# 打开AOF持久化
appendonly yes

# 关闭AOF持久化
appendonly no

2.1.1 设置 AOF 文件冲洗频率

为了提高程序的写入性能,现代化的操作系统通常会把针对硬盘的多次写操作优化为一次写操作。具体的做法是,当程序调用 write 系统调用对文件进行写入时,系统并不会直接把数据写入硬盘,而是会先将数据写入位于内存的缓冲区中,等到指定的时限到达或者满足某些写入条件时,系统才会执行 flush 系统调用,将缓冲区中的数据冲洗至硬盘。 这种优化机制虽然提高了程序的性能,但是也给程序的写入操作带来了不确定性,特别是对于 AOF 这样的持久化功能来说,AOF 文件的冲洗机制将直接影响 AOF 持久化的安全性。为了消除上述机制带来的不确定性,Redis 向用户提供了 appendfsync 选项,以此来控制系统冲洗 AOF 文件的频率:

shell
appendfsync <value>

appendfsync 选项拥有 always、everysec、no 三个可选值,Redis 使用 everysec 作为 appendfsync 选项的默认值。它们的作用如下:

  • always:没执行一个命令就对 AOF 文件执行一次冲洗操作。在使用 always 值的情况下,服务器在停机时最多只会丢失一个命令的数据,但使用这种冲洗方式很消耗性能。
  • everysec:每隔 1s 就会 AOF 文件执行一次冲洗操作。在使用 everysec 值的情况下,服务器在停机时最多只会丢失 1s 内产生的命令数据,这是一种兼顾性能和安全性的折中方案,Redis 使用 everysec 作为 appendfsync 选项的默认值。
  • no:不主动对 AOF 文件执行冲洗操作,由操作系统决定何时对 AOF 进行冲洗。在使用 no 值的情况下,可能丢失的数据具有不确定性。

2.1.2 AOF 重写

随着服务器不断执行,被执行的命令也会越来越多,而负责记录这个命令的 AOF 文件体积也会越来越大。如果服务器曾经对相同的 key 执行过多次修改操作,那么 AOF 文件中还会出现大量冗余的命令,例如下面的例子:

shell
select 0
set msg "zxp"
set msg "zxp11"
set msg "zxp22"

上面这些命令最终修改可简化为以下命令:

shell
select 0
set msg "zxp22"

冗余的命令不仅增加了 AOF 文件的体积,AOF 文件越来越大时 Redis 服务器恢复 AOF 文件的数据时消费的时间也会越来越多。为了减少冗余命令和 AOF 文件体积,提升恢复 AOF 文件执行速度,Redis 提供了 AOF 重写功能,该功能能够生成一个全新的 AOF 文件,并且该文件只包含恢复当前数据库所需的尽可能少的命令。开启 AOF 重写功能有两种方式:

  • 通过 BGREWRITEAOF 命令来显示地触发 AOF 重写操作。
  • 通过在 Redis 配置文件配置 AOF 相关选项来触发 AOF 重写操作。

BGREWRITEAOF 命令是一个异步命令,它没有任何参数,Redis 服务器在接收到该命令之后会创建一个子进程,由它扫描整个数据库并生成新的 AOF 文件。当新的 AOF 文件生成完毕,子进程就会退出并通知 Redis 服务器(父进程),然后 Redis 服务器就会使用新的 AOF 文件代替已有的 AOF 文件,借此完成整个重写操作。关于 BGREWRITEAOF 还有两点需要注意:首先,如果用户发送 BGREWRITEAOF 命令请求时,服务器正在创建 RDB 文件,那么服务器将把 AOF 重写操作延后到 RDB 文件创建完毕之后再执行,从而避免两个写硬盘操作同时执行导致机器性能下降;其次,如果服务器在执行重写操作的过程中,又接收到了新的 BGREWRITEAOF 命令请求,那么服务器将返回以下错误:

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

除了显示的使用 BGREWRITEAOF 命令,还可以在 Redis 配置文件通过设置以下两个配置选项让 Redis 自动触发 BGREWRITEAOF 命令:

shell
auto-aof-rewrite-mini-size <value>
auto-aof-rewrite-percentage <value>
  • auto-aof-rewrite-mini-size:选项用于设置触发自动 AOF 文件重写所需的最小 AOF 文件体积,当 AOF 文件体积小于给定值时,服务器将不会自动执行 BGREWRITEAOF 命令。默认情况 auto-aof-rewrite-mini-size 的值为 64mb。auto-aof-rewrite-mini-size 64mb表示如果 AOF 文件体积小于 64MB,那么将不会自动执行 BGREWRITEAOF 命令。
  • auto-aof-rewrite-percentage:此选项用于控制触发自动 AOF 文件重写所需的文件体积增大比例。默认情况 auto-aof-rewrite-mini-size 的值为 100,表示如果当前 AOF 文件的体积比最后一次 AOF 文件重写之后的体积增大了一倍(100%),那么将自动执行一次 BGREWRITEAOF 命令。如果 Redis 服务器刚刚启动,还没有执行过 AOF 文件重写操作,那么启动服务器时使用的 AOF 文件的体积将被用作最后一次 AOF 文件重写的体积。举个例子,如果服务器启动时 AOF 文件的体积为 200MB,而 auto-aof-rewrite-percentage 选项的值为 100,那么当 AOF 文件的体积增大至超过 400MB 时,服务器就会自动进行一次 AOF 重写。与此类似,在同样设置下,如果 AOF 文件的体积从最后一次重写之后的 300MB 增大至超过 600MB,那么服务器将再次执行 AOF 重写操作。

3.RDB 持久化与 AOF 持久化的优缺点

RDB 持久化是一种全量持久化,它在创建 RDB 文件时需要存储整个服务器包含的所有数据,并因此消耗大量计算资源和内存资源,所以通过增大 RDB 文件的生成频率来保证数据安全不是一种好的方案。其优缺点如下:

  • 生成的持久化文件体积更小。使用 RDB 持久化的方式生成的 RDB 文件是一个经过压缩的二进制文件,相比较 AOF 持久化方式,生成的持久化文件体积更小。

  • 在恢复大数据集时速度更快,因为体积小所以 Redis 在恢复 RDB 文件时恢复数据的速度更快(相比较 AOF)。

  • RDB 持久化方式易丢失数据,RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,若创建 RDB 文件的期间发生故障,那么这段的所操作的数据都可能被丢失。

  • RDB 会阻塞服务器。由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。

AOF 提供的是增量式的持久化功能,这种持久化的核心原理在于:服务器每次执行完命令后,都会以协议文件的方式将被执行的命令追加到 AOF 文件的末尾。 其优缺点如下:

  • AOF 持久化方式不易丢失数据,且持久化消耗资源相比较 RDB 持久化方式少很多。AOF 通过协议文件追加的形式将被执行的命令追加到 AOF 文件末尾,即使服务器中途出现故障也只会丢失少量数据,RDB 持久化以某个周期创建整个 RDB 文件,创建 RDB 文件过程会消耗大量计算、内存资源。
  • AOF 文件体积相比较 RDB 文件大很多。因为 AOF 文件存储的是协议文件,所以它的体积会比包含相同数据、二进制格式的 RDB 文件要大很多,因为 AOF 文件体积大,所以生成 AOF 文件所需的时间也会比生成 RDB 文件所需的时间更长。
  • RDB 持久化的恢复速度比 AOF 持久化的恢复速度快很多。因为 RDB 持久化可以直接通过 RDB 文件恢复数据库,而 AOF 持久化则需要通过执行 AOF 文件中保存的命令来恢复数据库(RDB 属于直接恢复数据库,而 AOF 属于间接的恢复数据库),所以 RDB 持久化的恢复速度比 AOF 持久化的恢复速度快很多,并且体积越大越明显。

4.RDB-AOF 混合持久化

RDB 和 AOF 这两种持久化方式即有优点又有缺点,为了一个折中的持久化方案,Redis 在 4.0 版本引入了 RDB-AOF 混合持久化模式,这种模式是基于 AOF 持久化模式构建而来,如果打开了服务器的 AOF 持久化功能,并 Redis 配置文件配置了 aof-use-rdb-preamble 为 yes 即可开启 RDB-AOF 混合持久化模式。

shell
aof-use-rdb-preamble yes

当使用上面命令配置时,Redis 服务器在执行 AOF 重写时,就会像执行 BGSAVE 命令那样,根据数据库当前的状态生成出相应的 RDB 数据,并将这些数据写入新建的 AOF 文件中,至于那些在 AOF 重写开始之后执行的 Redis 命令,则会继续以协议文本的方式继续追加到 AOF 文件的末尾,即已有 RDB 数据的后面。 换句话说,在开启了 RDB-AOF 混合持久化功能之后,服务器生成的 AOF 文件将由两个部分组成,其中位于 AOF 文件开头的是 RDB 格式的数据,而跟在 RDB 数据后面的则是 AOF 格式的数据。 当一个支持 RDB-AOF 混合持久化模式的 Redis 服务器启动并载入 AOF 文件时,它会检查 AOF 文件的开头是否包含了 RDB 格式的内容:

  • 如果包含.那么服务器就会先载入开头的 RDB 数据,然后再载入之后的 AOF 数据。
  • 如果 AOF 文件只包含 AOF 数,那么服务器将直接载入 AOF 数据。

通过使用 RDB-AOF 混合持久化功能,用户可以同时获得 RDB 持久化和 AOF 持久化的优点:服务器既可以通过 AOF 文件包含的 RDB 数据来实现快速的数据恢复操作,又可以通过 AOF 文件包含的 AOF 数据来将丢失数据的时间窗口限制在 1s 之内。 需要注意的是,因为 RDB-AOF 混合持久化生成的 AOF 文件会同时包含 RDB 格式的数据和 AOF 格式的数据,而传统的 AOF 持久化只会生成包含 AOF 格式的数据,所以为了避免全新的 RDB-AOF 混合持久化功能给传统的 AOF 持久化功能使用者带来困惑,Redis 目前默认是没有打开 RDB-AOF 混合持久化功能的:

shell
aof-use-rdb-preamble no

5.Redis 持久化机制总结

  • Redis 主要提供 RDB 和 AOF 两种持久化方式,Redis 使用 RDB 持久化作为默认持久化功能。RDB 持久化方式能够在指定的时间间隔能对数据进行快照存储。RDB 持久化功能会创建一个经过压缩的二进制快照文件。

  • AOF 持久化方式其工作原理是将执行完毕的命令以协议文本的方式追加到 AOF 文件末尾,当执行的命令越来越多时 AOF 文件体积也会越来越大,但向 AOF 文件末尾追加执行完毕的命令的所占开销相比较 RDB 持久化方式低。

  • RDB 会阻塞服务器。由于 RDB 是通过父进程 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。

  • RDB 持久化的优点:

    • RDB 持久化生成的 RDB 文件体积相比较 AOF 持久化生成的 AOF 文件小。因为 AOF 持久化是通过协议文本追加的形式将执行的命令追加到 AOF 文件末尾,追加命令越多 AOF 文件体积越大(AOF 文件可能存在大量冗余命令)。而 RDB 持久化生成的 RDB 文件是经过压缩的二进制文件,所以体积相比较 AOF 文件要小很多。
    • RDB 持久化相比较 AOF 持久化恢复速度快。因为 RDB 文件体积比 AOF 体积小很多所以恢复速度也会快很多,尤其是在体积特别明显的情况下。
  • RDB 持久化的缺点:

    • RDB 持久化易丢失数据。RDB 持久化方式能够在指定的时间间隔能对数据进行快照存储,所以会导致部分数据丢失。
    • RDB 持久化会阻塞服务器。由于 RDB 是通过父进程 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。而且 RDB 持久化生成 RDB 文件相比较 AOF 持久化生成 AOF 文件开销高很多。
  • AOF 持久化的优点:

    • 不易丢失数据。AOF 持久化是通过协议文本追加的形式将执行的命令追加到 AOF 文件末尾,所以不易丢失数据。
    • 创建 AOF 文件无需大量开销。

AOF 持久化的缺点:

  • AOF 文件体积相比较 RDB 文件大很多。因为 AOF 文件存储的是协议文件,所以它的体积会比包含相同数据、二进制格式的 RDB 文件要大很多,因为 AOF 文件体积大,所以生成 AOF 文件所需的时间也会比生成 RDB 文件所需的时间更长。
  • RDB 持久化的恢复速度比 AOF 持久化的恢复速度快很多。因为 RDB 持久化可以直接通过 RDB 文件恢复数据库,而 AOF 持久化则需要通过执行 AOF 文件中保存的命令来恢复数据库(RDB 属于直接恢复数据库,而 AOF 属于间接的恢复数据库),所以 RDB 持久化的恢复速度比 AOF 持久化的恢复速度快很多,并且体积越大越明显。
  • 如果即想拥有 RDB 的优点又想拥有 AOF 的优点,那么 RDB-AOF 混合持久化是一种折中方案。首先要开启 AOF 持久化功能,然后在 Redis 配置文件中将 aof-use-rdb-preamble 选项设置为 yes,Redis 默认设置为 no。

Released under the MIT License.