# Redis 持久化

Redis 如何将数据写入磁盘(仅附加文件、快照等)

持久性是指将数据写入持久存储,例如固态磁盘 (SSD)。Redis 本身提供了一系列持久化选项:

  • RDB(Redis 数据库):RDB 持久性以指定的时间间隔执行数据集的时间点快照。
  • AOF(Append Only File):AOF 持久化记录服务器接收到的每个写操作,在服务器启动时再次播放,重建原始数据集。命令使用与 Redis 协议本身相同的格式以仅附加方式记录。当日志变得太大时,Redis 能够在后台 重写日志。
  • 无持久性:如果您愿意,您可以完全禁用持久性,如果您希望您的数据只要服务器正在运行就存在。
  • RDB + AOF:可以在同一个实例中结合 AOF 和 RDB。请注意,在这种情况下,当 Redis 重新启动时,AOF 文件将用于重建原始数据集,因为它保证是最完整的。

最重要的是要了解 RDB 和 AOF 持久性之间的不同权衡。

# 关系型数据库优势

  • RDB 是 Redis 数据的一个非常紧凑的单文件时间点表示。RDB 文件非常适合备份。例如,您可能希望在最近的 24 小时内每小时归档一次 RDB 文件,并在 30 天内每天保存一个 RDB 快照。这使您可以在发生灾难时轻松恢复不同版本的数据集。
  • RDB 非常适合灾难恢复,它是一个可以传输到远程数据中心或 Amazon S3(可能已加密)的压缩文件。
  • RDB 最大限度地提高了 Redis 的性能,因为 Redis 父进程为了持久化而需要做的唯一工作就是派生一个将完成所有其余工作的子进程。父进程永远不会执行磁盘 I/O 或类似操作。
  • 与 AOF 相比,RDB 允许使用大数据集更快地重启。
  • 在副本上,RDB 支持 重启和故障转移后的部分重新同步

# RDB 的缺点

  • 如果您需要在 Redis 停止工作时(例如断电后)将数据丢失的可能性降到最低,那么 RDB 并不好。您可以配置生成 RDB 的不同保存点(例如,在对数据集至少 5 分钟和 100 次写入之后,您可以有多个保存点)。但是,您通常会每五分钟或更长时间创建一个 RDB 快照,因此,如果 Redis 由于任何原因在没有正确关闭的情况下停止工作,您应该准备好丢失最新分钟的数据。
  • RDB 需要经常 fork() 以便使用子进程在磁盘上持久化。如果数据集很大,fork() 可能会很耗时,并且如果数据集很大并且 CPU 性能不是很好,可能会导致 Redis 停止为客户端服务几毫秒甚至一秒钟。AOF 也需要 fork() 但频率较低,您可以调整要重写日志的频率,而不需要对持久性进行任何权衡。

# AOF 优势

  • 使用 AOF Redis 更加持久:您可以有不同的 fsync 策略:根本不 fsync、每秒 fsync、每次查询时 fsync。使用每秒 fsync 的默认策略,写入性能仍然很棒。fsync 是使用后台线程执行的,当没有 fsync 正在进行时,主线程将努力执行写入,因此您只能丢失一秒钟的写入。
  • AOF 日志是一个仅附加日志,因此不会出现寻道问题,也不会在断电时出现损坏问题。即使由于某种原因(磁盘已满或其他原因)日志以写一半的命令结束,redis-check-aof 工具也能够轻松修复它。
  • 当 AOF 变得太大时,Redis 能够在后台自动重写 AOF。重写是完全安全的,因为当 Redis 继续附加到旧文件时,会使用创建当前数据集所需的最少操作集生成一个全新的文件,一旦第二个文件准备就绪,Redis 就会切换两者并开始附加到新的那一个。
  • AOF 以易于理解和解析的格式依次包含所有操作的日志。您甚至可以轻松导出 AOF 文件。例如,即使您不小心使用该 FLUSHALL 命令刷新了所有内容,只要在此期间没有执行日志重写,您仍然可以通过停止服务器、删除最新命令并重新启动 Redis 来保存您的数据集.

# AOF 缺点

  • AOF 文件通常比相同数据集的等效 RDB 文件大。
  • 根据确切的 fsync 策略,AOF 可能比 RDB 慢。一般来说,将 fsync 设置为每秒的性能仍然非常高,并且在禁用 fsync 的情况下,即使在高负载下它也应该与 RDB 一样快。即使在巨大的写入负载的情况下,RDB 仍然能够提供关于最大延迟的更多保证。

