redis 持久化

Jackey Redis 355 次浏览 , 没有评论

持久化

因为 Redis 服务器将数据储存在内存里面,而一旦服 务器被关闭、或者运行服务器的主机本身被关闭的话,储存在内存里面的数据就会消失不 见。

如果我们仅仅是将 Redis 用作缓存的话,那么这种数据丢失带来的问题并不是非常大,我们只需要重启机器,然后再次将数据放到 缓存里面就可以了;但如果我 们将 Redis 用作数据库的话,那么这种数据丢失就不能接受了。

为了在 Redis 服务器关闭时,仍然保留数据库中的数据,Redis 提供了 RDB 和 AOF 两种持久化功能,这两种功能可以将储存在内存里面的数据库数据以文件的形式保存到硬 盘里面,这样的话,即使服务器关闭,已经保存到硬盘里面的数据也不会丢失。

除此之外,服务器也可以在重新启动时,通过载入持久化文件来还原服务器在关闭之前的数据库数据。或者使用持久化文件来 进行数据备份、数据迁移等工作。

RDB持久化

RDB 持久化功能可以将服务器包含的所有数据库数据以二进制文件的形式保存到硬 盘里面。

而通过在服务器启动时载入 RDB 文件,服务器可以根据 RDB 文件的内容,还原服务器原有的数据库数据。

RDB 文件

那么 Redis 服务器在什么时候才会创建 RDB 文件呢?

在 Redis 服务器创建 RDB 文件的情况中,以下三种是最常 见的:

  1. 服务器执行客户端发送的 SAVE 命令;
  2. 服务器执行客户端发送的 BGSAVE 命令;
  3. 使用 save 配置选项设置的自动保存条件被满足,服务器自动执行 BGSAVE 。

在这三种创建 RDB 文件的情况中,前两种情况需要用 户手动执行,而第三种情况则是由 Redis 服务器自动执行。

接下来的内容将详细地介绍这三种情况的相同和不同之 处。

SAVE 命令

通过使用客户端向服务器发送 SAVE 命令,可以命令服务器去创建一个新的 RDB 文件:

redis> SAVE

OK

在执行 SAVE 命令的过程中(也即是创建 RDB 文件的过程中),Redis 服务器将被阻塞,无法处理客户端发送的命令请求,只有在 SAVE 命令执行完毕之后(也即是 RDB 文件创建完毕之后),服务器才会重新开始处理客户端发送的命令请求。

如果 RDB 文件已经存在,那么服务器将自动使用新的 RDB 文件去代替旧的 RDB 文件。

SAVE 命令的复杂度为 O(N) ,N 为服务器中,所有数据库包含的键值对数量总和。

BGSAVE 命令

执行 BGSAVE 命令同样可以创建一个新的 RDB 文件,这个命令和 SAVE 命令的区别在于,BGSAVE不会造成 Redis 服务器阻塞:在执行 BGSAVE 命令的过程中, Redis 服务器仍然可以正常地处理其他客户端发送的命令请求。

BGSAVE 命令不会㐀成服务器阻塞的原因在于:

  1. 当 Redis 服务器接收到 BGSAVE 命令的时候,它不会自己来创建 RDB 文件,而是通过 fork() 来生成一个子进程,然后由子进程负责创建 RDB 文件,而自己则继续处理客户端的命令请求;
  1. 当子进程创建好 RDB 文件并退出时,它会向父进程(也即是负责处理命令请求的 Redis 服务器)发送一个信号,告知它 RDB 文件已经创建完毕;
  1. 最后 Redis 服务器(父进程)接收子进程创建的 RDB 文件,BGSAVE 执行完毕。

BGSAVE 是一个异步命令,发送命令的客户端会立即得到回复,而 实际的操作在回复之后才开始:

redis> BGSAVE

Background saving started

BGSAVE 命令的复杂度和 SAVE 一样,都是 O(N) ,N 为所有数据库包含的键值对数量总和。

SAVE BGSAVE 的区

命令SAVEBGSAVE
类型同步操作异步操作
是否会导致服务器阻塞?
复杂度O(N)O(N)
优点不需要创建子进程,不会消耗额外的内存,可以集中资源来创建RDB 文件,所以 SAVE 创建
RDB 文件的㏿度会比 BGSAVE 快。
使得 Redis 服务器可以在创建 RDB 文件的过程中,仍然正常地处理客户端的命令请求。
缺点执行期间会阻塞服务器。需要创建子进程,会耗费额外的内存。

