Redis 持久化机制简介:RDB 和 AOF

375 阅读7分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

简介

Redis 中一共有两种持久化机制:

  • RDBRedis 默认采用 RDB 持久化机制,该机制又被称作快照 snapshot
  • AOF:是英文 append-only file 的缩写,意为只进行追加操作的文件。

如果同时采用了两种持久化机制,Redis 重启后会优先使用 AOF 文件来还原数据集,因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。


RDB 持久化机制

RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照 point-in-time snapshot,它先对数据集进行压缩,然后保存为二进制文件。

/etc/redis/redis.conf 中可以看到有关 RDB 持久化的三行配置,均以 save 开头:

save 900 1
save 300 10
save 60 10000

它们分别表示:

  • 900 秒内数据集至少有 1 个键被改动时,自动保存数据集
  • 300 秒内数据集至少有 10 个键被改动时,自动保存数据集
  • 60 秒内数据集至少有 10000 个键被改动时,自动保存数据集

RedisRDB 快照保存在二进制文件中,该文件的存储路径和文件名由两个配置项决定:

  • dir /var/lib/redis:指定了 RDB 文件的存储目录为 /var/lib/redis
  • dbfilename dump.rdb:指定了 RDB 文件名为 dump.db
  • 所以,RDB 机制的数据库文件会被保存到 /var/lib/redis/dump.rdb

如果想要关闭 RDB 存储机制,只需要将 /etc/redis/redis.conf 文件中的 save 开头的配置都注释掉即可:

# save 900 1
# save 300 10
# save 60 10000

当然,需要重启 redis-server 服务才会生效:

sudo service redis-server restart

Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

  • Redis 调用 fork(),同时拥有父进程和子进程。
  • 子进程将数据集写入到一个临时 RDB 文件中。
  • 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

写时复制 copy-on-write 机制。


AOF 持久化机制

AOF 持久化记录服务器执行的所有写操作命令,新命令会被追加到文件的末尾。在 Redis 重新启动的时候,可以重新执行这些命令来还原数据集。

AOF 持久化机制默认是关闭的,在 /etc/redis/redis.conf 中可以看到它的配置:

appendonly no

只需要将 no 改成 yes 即可开启 AOF 持久化:

appendonly yes

AOF 文件的存储路径和文件名由以下两个配置决定:

  • dir /var/lib/redis:指定了 AOF 文件的存储目录为 /var/lib/redis
  • appendfilename "appendonly.aof":指定了 AOF 文件的文件名为 appendonly.aof

AOF 同步速率的配置有三项,选用一项即可:

  • appendfsync everysec:默认配置,每秒同步一次。这是可以兼顾速度和安全性的配置,推荐使用。
  • appendfsync always:只要发生改动就同步。非常慢,也非常安全。
  • appendfsync no:采用操作系统的同步机制,通常是每 30 秒同步一次。

对于 AOF 文件,经常会出现冗余的中间操作,Redis 支持在不打断客户端的情况下,对 AOF 文件进行重建 rebuild。执行 BGREWRITEAOF 命令,Redis 将生成一个新的 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 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制。以下是 AOF 重写的执行步骤:

  • Redis 执行 fork(),现在同时拥有父进程和子进程。
  • 子进程开始将新 AOF 文件的内容写入到临时文件。
  • 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾: 这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  • 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  • 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

RDB 和 AOF 的优缺点

RDB 的优点:

  • RDB 快照文件经过压缩后非常紧凑,适用于备份和灾难恢复,而且还能选择恢复到不同的版本。
  • RDB 在恢复大数据集时的速度比 AOF 要快。

RDB 的缺点:

  • 当需要保存的数据集非常庞大时,可能会至少 5 分钟才保存一次 RDB 文件,如果服务器发生故障,会丢失这一段时间内的数据。
  • 父进程在保存 RDB 文件时是通过 fork 一个子进程完成的,当数据集非常庞大时,fork 可能会非常耗时,造成服务器在一段时间内停止处理客户端。

AOF 的优点:

  • AOF 持久化会让 Redis 变得非常健壮 much more durable,如果采用默认配置,一秒钟 fsync 一次,就算发生故障,最多只会丢失一秒钟的数据。
  • AOF 文件是一个只进行追加操作的日志文件 append only log,因此对 AOF 文件的写入不需要进行 seek
  • AOF 文件体积过大时,Redis 会在后台对其自动重写(rewrite 机制),重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。

如果不小心 flushall 了一下,只要 AOF 文件未被重写,就可以移除 AOF 文件末尾的命令,重启 Redis 即可恢复到之前的数据集。

AOF 的缺点:

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。
  • AOF 在过去曾经发生过这样的 bug:因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 虽然这种 bugAOF 文件中并不常见, 但是对比来说,RDB 几乎是不可能出现这种 bug 的。

混合持久化

Redis 4.x 及以后的版本,可以两种缓存机制混合使用。两种机制共存于同一份二进制文件,先使用 RDB 持久化全量数据,再使用 AOF 持久化增量数据。

相关配置:

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

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

简单的说,新的 AOF 文件前半段是 RDB 格式的全量数据后半段是 AOF 格式的增量数据。