Redis < 7.0

  • 如果在重写期间有对数据库的写入,AOF 可能会使用大量内存(这些被缓冲在内存中并在最后写入新的 AOF)。
  • 重写期间到达的所有写入命令都会写入磁盘两次。
  • Redis 可以在重写结束时冻结写入并将这些写入命令同步到新的 AOF 文件。

# 好的,那我应该用什么?

您应该使用这两种持久性方法的一般指示是,如果您想要与 PostgreSQL 可以提供的数据安全程度相当的数据安全性。

如果您非常关心您的数据,但在发生灾难时仍然可以忍受几分钟的数据丢失,您可以简单地单独使用 RDB。

有很多用户单独使用 AOF,但我们不鼓励这样做,因为不时拥有 RDB 快照对于进行数据库备份、更快的重启以及 AOF 引擎中出现错误是一个好主意。

以下部分将说明有关这两种持久性模型的更多细节。

# 快照

默认情况下,Redis 将数据集的快照保存在磁盘上的一个名为dump.rdb. 如果数据集中至少有 M 次更改,您可以将 Redis 配置为每 N 秒保存一次数据集,或者您可以手动调用 SAVE or BGSAVE 命令。

例如,如果至少更改了 1000 个键,则此配置将使 Redis 每 60 秒自动将数据集转储到磁盘:

save 60 1000

这种策略称为快照

# 这个怎么运作

每当 Redis 需要将数据集转储到磁盘时,都会发生以下情况:

  • Redis 分叉。我们现在有一个子进程和一个父进程。
  • 孩子开始将数据集写入临时 RDB 文件。
  • 当孩子写完新的 RDB 文件后,它会替换旧的。

此方法允许 Redis 从写时复制语义中受益。

# 仅附加文件

快照不是很耐用。如果您运行 Redis 的计算机停止,您的电源线出现故障,或者您不小心kill -9您的实例,最新写入 Redis 的数据将会丢失。虽然这对某些应用程序来说可能没什么大不了的,但也有完全持久性的用例,在这些情况下,单独的 Redis 快照并不是一个可行的选择。

仅附加文件是 Redis 的另一种完全持久的策略。它在 1.1 版中可用。

您可以在配置文件中打开 AOF:

appendonly yes

从现在开始,每次 Redis 接收到更改数据集的命令(例如 SET )时,它都会将其附加到 AOF。当您重新启动 Redis 时,它将重新播放 AOF 以重建状态。

从 Redis 7.0.0 开始,Redis 使用了多部分 AOF 机制。也就是将原来的单个AOF文件拆分为基础文件(最多一个)和增量文件(可能不止一个)。 基本文件表示重写 AOF 时存在的数据的初始(RDB 或 AOF 格式)快照。增量文件包含自创建最后一个基本 AOF 文件以来的增量更改。所有这些文件都放在一个单独的目录中,并由清单文件跟踪。

# 日志重写

随着写入操作的执行,AOF 变得越来越大。例如,如果您将计数器递增 100 次,您最终会在数据集中得到一个包含最终值的键,但在 AOF 中有 100 个条目。重建当前状态不需要其中的 99 个条目。

重写是完全安全的。在 Redis 继续追加到旧文件的同时,使用创建当前数据集所需的最少操作集生成一个全新的文件,一旦第二个文件准备好,Redis 就会切换这两个文件并开始追加到新文件。

所以 Redis 支持一个有趣的特性:它能够在后台重建 AOF 而不会中断对客户端的服务。每当您发出 a BGREWRITEAOF 时,Redis 都会在内存中写入重建当前数据集所需的最短命令序列。如果您将 AOF 与 Redis 2.2 一起使用,则需要 BGREWRITEAOF 不时运行。由于 Redis 2.4 能够自动触发日志重写(有关更多信息,请参阅示例配置文件)。