SAVE 和 BGSAVE 没有孰好孰坏之分,需要考 虑的是哪个更适合你。

RDB 持久化

以上提到的 SAVE 和 BGSAVE 都是手动操作,它们都需要用户介入。

为了让 Redis 服务器可以自动进行 RDB 持久化操作,Redis 提供了 save 配置选项,通过这个选项,用户可以设置任意多个保存条件,每当保存条件中的任意一个被 满足时,服务器就会自动执行 BGSAVE命令。

save 选项的格式为:

save <seconds> <changes>

这个选项的效果为:如果距离上一次创建 RDB 文件已经过去了 seconds 秒,并且服务器的所有数据库总共已经发生了不少于 changes 次修改(包括添加、删除和更新),那么执行 BGSAVE 。

举个例子,设置:

save 300 10

表示“如果距离上一次创建 RDB 文件已经过去了 300 秒,并且服务器的所有数据库总共已经发生了不少于 10 次修改,那么执行 BGSAVE 命令”。

而设置:

save 60 10000

则表示“如果距离上一次创建 RDB 文件已经过去了 60 秒,并且服务器的所有数据库总共已经发生了不少于 10000 次修改,那么执行 BGSAVE 命令”。

另外,用户还可以通过设置多个 save 选项来设置多个自动保存条件,当任意一个条件被满足时,服务器就会自动执行 BGSAVE 命令。

举个例子,对于以下设置来说:

save 900 1

save 300 10

save 60 10000

只要三个条件中的任意一个被 满足时,服务器就会执行 BGSAVE 。

注意:每次创建 RDB 文件之后,服务器为实现自动持久化而设置的时间计数器和次数计数器就会被清零,并重新开始计数,所以多个保存条件的效果是不会叠加的。

RDB 持久化的缺点

RDB 持久化有一个缺点,那就是,因 为创建 RDB 文件需要将服务器所有数据库的数据都保存起来,这是一个非常耗费资源和时间的操作,所以服务器需要隔一段时间才创建一个新的 RDB 文件,也即是说,创建 RDB 文件的操作不能执行得过于频繁,否则就会严重地影响服务器的性能。

比如说,在 save 配置选项的默认设置下,即使有超过 10000 次修改操作发生,服务器也至少会间隔一分钟才创建下一个 RDB 文件:

save 900 1

save 300 10

save 60 10000

如果在等待创建下一个 RDB 文件的过程中,服务器遭遇了意外停机,那么用 户将丢失最后一次创建RDB 文件之后,数据库发生的所有修改。

解决方法

为了解决 RDB 持久化在遭遇意外停机 时丢失大量数据的问题,Redis 提供了 AOF 持久化功能。

比起 RDB 持久化, AOF 持久化有一个巨大的优势,那就是,用户可以根据自己的需要对 AOF 持久化进行调整,让 Redis 在遭遇意外停机时不丢失任何数据,或者只丢失一秒钟数据,这比 RDB 持久化遭遇意外停机时,丢失的数据要少得多。

AOF 持久化

AOF 持久化保存数据库数据的方法是:每当有修改数据 库的命令被执行时,服务器就会将被执行的命令写入到 AOF 文件的末尾。

因为 AOF 文件里面储存了服务器执行过的所有数据库修改命令,所以给定一个 AOF 文件,服务器只要重新执行一遍 AOF 文件里面包含的所有命令,就可以达到 还原数据库数据的目的。

举个例子,对于包含以下内容的 AOF 文件来说:

SELECT 0

SET msg “hello”

INCR counter

SADD alphabets “a” “b” “c”

INCR counter

服务器只要重新执行这些命令,就可以还原数据库。

AOF 持久化的安全性问题

虽然服务器每执行一个修改数据库的命令,就会将被执行的命令写入到 AOF 文件,但这并不意味着AOF 持久化不会丢失任何数据。

在目前常见的操作系统中,执行系统调用 write 函数,将一些内容写入到某个文件里面 时,为了提高效率,系统通常不会直接将内容写入到硬 盘里面,而是先将内容放入到一个内存 缓冲区(buffer)里面,等到缓冲区被填满,或者用户执行 fsync 调用和 fdatasync 调用时,才将储存在缓冲区里面的内容真正地写入到硬盘里面。

对于 AOF 持久化来说,当一条命令真正地被写入到硬 盘里面时,这条命令才不会因为停机而意外丢失。

