Redis学习手册14—持久化

正如之前所说的,Redis与传统数据库的一个主要区别在于,Redis把所有数据都存储在内存中,而传统数据库通常只会把数据的索引存储在内存中,并将实际的数据存储在硬盘中。虽然Redis这样的数据存储方式使得用户可以极快的访问数据,但是由于内存属于易失存储器,这对于想把Redis当做数据库而不仅仅是缓存的用户来说是不愿看到的。

为了解决上述问题,Redis向用户提供了持久化功能,这一功能把内存中的数据以文件的形式存储到硬盘上,即可保证数据不会轻易丢失。

Redis持久化分类

为了满足不同的持久化需求,Redis提供了:RDB持久化AOF持久化RDB-AOF混合持久化 等多种持久化方式。

RDB持久化

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

创建RDB文件

Redis提供了多种创建RDB文件的方法,用户既可以使用 SAVE 命令或者 BGSAVE命令手动创建RDB文件,也可以通过设置 save 配置选项让服务器在满足条件时自动执行 BGSAVE 命令。

SAVE:阻塞服务器并创建RDB文件

使用SAVE命令,要求Redis服务器以同步方式创建一个记录了服务器当前所有数据库数据的RDB文件。SAVE 命令是一个无参数命令,它在创建RDB文件成功后返回OK:

redis> SAVE
OK

接收到SAVE命令的Redis服务器将遍历数据库包含的所有数据库,并将各个数据库包含的键值对全部记录到RDB文件中。在SAVE命令执行期间,Redis服务器被阻塞,直到RDB文件创建完成为止。如果已有RDB文件,则会使用新的RDB文件替换旧的RDB文件。

BGSAVE:以非阻塞方式创建RDB文件

因为SAVE命令在执行期间会阻塞服务器,为了解决这个问题,Redis提供了异步版本 BGSAVE 命令:这个命令会使用子进程来创建RDB文件。

  1. 创建一个子进程。
  2. 子进程执行SAVE命令,创建新的RDB文件。
  3. RDB文件创建完毕之后,子进程退出并通知Redis服务器进程。
  4. Redis服务器进程使用新的RDB文件替换已有的RDB文件。

因为BGSAVE命令是以异步方式执行的,所以Redis服务器在BGSAVE命令执行期间仍然可以继续处理其他客户端发送的命令请求。不过需要注意的是,虽然BGSAVE命令不会像SAVE命令那样一直阻塞Redis服务器,但由于执行BGSAVE命令需要创建子进程,因此父进程所占内存越大,数据量越大,创建子进程的过程越慢,因此也有可能会有短暂的阻塞。

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

除了可以使用 SAVE命令或者BGSAVE命令之外,还可以通过设置 save 选项,让Redis服务器在满足指定条件时自动执行 BGSAVE命令:

save <seconds> <changes>

save 选项接受 secondschanges 两个参数,前者用于指定触发持久化所需要的时长,后者则用于指定触发持久化操作所需要的修改次数。即是说,如果服务器在 seconds 秒之内,对其包含的各个数据库总共执行了 changes 次修改,那么服务器就会触发一次 BGSAVE 命令:

save 60 10000  -- 60s内至少执行了10000次修改

Redis允许用户同时向服务器提供多个 save 选项,当给定的选项中任意一个条件被满足时,就会执行一次 BGSAVE 命令

save 6000 1    -- 在6000s(100min)之内,修改了至少 1次
save 600 100   -- 在600s(10min)之内,修改了至少100次
save 60s 10000   -- 在60s(1min)之内,修改了至少 10000次

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

RDB持久化是默认的持久化方式,如果用户在启动Redis时,没有显示的关闭RDB持久化功能,也没有启用 AOF 持久化功能,那么Redis默认将使用如下默认配置进行 RDB 持久化:

save 60 10000
save 300 100
save 3600 1

RDB文件结构

在了解了如何创建RDB文件之后,接下来了解一下RDB文件的具体结构。

总体结构

