redis持久化(RDB、AOF、混合持久化)

  • 1. RDB快照(snapshot)

在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中。

你可以对 Redis 进行设置, 让它在“N 秒内数据集至少有 M 个改动”这一条件被满足时,

自动保存一次数据集。

比如说, 以下设置会让 Redis 在满足“60 秒内有至少有 1000 个键被改动”这一条件时,

自动保存一次数据集:

save 60 1000

优点:

  1. RDB 是一个非常紧凑(compact)的文件,体积小,因此在传输速度上比较快,因此适合灾难恢复。

  2. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快得多。

缺点:

  1. RDB是一个快照过程,无法完整的保存所以数据,尤其在数据量比较大时候,一旦出现故障丢失的数据将更多。

  2. RDB文件是特定的格式,阅读性差,由于格式固定,可能存在不兼容情况。


  • 2. AOF(append-only file)

快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化,将修改的每一条指令记录进文件

你可以通过修改配置文件来打开 AOF 功能:

appendonly yes

从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。

这样的话, 当 Redis 重新启动时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。

你可以配置 Redis 多久才将数据 fsync 到磁盘一次。

有三个选项:

  • appendfsync always。每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
  • appendfsync everysec。每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
  • appendfsync no。从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。

推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。

优点:

  1. 数据更完整,秒级数据丢失(取决于设置fsync策略)。、

  2. 兼容性较高,由于是基于redis通讯协议而形成的命令追加方式,无论何种版本的redis都兼容。

  3. aof文件是明文的,可阅读性较好。

缺点:

  1. 数据文件体积较大,即使有重写机制,但是在相同的数据集情况下,AOF文件通常比RDB文件大。

  2. 相对RDB方式,AOF速度慢于RDB,并且在数据量大时候,恢复速度AOF速度也是慢于RDB。

  3. 由于频繁地将命令同步到文件中,AOF持久化对性能的影响相对RDB较大。


  • AOF重写

aof 文件记录的是每一条redis命令,有时候我们会对某一个key进行多次set,中间会产生很多条命令,但是结果只有一个。

set name 1
...
set name 12
set name 123
...
set name 1234
set name 12345

例如我们执行了上述命令,aof 文件会记录着每一条命令。在redis重启时,会逐条执行上述的命令。但是其实可以精简为set name 12345,其余的几条命令其实没有意义。AOF重写就是实现了这个功能。

相关配置:

auto-aof-rewrite-percentage 100  # 指当前aof文件比上次重写的增长比例大小,达到这个大小就进行 aof 重写
auto-aof-rewrite-min-size 64mb # 最开始aof文件必须要达到这个文件时才触发,后面的每次重写就不会根据这个变量了

以上配置的意思是:

在 aof 文件小于64mb的时候不进行重写,当到达64mb的时候,就重写一次。重写后的 aof 文件可能是10mb。上面配置了auto-aof-rewrite-percentage 100,即 aof 文件到了20mb的时候,又开始重写一次。以此类推。

AOF 是在后台自动重写(redis会fork一个子进程来进行备份,保证主进程不会阻塞),也可以人为执行命令 bgrewriteaof 重写 AOF。


  • 使用子进程来进行AOF重写时会遇到的问题

问题:

子进程在进行AOF重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。

解决方案:

要知道redis是怎么处理这个问题的,需要先了解下AOF重写的具体实现:

redis持久化(RDB、AOF、混合持久化)-LMLPHP

AOF文件重写过程与RDB快照bgsave工作过程有点相似,都是通过fork子进程,由子进程完成相应的操作,同样的在fork子进程简短的时间内,redis是阻塞的。

(1)开始bgrewriteaof,判断当前有没有bgsave命令(RDB持久化)/bgrewriteaof在执行,倘若有,则这些命令执行完成以后在执行。

(2)主进程fork出子进程,在这一个短暂的时间内,redis是阻塞的。

(3)主进程fork完子进程继续接受客户端请求。此时,客户端的写请求不仅仅写入aof_buf缓冲区,还写入aof_rewrite_buf重写缓冲区。一方面是写入aof_buf缓冲区并根据appendfsync策略同步到磁盘,保证原有AOF文件完整和正确。另一方面写入aof_rewrite_buf重写缓冲区,保存fork之后的客户端的写请求,防止新AOF文件生成期间丢失这部分数据。

(4.1)子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。

(4.2)主进程把aof_rewrite_buf中的数据写入到新的AOF文件。

(5.)使用新的AOF文件覆盖旧的AOF文件,标志AOF重写完成。


  • Redis 4.0 混合持久化

重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。

如果使用 AOF 日志重放,性能则相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动的时候需要花费很长的时间。

Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。

混合持久化同样也是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将aof_rewrite_buf重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,如下图:

redis持久化(RDB、AOF、混合持久化)-LMLPHP

在redis重启的时候,加载 aof 文件进行恢复数据:先加载 rdb 内容再加载剩余的 aof。

混合持久化配置:

aof-use-rdb-preamble yes  # yes:开启,no:关闭

  • RDB和AOF,应该用哪一个?

如果你可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。

有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。如果只用AOF持久化,数据量很大时,在redis启动的时候需要花费大量的时间。

如果你非常关心你的数据,建议使用 redis 4.0 以后的混合持久化特性。

05-11 13:15