Redis 持久化

Last Modified: 2022/11/21

概述

Redis 提供了四种持久化选项:

  • rdb(redis database),每隔一段时间创建一个数据快照;
  • aof,跟日志文件很类似,将所有的写命令都记录下来,格式和 redis 协议相同。实例重启后可以通过该文件重建数据集;
  • 无持久化,如果只是用作缓存,可以关闭持久化;
  • rdb+aof,组合使用 rdb 和 aof 这两种持久化方式。

RDB 优点

结构紧凑适合用于备份,数据集较大的时候,相较于 aof,实例重启时使用 rdb 恢复速度更快。使用 rdb 持久化方式能够最大化 Redis 性能,Redis 父进程唯一要做的是 fork 一个子进程,然后由子进程完成持久化。rdb 支持故障转移或者实例重启时进行部分同步。

RDB 缺点

RDB 相对于 aof 丢失数据的风险更大,因为 rdb 定时创建快照,所以如果突然断电或者突然崩溃,就很有可能导致数据丢失。如果数据集很大,fork 子进程可能要消耗数毫秒甚至达到一秒时间,实例在此期间无法提供服务。

AOF 优点

aof 不及 rdb 存储方式紧凑,但 aof 持久化方式数据丢失风险更小。aof 模式下我们可以配置多种刷盘策略(独立后台线程专门刷盘):从不刷盘、每秒刷盘或者每次写都刷盘。默认情况下使用每秒刷盘,写效率依然高效,这种刷盘策略下,最多丢失 1 秒的数据。

当 aof 文件过大时,Redis 能够在后台对 aof 文件重写,重写就是使用最少的命令完成对数据集的重建。aof 文件易于理解和解析,可以很容易导出 aof 文件,当你不小心执行了 FLUSHALL 清空了数据,只要停止 Redis,将 FLUSHALL 删了,重启 Redis 即可。当然这个前提是后台没有正在执行重写操作。

AOF 缺点

持久化文件比 rdb 文件大,在写入负载很大的时候,aof 的写延迟往往比 rdb 要大,这和 fsync 的配置直接相关。

应该使用哪种持久化模式

如果可以承受几分钟的数据丢失,那么直接使用 RDB 即可。但是很多用户仅使用 AOF,但是我们并不提倡。有 rdb 快照始终是个好主意,不论是用作备份、重启后快速加载数据还是应对万一 aof 引擎的故障。

快照

默认情况下,Redis 会将快照存到名为 dump.rdb 文件中(名称可配)。快照创建时机可配置,可以配置 M 秒内如果有 N 次写入,就创建快照。例如以下配置 60 秒内如果有 1000 次写入则创建快照。

save 60 1000

快照工作方式

Redis 创建子进程,由子进程将数据集写入到一个临时文件中,写入完毕后,替换原先的快照文件。

Append-only file

# 开启 aof
appendonly yes

Redis 7.0 之后,aof 文件分为多个部分,一个基础文件(base)和多个增量文件。重写开始后,Redis 会打开新的文件(即增量文件)来继续记录客户端写入的命令,同时创建子进程执行重写逻辑并将结果写入 base 文件。这些文件被放入不同的文件夹中并有一个清单文件来记录这些日志文件。7.0 之前只有一个文件,所以如果重写的过程中有写入命令,这些命令都会被 Redis 缓存(如果写入很多将会消耗很多内存),并在重写完成后追加到 aof 日志文件中。

AOF 日志重写

重写的目的是为了减小日志文件的大小。举个例子,客户端执行了 100 次 incr counter,那么 aof 日志文件将会记录 100 次该命令,假如 counter 初始值为0,此时 counter 值为 100。可将 100 次 incr 命令重写成一次 set counter 100

重写过程是可靠的,重写会写入临时文件不会破坏原文件,重写完成后,临时文件会替换原文件。7.0 之后,Redis 会打开新的文件(即之前提到的增量文件)来继续记录客户端写入的命令,同时创建子进程执行重写逻辑并将结果写入 base 文件。

注:为什么会有多个增量文件呢?似乎是重写的过程中有可能会报错,以下是原文:

In order to avoid the problem of creating many incremental files in case of repeated failures and retries of an AOF rewrite, Redis introduces an AOF rewrite limiting mechanism to ensure that failed AOF rewrites are retried at a slower and slower rate.

日志重写细节:

7.0 之后的日志重写过程:

  • 创建子进程,子进程执行重写,并将结果写入一个临时文件,这个文件称之为 base 文件。
  • 父进程打开新的 aof 文件(也称之为增量文件)继续记录客户端写入的命令。如果重写失败,老的 base 文件和新的增量文件依然可以表示完整的数据集,即使重写失败,我们数据仍然是安全的。
  • 当子进程重写完毕,父进程会收到信号,之后使用新的 base 和增量文件构建一个临时的清单文件(manifest),并保存该清单;
  • 将临时清单文件替换原清单文件使重写生效,最后清除旧的 base 和未使用的增量文件。

7.0 之前的日志重写过程:

  • 创建子进程,子进程执行重写,并将结果写入一个临时文件;
  • 父进程将客户端在子进程执行重写过程中的写命令记录到缓存中并同时写入老的 aof 日志文件中;
  • 当子进程重写完毕,父进程收到信号后,将缓存的命令写入到新 aof 文件中;
  • 最后将新的 aof 文件替换老的文件。

以上细节都是浮云,多半记不住,唯一需要理解的是 7.0 版本之后之所以采用增量文件的形式主要是为了解决之前版本在重写过程中可能消耗大量内存的问题。另外重写的过程是安全的,不会导致文件损坏。

AOF 日志文件被截断的处理

如果是因为服务挂了或者磁盘不够,导致日志文件最后一条命令不完整,该如何处理?针对这种情况,新版本 Redis 可以直接加载这样的 aof 文件。老版本的 Redis 我们可以先备份 aof 文件,再使用下列命令:

# 执行修复
 redis-check-aof --fix <filename>
 # 比较修复的文件和原文件之间的差异(可选,只为了确认下差异)
 diff -u

最后重启 Redis 即可。

AOF 日志文件损坏的处理

先使用 redis-check-aof <filename> 看看是什么原因导致的,在尝试定位到错误手动修复,也可以尝试自动修复(使用--fix选项),但是自动修复会丢弃从损坏开始直至最后的所有命令导致大量数据丢失。

从 RDB 切换到 AOF

  • 备份最新的 dump.rdb 文件。
  • 执行下面两个命令。
  • redis-cli config set appendonly yes,打开 aof 持久化模式,同时别忘了修改 redis.conf,否则重启后,又变成 rdb 模式了。
  • redis-cli config set save "",关闭 rdb 模式,这一步是可选的,aof 和 rdb 模式可以同时开启。
  • 检查 aof 文件是否正常追加命令。

注:2.2 之前稍有不同,但是 2.0 太老了,这里不作说明。

RDB 和 AOF 的交互

Redis >=2.4 会避免创建 rdb 快照的过程中触发 aof 日志重写,主要原因是这两个操作都是 io 开销比较重的操作。如果在快照过程中,用户主动执行了 BGREWRITEAOF 命令进行日志重写,那么 Redis 会回复 OK,但是要等到快照创建完毕后才开始日志重写。如果同时打开了 rdb 和 aof 持久化方式,重启的时候会使用 aof 日志文件重建数据,因为 aof 丢失数据的风险相对于 rdb 要小。

备份数据

备份 RDB 文件

很简单,直接复制 rdb 文件即可,即便服务正在运行也可以直接复制。也可以使用 cron 定期备份 rdb 文件。

备份 AOF 文件

备份 aof 应确保当前不在执行重写。使用 INFO persistence 查看 aof_rewrite_in_progress 是否为0,如果为 0 则当前没有重写。否则需要等待重写完成后再备份。备份步骤如下:

  • 关闭日志重写: CONFIG SET auto-aof-rewrite-percentage 0,同时确认当前没有执行日志重写;
  • 直接拷贝 appenddirname 配置的目录;
  • 恢复日志重写,执行 CONFIG SET auto-aof-rewrite-percentage xx,xx 为原先设置的值;
有问题吗?点此反馈!

温馨提示:反馈需要登录