RDB文件总体结构主要包含7个部分:

  • RDB文件标识符: 文件最开头的部分为RDB文件标识符,这个标识符的内容为 “REDIS” 这5个字符。Redis服务器在尝试载入RDB文件时,可以通过这个标识符快速的判断文件是否为真正的RDB文件。
  • 版本号: 跟在RDB文件标识符之后的是RDB文件的版本号,这个版本号是一个字符串格式的数字,长度为4个字符。如最新版的:“0009”
  • 设备附加信息: RDB文件的设备附加信息部分记录了生成RDB文件的Redis服务器及其所在平台的信息,比如服务器的版本号、宿主机器的架构、创建RDB文件的时间戳、服务器占用的内存数量等。
  • 数据库数据: RDB文件的数据库数据部分记录了Redis服务器存储的0个或任意多个数据库的数据,当这个部分包含多个数据库时,各个数据库的数据将按照数据库号码从小到大进行排列。
  • Lua脚本缓存: 如果Redis服务器启用了复制功能,那么服务器将在RDB文件的Lua脚本缓存部分保存所有已被缓存的Lua脚本。
  • EOF: RDB文件的EOF部分用于标识RDB正文内容的末尾,它实际值为二进制值 0xFF
  • CRC64校验和: RDB文件的末尾是一个以无符号64位整数表示的CRC64校验和,通过这个值可以快速校验出RDB文件是否有损坏。

数据库信息结构

RDB文件的数据库数据部分包含了任意多个数据库的数据,其中每个数据库都包含了:

  • 数据库号码
  • 键值对总数量
  • 带有过期时间的键值对数量
  • 键值对数据部分

载入RDB文件

Redis载入RDB文件的步骤如下:

  1. 检查文件开头的标识符是否为 “REDIS”,如果是则继续执行后续步骤,否则报错并终止载入操作。

  2. 检查文件的RDB版本号,以此来判断当前Redis服务器能否读取这一版本的RDB文件。

  3. 根据文件中记录的设备附加信息,执行相应的操作和设置。

  4. 检查文件的数据库数据部分是否为空,如果不为空就执行以下子操作:
    ① 根据文件记录的数据库号码,切换至正确的数据库。
    ② 根据文件记录的键值对总量以及带有过期时间的键值对数量,设置数据库底层数据结构。
    ③ 载入文件记录的所有键值对数据,并在数据库中重建这些键值对。

  5. 如果服务器启用了复制功能,那么将之前缓存的Lua脚本重新载入缓存中。

  6. 遇到EOF表示,确认RDB正文内容读取完毕。

  7. 载入RDB文件末尾的CRC64校验和,把它与载入数据期间计算出的CRC64校验和进行对比,以此来判断被载入的数据是否完好无损。

  8. RDB文件载入完毕,服务器开始接受客户端请求。

数据丢失

由于RDB持久化方式记录的是开始执创建文件的那一刻,数据库中包含的所有数据,这种方式通常也叫做时间点快照。时间快照的特点是:系统在停机时将丢失最后一次成功实施持久化之后的所有数据

因此,对于一个只是用RDB持久化的Redis服务器来说,服务器停机时丢失的数据量将取决于最后一次成功执行RDB持久化操作,以及该操作开始执行的时间。

SAVE命令和BGSAVE命令对于故障停机时的表现也不同:

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

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

RDB持久化的缺陷

总的来说,无论用户使用的是SAVE命令还是BGSAVE命令,停机服务器丢失的数据量将取决于创建RDB文件的时间间隔:间隔越长,丢失的数据量越大

矛盾的地方在于,RDB持久化是一种全量持久化操作,它在创建RDB文件时需要存储整个服务器包含的所有数据,并因此消耗大量的计算资源和内存资源,所以也不太可能通过增大RDB文件的生成频率来保证数据安全。

RDB持久化的特征决定了,它更适合用于数据备份,而非普通的数据持久化。

AOF持久化

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

打开AOF持久化功能

默认情况下,Redis的AOF持久化功能并未开启,用户可以通过 appendonly 选项来决定是否开启AOF持久化功能:

appendonly yes -- 开启AOF持久化
appendonly no -- 关闭AOF持久化

设置AOF文件的冲洗频率

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

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

appendfsync <value>

