Redis 持久化
Redis 如何将数据写入磁盘
持久化是指将数据写入持久存储,例如固态硬盘 (SSD)。Redis 提供了一系列持久化选项,包括
- RDB (Redis 数据库):RDB 持久化以指定的时间间隔对数据集执行时间点快照。
- AOF (追加仅文件):AOF 持久化记录服务器接收到的每个写入操作。然后,这些操作可以在服务器启动时重新播放,从而重建原始数据集。命令使用与 Redis 协议本身相同的格式记录。
- 无持久化:您可以完全禁用持久化。这在某些情况下用于缓存。
- RDB + AOF:您也可以在同一实例中同时使用 AOF 和 RDB。
如果您不想考虑这些不同持久化策略之间的权衡,您可能需要考虑Redis Enterprise 的持久化选项,这些选项可以使用 UI 预先配置。
要详细了解如何评估 Redis 持久化策略,请继续阅读。
RDB 优势
- RDB 是 Redis 数据的非常紧凑的单文件时间点表示形式。RDB 文件非常适合备份。例如,您可能希望每小时存档最新的 24 小时的 RDB 文件,并每天保存 30 天的 RDB 快照。这使您能够在灾难发生时轻松恢复数据集的不同版本。
- RDB 非常适合灾难恢复,因为它是一个可以传输到远端数据中心或 Amazon S3(可能加密)的单个压缩文件。
- RDB 最大限度地提高了 Redis 的性能,因为 Redis 父进程为了持久化所需要做的唯一工作就是派生一个子进程来完成其余的所有工作。父进程永远不会执行磁盘 I/O 或类似操作。
- 与 AOF 相比,RDB 允许在大型数据集上更快地重启。
- 在副本上,RDB 支持 重启和故障转移后的部分重新同步。
RDB 缺点
- 如果您需要最大程度地减少 Redis 停止工作(例如停电后)时数据丢失的可能性,RDB 不适合您。您可以配置不同的保存点,在这些点上会生成 RDB(例如,在至少 5 分钟和 100 次数据集中写入操作之后,您可以拥有多个保存点)。但是,您通常每 5 分钟或更长时间创建一次 RDB 快照,因此,如果 Redis 因任何原因在未正常关闭的情况下停止工作,您应做好丢失最近几分钟数据的准备。
- RDB 需要经常使用子进程 fork() 来持久化到磁盘。如果数据集很大,fork() 可能很耗时,并且可能会导致 Redis 停止为客户端提供服务几毫秒,甚至如果数据集非常大且 CPU 性能不是很好,则可能停止服务一秒钟。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 可能会冻结写入并将这些写入命令在重写结束时 fsync 到新的 AOF 文件。
好的,那么我应该使用什么?
如果您希望获得与 PostgreSQL 可以提供的相当程度的数据安全性,一般来说,您应该使用这两种持久化方法。
如果您非常关心您的数据,但仍然可以接受在灾难情况下丢失几分钟数据,您只需使用 RDB。
许多用户单独使用 AOF,但我们不建议这样做,因为定期进行 RDB 快照对于进行数据库备份、加快重启速度以及在 AOF 引擎出现错误时非常有用。
以下部分将说明这两种持久化模型的更多细节。
快照
默认情况下,Redis 会将数据集的快照保存到磁盘上,保存在名为dump.rdb
的二进制文件中。您可以配置 Redis 使其每 N 秒保存一次数据集(如果数据集至少发生了 M 次更改),或者您可以手动调用 SAVE
或 BGSAVE
命令。
例如,此配置将使 Redis 每 60 秒自动将数据集转储到磁盘,如果至少有 1000 个键发生更改
save 60 1000
这种策略被称为快照。
工作原理
每当 Redis 需要将数据集转储到磁盘时,就会发生以下情况
-
Redis fork。现在我们有一个子进程和一个父进程。
-
子进程开始将数据集写入一个临时 RDB 文件。
-
子进程完成写入新的 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。每当您发出 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 仍然包含一致的数据,这些数据代表数据集的某个时间点的版本(使用默认的 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 部分都可能会被丢弃,如果损坏发生在文件的初始部分,则会导致大量数据丢失。
工作原理
日志重写使用与快照相同的写时复制技巧。以下是工作原理
Redis >= 7.0
-
Redis fork,因此现在我们拥有一个子进程和一个父进程。
-
子进程开始在一个临时文件中写入新的基本 AOF。
-
父进程打开一个新的增量 AOF 文件以继续写入更新。如果重写失败,旧的基本文件和增量文件(如果有)加上这个新打开的增量文件代表了完整的更新数据集,因此我们是安全的。
-
当子进程完成基本文件的重写时,父进程会收到一个信号,并使用新打开的增量文件和子进程生成的基文件构建一个临时清单并将其持久化。
-
成功!现在 Redis 对清单文件执行原子交换,使这次 AOF 重写的结果生效。Redis 还清理旧的基本文件和任何未使用的增量文件。
Redis < 7.0
-
Redis fork,因此现在我们拥有一个子进程和一个父进程。
-
子进程开始在一个临时文件中写入新的 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_progress
和aof_rewrite_scheduled
变为0
,并验证aof_last_bgrewrite_status
为ok
来做到这一点。 - 重启服务器后,检查你的数据库是否包含与之前相同的键数量。
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 持久化
如果你运行的是只启用了 AOF 持久化的 Redis 实例,你仍然可以执行备份。从 Redis 7.0.0 开始,AOF 文件被拆分为多个文件,这些文件位于由 appenddirname
配置决定的单个目录中。在正常操作期间,你只需要复制/压缩此目录中的文件以实现备份。但是,如果在 重写 期间执行此操作,你可能会得到一个无效的备份。要解决此问题,你必须在备份期间禁用 AOF 重写
- 使用以下命令关闭自动重写
CONFIG SET
auto-aof-rewrite-percentage 0
确保在此期间你不会手动启动重写(使用BGREWRITEAOF
)。 - 使用以下命令检查是否有当前正在进行的重写
INFO
persistence
并验证aof_rewrite_in_progress
是否为 0。如果为 1,则需要等待重写完成。 - 现在你可以安全地复制
appenddirname
目录中的文件。 - 完成后重新启用重写
CONFIG SET
auto-aof-rewrite-percentage <prev-value>
注意:如果你想将 AOF 重写禁用的时间降到最低,你可以在 appenddirname
中创建指向这些文件的硬链接(在步骤 3 中),然后在创建硬链接后重新启用重写(步骤 4)。现在你可以复制/压缩这些硬链接,并在完成后删除它们。这是有效的,因为 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 客户端密钥,然后将其添加到小型 VPS 的
authorized_keys
文件中。你已准备好以自动化方式传输备份。为了获得最佳效果,请至少在两个不同的提供商中获取两个 VPS。
重要的是要了解,如果未以正确的方式实施,此系统很容易失败。至少,请务必确保在传输完成后,你能够验证文件大小(应与你复制的文件的大小相匹配),如果使用的是 VPS,还可以验证 SHA1 哈希值。
如果由于某种原因无法传输新的备份,你还需要某种独立的警报系统。