Redis 持久化

Redis 如何将数据写入磁盘

Redis 开源版

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

  • RDB (Redis Database):RDB 持久化在指定间隔执行数据集的时间点快照。
  • AOF (Append Only File):AOF 持久化记录服务器接收到的每一个写操作。这些操作可以在服务器启动时重新执行,以重建原始数据集。命令使用与 Redis 协议本身相同的格式记录。
  • 无持久化:你可以完全禁用持久化。这有时用于缓存场景。
  • RDB + AOF:你也可以在同一个实例中结合使用 AOF 和 RDB。

如果你不想考虑这些不同持久化策略之间的权衡,你可能需要考虑 Redis Enterprise 的持久化选项,这些选项可以使用 UI 进行预配置。

要了解更多关于如何评估你的 Redis 持久化策略的信息,请继续阅读。

RDB 的优点

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

RDB 的缺点

  • 如果你需要最小化 Redis 停止工作(例如停电后)时数据丢失的可能性,RDB 则不是一个好的选择。你可以配置不同的 保存点 来生成 RDB(例如,至少五分钟后且数据集发生 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 文件更大。
  • AOF 的速度可能比 RDB 慢,具体取决于精确的 fsync 策略。一般来说,将 fsync 设置为 每秒 时,性能仍然很高;禁用 fsync 时,即使在高负载下也应该与 RDB 一样快。尽管如此,即使在巨大的写入负载下,RDB 也能提供关于最大延迟的更多保证。

Redis < 7.0

  • 如果在重写过程中数据库发生写入(这些写入会缓冲在内存中并在末尾写入新的 AOF),AOF 可能会占用大量内存。
  • 在重写期间到达的所有写命令都会写入磁盘两次。
  • 在重写结束时,Redis 可能会暂停写入并将这些写命令 fsync 到新的 AOF 文件。

那么,我应该使用哪种方式?

如果你希望获得与 PostgreSQL 类似的数据安全级别,则通常建议同时使用这两种持久化方法。

如果你非常关心你的数据,但在发生灾难时可以容忍几分钟的数据丢失,那么你可以只使用 RDB。

有许多用户单独使用 AOF,但我们不鼓励这样做,因为时不时创建 RDB 快照对于数据库备份、更快的重启以及在 AOF 引擎出现 Bug 时都是一个非常好的主意。

以下部分将更详细地介绍这两种持久化模型。

快照

默认情况下,Redis 将数据集的快照保存在磁盘上,文件名为 dump.rdb 的二进制文件。你可以配置 Redis 在数据集发生至少 M 次更改后的每 N 秒保存数据集,或者你可以手动调用 SAVEBGSAVE 命令。

例如,此配置将使 Redis 在至少 1000 个键发生更改后,每 60 秒自动将数据集转储到磁盘

save 60 1000

这种策略被称为 快照

工作原理

无论何时 Redis 需要将数据集转储到磁盘,都会发生以下情况

  • Redis forks。现在我们有一个子进程和一个父进程。

  • 子进程开始将数据集写入临时的 RDB 文件。

  • 当子进程完成写入新的 RDB 文件后,它会替换旧文件。

这种方法使得 Redis 可以利用写时复制(copy-on-write)语义。

仅追加文件

快照的持久性不是很强。如果运行 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,而不会中断对客户端的服务。无论何时你发出 BGREWRITEAOF 命令,Redis 会写入重建内存中当前数据集所需的最短命令序列。如果你使用 Redis 2.2 的 AOF,你需要时不时地运行 BGREWRITEAOF。自 Redis 2.4 起,它能够自动触发日志重写(有关详细信息,请参阅示例配置文件)。

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

仅追加文件的持久性如何?

你可以配置 Redis 将数据 fsync 到磁盘的频率。有三个选项:

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

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

如果我的 AOF 文件被截断了怎么办?

服务器在写入 AOF 文件时可能崩溃,或者存储 AOF 文件的卷在写入时已满。发生这种情况时,AOF 仍然包含表示数据集特定时间点版本的consistent 数据(使用默认 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 自带的 redis-check-aof 工具修复原始文件

    $ 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 内容都可能被丢弃,如果损坏发生在文件开头部分,这将导致大量数据丢失。

工作原理

日志重写使用了与快照相同的写时复制(copy-on-write)技巧。其工作原理如下:

Redis >= 7.0

  • Redis forks,这样我们就有了子进程和父进程。

  • 子进程开始将新的基础 AOF 写入临时文件。

  • 父进程打开一个新的增量 AOF 文件以继续写入更新。如果重写失败,旧的基础文件和增量文件(如果存在)加上这个新打开的增量文件代表完整的更新数据集,因此我们是安全的。

  • 当子进程完成基础文件重写后,父进程会收到信号,然后使用新打开的增量文件和子进程生成的基础文件构建一个临时清单,并将其持久化。

  • 搞定!现在 Redis 执行清单文件的原子交换,使 AOF 重写的结果生效。Redis 还会清理旧的基础文件和任何未使用的增量文件。

Redis < 7.0

  • Redis forks,这样我们就有了子进程和父进程。

  • 子进程开始将新的 AOF 写入临时文件。

  • 父进程将所有新的更改积累在一个内存缓冲区中(但同时也会将新的更改写入旧的仅追加文件中,因此如果重写失败,我们仍然是安全的)。

  • 当子进程完成文件重写后,父进程会收到信号,并将内存缓冲区的内容追加到子进程生成的文件末尾。

  • 现在 Redis 原子地将新文件重命名为旧文件(文件名),并开始向新文件追加新的数据。

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

如果你想在当前使用 RDB 快照的服务器中启用 AOF,你需要先在运行中的服务器上通过 CONFIG 命令启用 AOF 来转换数据。

重要提示:不遵循此步骤(例如,仅更改配置并重启服务器)可能会导致数据丢失!

Redis >= 2.2

准备工作

  • 备份最新的 dump.rdb 文件。
  • 将此备份传输到安全位置。

在运行中的数据库上切换到 AOF

  • 启用 AOF:redis-cli config set appendonly yes
  • (可选)禁用 RDB:redis-cli config set save ""
  • 确保写入正确地追加到仅追加文件。
  • 重要提示:更新你的 redis.conf 文件(可能通过 CONFIG REWRITE 命令),并确保它与上述配置一致。如果忘记此步骤,当你重启服务器时,配置更改将丢失,服务器将使用旧配置再次启动,从而导致数据丢失。

下次重启服务器时

  • 在重启服务器之前,等待 AOF 重写完成持久化数据。你可以通过查看 INFO persistence,等待 aof_rewrite_in_progressaof_rewrite_scheduled 都为 0,并验证 aof_last_bgrewrite_statusok 来做到这一点。
  • 重启服务器后,检查你的数据库是否包含与之前相同数量的键。

Redis 2.0

  • 备份最新的 dump.rdb 文件。
  • 将此备份传输到安全位置。
  • 停止对数据库的所有写入!
  • 执行 redis-cli BGREWRITEAOF 命令。这将创建仅追加文件。
  • 当 Redis 完成生成 AOF 转储后,停止服务器。
  • 编辑 redis.conf 文件并启用仅追加文件持久化。
  • 重启服务器。
  • 确保你的数据库包含与切换之前相同数量的键。
  • 确保写入正确地追加到仅追加文件。

AOF 和 RDB 持久化之间的交互

Redis >= 2.4 会确保在 RDB 快照操作正在进行时避免触发 AOF 重写,或者在 AOF 重写进行时允许 BGSAVE。这可以防止两个 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 持久化

如果你的 Redis 实例仅启用了 AOF 持久化,你仍然可以执行备份。自 Redis 7.0.0 起,AOF 文件被分割成多个文件,这些文件位于由 appenddirname 配置指定的单个目录中。在正常操作期间,你只需复制/打包此目录中的文件即可进行备份。但是,如果在 重写 期间执行此操作,你可能会得到一个无效的备份。为了解决这个问题,你必须在备份期间禁用 AOF 重写

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

注意:如果你想最大程度地缩短禁用 AOF 重写的时间,你可以创建到 appenddirname 目录中文件的硬链接(如上面步骤 3),然后在创建硬链接后重新启用重写(步骤 4)。现在你可以复制/打包这些硬链接,并在完成后删除它们。这样做之所以可行,是因为 Redis 保证它只会向此目录中的文件追加内容,或者在必要时完全替换它们,因此在任何给定时间点,内容都应该是consistent 的。

注意:如果你想处理备份期间服务器重启的情况,并确保重启后不会自动启动重写,你可以更改上面的步骤 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 客户端密钥,然后将其添加到你的小型 VPS 的 authorized_keys 文件中。你就准备好自动化地传输备份了。为了获得最佳效果,至少在两个不同的提供商处获取两个 VPS。

重要的是要明白,如果实施不当,这个系统很容易失败。至少,请务必确保在传输完成后能够验证文件大小(应与你复制的文件大小匹配),如果使用 VPS,则可能还需要验证 SHA1 摘要。

如果由于某种原因新的备份传输不工作,你还需要某种独立的警报系统。

评价本页
返回顶部 ↑