appendfsync 选项拥有如下3个值可选:

  • always —— 每执行一个写命令,就对AOF文件执行一次冲洗操作
  • everysec —— 每隔1s,就对AOF文件执行一次冲洗操作
  • no —— 不主动对AOF文件执行冲洗操作,由操作系统来决定何时执行AOF冲洗操作。

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

  • 在使用 always 的情况下,服务器在停机时最多丢失一个命令的数据,但是频率太高,会降低Redis服务器的性能。
  • 使用 everysec 的情况下,服务器在停机时最多丢失1s内产生的数据,这是一种兼顾性能和安全的这种方案,也是默认的策略。
  • 使用 no 的情况下,停机时将丢失最后一次冲洗AOF文件之后的所有数据,具体大小则取决于系统对AOF文件冲洗的频率。

AOF重写

随着服务器的不断运行,被执行的命令越来越多,而负责记录这些命令的AOF文件也会越来越大。为了保证AOF文件不会过大,Redis提供了AOF重写功能,该功能可以生成一个新的AOF文件,并且文件中只包含恢复当前数据库所需的尽可能少的命令。

BGREWRITEAOF命令

执行BGREWRITEAOF命令可以显示的触发AOF重写操作:

redis> BGREWRITEAOF
Background append only file rewriting started

BGREWRITEAOF命令是一个异步命令,Redis服务器在接收到该命令之后会创建出一个子进程,它会扫描数据库并生成新的AOF文件。当新的AOF文件生成后,子进程就会退出并通知Redis服务器,然后Redis服务器就会使用新的AOF文件替换旧的AOF文件。

  • 如果用户发送BGREWRITEAOF命令请求时,服务器正在创建RDB文件,那么服务器将把AOF重写操作延后到RDB文件创建完毕之后再执行。
  • 如果服务器在执行重写操作的过程中,再次收到新的BGREWRITEAOF命令请求,那么服务器将返回错误。

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将不会自动执行BGREWRITEAOF命令。

auto-aof-rewrite-percentage选项控制的是触发自动AOF文件重写所需的文件体积增大比例,默认值如下:

auto-aof-rewrite-percentage 100

表示当前AOF文件的体积比最后一次AOF重写之后的体积增大了一倍(100%),那么将自动执行一次BGREWRITEAOF命令。

AOF持久化的优缺点

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

AOF持久化的缺点如下:

  • 首先,因为AOF文件存储的是协议文本,所以它的体积会比包含相同数据的二进制格式的RDB文件要大得多,并且生成AOF文件所需的时间也会比生成RDB文件所需的时间长。
  • 其次,因为RDB持久化可以直接通过RDB文件恢复数据库数据,而AOF持久化则需要通过执行AOF文件中保存的命令来恢复数据库。所以RDB持久化的数据恢复速度将比AOF持久化的速度快的多。
  • 最后,因为AOF重写使用的BGREWRITEAOF命令与RDB持久化使用的BGSAVE命令一样都需要创建子进程,所以在数据库体积较大的情况下,进行AOF文件重写将占用大量资源,并导致服务器被短暂的阻塞。

RDB-AOF混合持久化

为了兼顾RDB持久化和AOF持久化各自的优点,Redis从4.0版本开始引入了RDB-AOF混合持久化模式,这种模式是基于AOF持久化模式构建而来——如果用户打开了AOF持久化,并且将

aof-use-rdb-preamble <value>

选项的值设置成了 yes,那么Redis服务器在执行AOF重写的时候,就会像执行BGSAVE命令那样,根据数据库当前的状态生成出相应的RDB数据,并将这些数据写入AOF文件中,至于那些AOF重写开始之后执行的Redis命令,则会继续以协议文本的形式追加到新AOF文件的末尾。

也就是说,在开启了RDB-AOF混合持久化功能后,服务器生成的AOF文件将由两部分组成,其中位于AOF文件头部的是RDB格式的数据,而跟在RDB数据后面的则是AOF格式的数据。

关闭持久化

默认情况下,即使用户没有显示的开启RDB持久化或者AOF持久化,Redis也会默认使用以下配置进行RDB持久化:

save 60 10000
save 300 100
save 3600 1

如果用户想要彻底关闭这一默认的RDB持久化行为,那么可以在Redis服务器启动时,给出以下配置即可:

save ""

上一篇:Redis学习手册13—Lua脚本

下一篇:Redis学习手册15—发布与订阅

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章