从 Redis 7.0.0 开始,在调度 AOF 重写时,Redis 父进程会打开一个新的增量 AOF 文件继续写入。子进程执行重写逻辑并生成新的基础 AOF。Redis 将使用一个临时清单文件来跟踪新生成的基础文件和增量文件。当它们准备好后,Redis 会执行原子替换操作,使这个临时清单文件生效。为了避免在 AOF 重写重复失败和重试的情况下创建大量增量文件的问题,Redis 引入了 AOF 重写限制机制,以确保失败的 AOF 重写以越来越慢的速度重试。

# 仅附加文件的耐用性如何?

您可以配置 Redis 将 fsync 数据存储在磁盘上的次数。有三个选项:

  • appendfsync alwaysfsync每次将新命令附加到 AOF 时。非常非常慢,非常安全。请注意,在执行来自多个客户端或管道的一批命令之后,这些命令会附加到 AOF,因此这意味着一次写入和一次 fsync(在发送回复之前)。
  • appendfsync everysec:fsync每一秒。足够快(因为 2.4 版本可能和快照一样快),如果发生灾难,您可能会丢失 1 秒的数据。
  • appendfsync no:从不fsync,只是将您的数据交到操作系统手中。更快,更不安全的方法。通常,Linux 将使用此配置每 30 秒刷新一次数据,但这取决于内核的精确调整。

建议(和默认)策略是fsync每秒。它既快速又相对安全。该always策略在实践中很慢,但它支持组提交,因此如果有多个并行写入,Redis 将尝试执行单个fsync操作。

# 如果我的 AOF 被截断,我该怎么办?

可能是服务器在写入 AOF 文件时崩溃,或者存储 AOF 文件的卷在写入时已满。发生这种情况时,AOF 仍然包含表示数据集的给定时间点版本的一致数据(使用默认的 AOF fsync 策略可能会旧到一秒),但 AOF 中的最后一个命令可能会被截断。无论如何,最新的主要版本的 Redis 将能够加载 AOF,只需丢弃文件中最后一个格式不正确的命令。在这种情况下,服务器将发出如下日志:

* Reading RDB preamble from AOF file...
* Reading the remaining AOF tail...
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled

如果需要,您可以更改默认配置以强制 Redis 在这种情况下停止,但默认配置是继续,无论文件中的最后一条命令格式不正确,以保证重启后的可用性。

旧版本的 Redis 可能无法恢复,可能需要执行以下步骤:

  • 制作 AOF 文件的备份副本。

  • redis-check-aof使用Redis 附带的工具修复原始文件:

    $ redis-check-aof --fix <filename>
    
  • 可选地diff -u用于检查两个文件之间的区别。

  • 使用固定文件重新启动服务器。

# 如果我的 AOF 损坏了怎么办?

如果 AOF 文件不仅被截断,而且在中间被无效字节序列损坏,事情就变得更加复杂了。Redis 会在启动时报错并中止:

* Reading the remaining AOF tail...
# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>

最好的办法是运行该redis-check-aof实用程序,最初没有--fix选项,然后了解问题,跳转到文件中给定的偏移量,看看是否可以手动修复文件:AOF 使用与Redis 协议,手动修复非常简单。否则可以让实用程序为我们修复文件,但在这种情况下,从无效部分到文件末尾的所有 AOF 部分都可能被丢弃,如果损坏发生,则会导致大量数据丢失在文件的初始部分。

# 这个怎么运作

日志重写使用与快照相同的写时复制技巧。这是它的工作原理:

Redis >= 7.0

  • Redis 分叉,所以现在我们有一个子进程和一个父进程。
  • 孩子开始在一个临时文件中写入新的基础 AOF。
  • 父级打开一个新的增量 AOF 文件以继续写入更新。如果重写失败,旧的基础和增量文件(如果有的话)加上这个新打开的增量文件就代表了完整的更新数据集,所以我们是安全的。
  • 当子进程完成基础文件的重写后,父进程会收到一个信号,并使用新打开的增量文件和子进程生成的基础文件来构建临时清单,并将其持久化。
  • 利润!现在 Redis 对清单文件进行原子交换,以便此 AOF 重写的结果生效。Redis 还会清理旧的基础文件和任何未使用的增量文件。

Redis < 7.0

  • Redis 分叉,所以现在我们有一个子进程和一个父进程。
  • 孩子开始在一个临时文件中写入新的 AOF。
  • 父级在内存缓冲区中累积所有新更改(但同时它将新更改写入旧的仅附加文件中,因此如果重写失败,我们是安全的)。
  • 当子进程完成文件重写时,父进程会收到一个信号,并将内存缓冲区附加到子进程生成的文件的末尾。
  • 现在 Redis 以原子方式将新文件重命名为旧文件,并开始将新数据附加到新文件中。

