Redis 持久化
Redis如何将数据写入磁盘
持久性指的是将数据写入持久存储,例如固态硬盘(SSD)。Redis 提供了一系列持久性选项。这些选项包括:
- RDB (Redis 数据库): RDB 持久化在指定的时间间隔内对您的数据集执行时间点快照。
- AOF (Append Only File): AOF持久化记录服务器接收到的每个写操作。这些操作可以在服务器启动时再次重放,重建原始数据集。命令使用与Redis协议本身相同的格式记录。
- 无持久化:您可以完全禁用持久化。这在缓存时有时会使用。
- RDB + AOF: 你也可以在同一实例中同时使用AOF和RDB。
如果您不想考虑这些不同持久化策略之间的权衡,您可能想要考虑Redis Enterprise的持久化选项,这些选项可以通过用户界面进行预配置。
要了解更多关于如何评估您的Redis持久化策略的信息,请继续阅读。
RDB 优势
- RDB 是一个非常紧凑的单文件时间点表示您的 Redis 数据。RDB 文件非常适合备份。例如,您可能希望每小时归档一次 RDB 文件以保存最近 24 小时的数据,并且每天保存一次 RDB 快照以保存 30 天的数据。这使您可以在灾难发生时轻松恢复不同版本的数据集。
- RDB 非常适合灾难恢复,因为它是一个单一的紧凑文件,可以传输到远距离的数据中心,或者上传到 Amazon S3(可能加密)。
- RDB 最大限度地提高了 Redis 的性能,因为 Redis 父进程为了持久化唯一需要做的工作就是 fork 一个子进程,子进程将完成所有其余的工作。父进程永远不会执行磁盘 I/O 或类似操作。
- 与AOF相比,RDB允许在大型数据集上更快地重启。
- 在副本上,RDB支持重启和故障转移后的部分重新同步。
关系型数据库的缺点
- 如果你需要最小化在Redis停止工作(例如断电后)时数据丢失的可能性,RDB并不适合。你可以配置不同的保存点来生成RDB(例如在至少五分钟和100次写操作之后,你可以有多个保存点)。然而,你通常每五分钟或更长时间创建一个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,使其在数据集至少有M次更改时,每N秒保存一次数据集,或者你可以手动调用SAVE
或BGSAVE
命令。
例如,此配置将使Redis在至少1000个键更改时,每60秒自动将数据集转储到磁盘:
save 60 1000
这种策略被称为快照。
它是如何工作的
每当Redis需要将数据集转储到磁盘时,会发生以下情况:
-
Redis forks。我们现在有一个子进程和一个父进程。
-
子进程开始将数据集写入一个临时的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
-
可选地使用
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 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_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
注意: 如果你想最小化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摘要。
如果由于某种原因无法传输新的备份,您还需要某种独立的警报系统。