因此,AOF 持久化在遭遇停机时丢失命令的数量,取决于命令被写入到硬 盘的时间:

  • 越早将命令写入到硬盘,发生意外停机时丢失的数据就越少;
  • 而越迟将命令写入到硬盘,发生意外停机时丢失的数据就越多。

安全性控制

为了控制 Redis 服务器在遇到意外停机时丢失的数据量,Redis 为 AOF 持久化提供了 appendfsync选项,这个选项的值可以是 always 、 everysec 或者 no ,这些值的意思分别为:

  • always :服务器每写入一个命令,就 调用一次 fdatasync ,将缓冲区里面的命令写入到硬 盘里面。在这种模式下,服务器即使遭遇意外停机,也不会 丢失任何已经成功执行的命令数据。
  • everysec :服务器每秒钟调用一次 fdatasync ,将缓冲区里面的命令写入到硬 盘里面。在这种模式下,服务器遭遇意外停机时,最多只丢失一秒钟内执行的命令数据。
  • no :服务器不主动调用 fdatasync ,由操作系统决定何时将缓冲区里面的命令写入到硬 盘里面。在这种模式下,服务器遭遇意外停机时,丢失命令的数量是不确定的。

运行速度: always 的速度慢, everysec 和 no 都很快。

AOF 文件中的冗余命令

随着服务器的不断运行,为了记录数据库发生的变化,服务器会将越来越多的命令写入到 AOF 文件里面,使得 AOF 文件的体积不断地增大。

为了让 AOF 文件的大小控制在合理的范 围,避免它胡乱地增长,Redis 提供了 AOF 重写功能,通过这个功能,服务器可以产生一个新的 AOF 文件:

  • 新 AOF 文件记录的数据库数据和原有 AOF 文件记录的数据库数据完全一样;
  • 新的 AOF 文件会使用尽可能少的命令来 记录数据库数据,因此新 AOF 文件的体积通常会比原有 AOF 文件的体积要小得
  • AOF 重写期间,服务器不会被阻塞,可以正常 处理客户端发送的命令请求。

来看一个 AOF 重写的示例。

原有的AOF文件:

SELECT 0

SADD fruits “apple”

SADD fruits “banana”

SADD fruits “cherry”

SADD fruits “apple”

INCR counter

INCR counter

DEL counter

SET msg “hello world”

SET msg “goodbye”

SET msg “hello world again!”

RPUSH lst 1 3 5

RPUSH lst 7 9

LPOP lst

RPOP lst

重写后的AOF文件:

SELECT 0

SADD fruits “apple” “banana” “cherry”

SET msg “hello world again!”

RPUSH lst 3 5 7

AOF 重写的触

有两种方法可以触发 AOF 重写:

  1. 客户端向服务器发送 BGREWRITEAOF 命令。
  2. 通过设置配置选项来让服务器自动执行 BGREWRITEAOF 命令,它们分别是:
  • auto-aof-rewrite-min-size <size> ,触发 AOF 重写所需的最小体积:只有在 AOF 文件的体积大于等于 size 时,服务器才会考虑是否需要进行 AOF 重写。这个选项用于避免对体积过小的AOF 文件进行重写。
  • auto-aof-rewrite-percentage <percent> ,指定触发重写所需的 AOF 文件体积百分比:当 AOF文件的体积大于 auto-aof-rewrite-min-size 指定的体积,并且超过上一次重写之后的 AOF 文件体积的 percent% 时,就会触发 AOF 重写。(如果服务器刚刚启动不久,还没有进行过 AOF 重写,那么使用服务器启动时载入的 AOF 文件的体积来作为基准值。)将这个值设置为 0 表示关闭自动 AOF 重写。

例子:

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

持久化

RDB持久化AOF持久化
全量备份,一次保存整个数据 库。增量备份,一次保存一个修改数据 库的命令。
保存的间隔较长。保存的间隔默认为一秒钟。
数据还原速度快。数据还原速度一般。冗余命令越多, 还原速度越慢。
执行 SAVE 命令时会阻塞服务器,但手动或者自动触发的 BGSAVE 都不会阻塞服务器。无论是平时还是进行 AOF 重写时,都不会阻塞服务器。
更适合数据备份。更适合用来保存数据,通常意 义上的数据持化。
在 appendfsync always 模式下运行时,Redis 的持久化方式和一般的 SQL 数据库的持久化方式是一样的。

注意:可以同时使用两种持久化,根据你的需求来判断。 还原数据优先使用 AOF 文件。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Go