# 如果我当前正在使用 dump.rdb 快照,如何切换到 AOF?

在 2.0 版和更高版本中执行此操作的过程有所不同,您可以猜到自 Redis 2.2 以来它更简单,并且根本不需要重新启动。

Redis >= 2.2

  • 备份最新的 dump.rdb 文件。
  • 将此备份转移到安全的地方。
  • 发出以下两个命令:
  • redis-cli config set appendonly yes
  • redis-cli config set save ""
  • 确保您的数据库包含与其包含的相同数量的键。
  • 确保将写入正确附加到仅附加文件。

第一个 CONFIG 命令启用 Append Only File 持久性。

第二个 CONFIG 命令用于关闭快照持久性。这是可选的,如果您希望可以启用两种持久性方法。

**重要提示:**记得编辑你的 redis.conf 以打开 AOF,否则当你重新启动服务器时,配置更改将丢失,服务器将以旧配置重新启动。

雷迪斯 2.0

  • 备份最新的 dump.rdb 文件。
  • 将此备份转移到安全的地方。
  • 停止对数据库的所有写入!
  • 发出一个redis-cli BGREWRITEAOF. 这将创建仅附加文件。
  • 当 Redis 完成生成 AOF 转储时停止服务器。
  • 编辑 redis.conf 结束启用仅附加文件持久性。
  • 重新启动服务器。
  • 确保您的数据库包含与切换前相同数量的键。
  • 确保将写入正确附加到仅附加文件。

# AOF 和 RDB 持久化之间的交互

Redis >= 2.4 确保避免在 RDB 快照操作已经在进行时触发 AOF 重写,或者允许一段 BGSAVE 时间 AOF 重写正在进行。这可以防止两个 Redis 后台进程同时进行繁重的磁盘 I/O。

当快照正在进行中并且用户使用服务器明确请求日志重写操作 BGREWRITEAOF 时,服务器将回复一个 OK 状态代码,告诉用户该操作已安排好,一旦快照完成,重写将开始。

在启用 AOF 和 RDB 持久性的情况下,Redis 重新启动 AOF 文件将用于重建原始数据集,因为它保证是最完整的。

# 备份 Redis 数据

在开始本节之前,请务必阅读以下句子:确保备份您的数据库。磁盘损坏,云中的实例消失等等:没有备份意味着数据消失到 /dev/null 的巨大风险。

Redis 对数据备份非常友好,因为您可以在数据库运行时复制 RDB 文件:RDB 一旦生成就永远不会修改,并且在生成时它使用临时名称并仅使用 rename(2) 以原子方式重命名为其最终目标当新快照完成时。

这意味着在服务器运行时复制 RDB 文件是完全安全的。这是我们的建议:

  • 在您的服务器中创建一个 cron 作业,在一个目录中创建 RDB 文件的每小时快照,并在不同目录中创建每日快照。
  • 每次运行 cron 脚本时,请确保调用find命令以确保删除太旧的快照:例如,您可以拍摄最近 48 小时的每小时快照,以及一两个月的每日快照。确保使用日期和时间信息命名快照。
  • 每天至少一次确保将 RDB 快照传输到您的数据中心之外,或者至少传输到运行您的 Redis 实例的物理机之外。

# 备份 AOF 持久性

如果您运行仅启用 AOF 持久性的 Redis 实例,您仍然可以执行备份。appenddirname从 Redis 7.0.0 开始,AOF 文件被拆分为多个文件,这些文件驻留在由配置确定的单个目录中。在正常操作期间,您只需复制/压缩此目录中的文件即可实现备份。但是,如果这是在 rewrite 期间完成的,您最终可能会得到一个无效的备份。要解决此问题,您必须在备份期间禁用 AOF 重写:

  1. 使用 确保在此期间 不手动启动重写(使用)关闭自动重写。 CONFIG SET auto-aof-rewrite-percentage 0 BGREWRITEAOF
  2. 检查当前没有正在进行的重写,使用 验证为 0。如果为 1,则您需要等待重写完成。 INFO persistence aof_rewrite_in_progress
  3. 现在您可以安全地复制目录中的appenddirname文件。
  4. 完成后重新启用重写: CONFIG SET auto-aof-rewrite-percentage <prev-value>

**注意:**如果您想最大限度地减少禁用 AOF 重写的时间,您可以创建指向文件的硬链接appenddirname(在上面的步骤 3 中),然后在创建硬链接后重新启用重写(步骤 4)。现在您可以复制/tar 硬链接并在完成后将其删除。这是有效的,因为 Redis 保证它只附加到该目录中的文件,或者在必要时完全替换它们,因此内容在任何给定时间点都应该是一致的。

**注意:**如果您想处理在备份期间重新启动服务器的情况,并确保在重新启动后不会自动开始重写,您可以更改上面的步骤 1 以也通过 CONFIG REWRITE. 只需确保在完成后重新启用自动重写(第 4 步)并将其与另一个 CONFIG REWRITE.

在 7.0.0 版本之前,备份 AOF 文件可以简单地通过复制 aof 文件来完成(就像备份 RDB 快照一样)。该文件可能缺少最后一部分,但 Redis 仍然可以加载它(请参阅前面关于 截断 AOF 文件 的部分)。

  1. 使用 确保在此期间 不手动启动重写(使用)关闭自动重写。 CONFIG SET auto-aof-rewrite-percentage 0 BGREWRITEAOF
  2. 检查当前没有正在进行的重写,使用 验证为 0。如果为 1,则您需要等待重写完成。 INFO persistence aof_rewrite_in_progress
  3. 现在您可以安全地复制目录中的appenddirname文件。
  4. 完成后重新启用重写: CONFIG SET auto-aof-rewrite-percentage <prev-value>

**注意:**如果您想最大限度地减少禁用 AOF 重写的时间,您可以创建指向文件的硬链接appenddirname(在上面的步骤 3 中),然后在创建硬链接后重新启用重写(步骤 4)。现在您可以复制/tar 硬链接并在完成后将其删除。这是有效的,因为 Redis 保证它只附加到该目录中的文件,或者在必要时完全替换它们,因此内容在任何给定时间点都应该是一致的。

**注意:**如果您想处理在备份期间重新启动服务器的情况,并确保在重新启动后不会自动开始重写,您可以更改上面的步骤 1 以也通过 CONFIG REWRITE. 只需确保在完成后重新启用自动重写(第 4 步)并将其与另一个 CONFIG REWRITE.

在 7.0.0 版本之前,备份 AOF 文件可以简单地通过复制 aof 文件来完成(就像备份 RDB 快照一样)。该文件可能缺少最后一部分,但 Redis 仍然可以加载它(请参阅前面关于 截断 AOF 文件 的部分)。

# 灾难恢复

Redis 环境中的灾难恢复与备份基本相同,并且能够在许多不同的外部数据中心传输这些备份。即使在某些灾难性事件影响 Redis 正在运行并生成其快照的主数据中心的情况下,这种方式也可以保护数据。

我们将回顾成本不高的最有趣的灾难恢复技术。

  • Amazon S3 和其他类似服务是实施灾难恢复系统的好方法。只需以加密形式将您的每日或每小时 RDB 快照传输到 S3。您可以使用gpg -c(在对称加密模式下)加密您的数据。确保将您的密码存储在许多不同的安全位置(例如,将副本提供给组织中最重要的人)。建议使用多个存储服务以提高数据安全性。
  • 使用 SCP(SSH 的一部分)将您的快照传输到远程服务器。这是一个相当简单和安全的方法:在离你很远的地方找一个小型 VPS,在那里安装 ssh,并生成一个没有密码的 ssh 客户端密钥,然后将其添加到authorized_keys你的小型 VPS 的文件中。您已准备好以自动方式传输备份。在两个不同的提供商中获得至少两个 VPS 以获得最佳效果。

重要的是要理解,如果没有以正确的方式实施,这个系统很容易失败。至少,绝对确保在传输完成后您能够验证文件大小(应该与您复制的文件匹配)以及可能的 SHA1 摘要,如果您使用的是 VPS。

如果新备份的传输由于某种原因无法正常工作,您还需要某种独立的警报系统。

# 反馈

如果您在此页面上发现问题,或有改进建议,请提交请求以合并或打开存储库中的问题。

Last Updated: 4/18/2023, 8:45:33 AM