Redis 选 RDB 和 AOF 双重持久化,背后逻辑大起底!

在这篇文章里,我们将一同探讨Redis持久化机制的实现原理,以及Redis为何采用RDB和AOF这两种持久化方式。

1. 什么是持久化?

持久化,英文为Persistence,其核心是将数据(例如内存中的对象)存储到可永久性保存数据的存储设备(比如磁盘)中。这一过程意义重大,它确保了程序在服务器遭遇宕机等意外情况后重新启动时,数据不会丢失,保障了系统的数据完整性与连续性。

2. 持久化方式

Redis提供了两种不同的持久化方式,分别是RDB和AOF 。这两种方式各有特点,在Redis的数据持久化中发挥着关键作用。

2.1 RDB

RDB,即Redis DataBase的缩写。这种持久化方式是按照一定的时间间隔,定期将Redis内存中的数据以快照的形式存储到磁盘上,生成一个RDB文件。该文件完整地记录了Redis在某个特定时间点的数据状态,相当于一份数据的“快照”。当Redis重新启动时,系统会自动加载这个RDB文件,依据其中的数据快照,将Redis的数据恢复到上次生成快照时的状态,极大地提高了数据恢复的效率和便捷性。

2.2 AOF

AOF,全称是Append Only File。它的工作原理是将Redis执行的每一条写命令,按照执行顺序追加到AOF文件的末尾。每当Redis启动时,系统会依次重新执行AOF文件中记录的所有写命令,从而根据这些操作记录重新构建数据集。这种方式最大的优势在于能够确保数据的完整性,几乎不会出现数据丢失的情况。然而,由于每一次写操作都需要记录到AOF文件中,随着时间的推移和写操作的不断增加,AOF文件的体积会变得越来越大,这在一定程度上会对系统的存储和性能产生影响 。

实现原理图如下:

3. RDB原理

Redis DataBase(RDB)是一种将Redis内存中的数据,以二进制形式按照一定周期或在指定时间间隔进行快照,并存储到磁盘上,进而生成一个RDB文件的持久化机制。该文件完整记录了Redis在特定时间点的数据状态。当Redis重新启动时,便能通过加载这个RDB文件,快速恢复数据。其原理主要涵盖以下3个关键流程:

3.1 触发持久化

Redis提供了两种触发持久化的方式。其一,基于预设的时间间隔,系统会自动触发持久化操作,以此保障数据在一定时间周期内得到备份。其二,用户可手动执行SAVE或BGSAVE命令来触发。需特别注意的是,手动执行BGSAVE命令时,Redis能够在持续对外提供服务的同时,后台进行持久化操作,几乎不影响系统的正常运行;而手动执行SAVE命令则会阻塞Redis的服务进程,直至持久化操作全部完成,这期间Redis无法响应其他请求。因此,在生产环境中,为避免对业务造成影响,应谨慎使用SAVE指令。

3.2 生成RDB文件

一旦Redis触发了持久化操作,便会通过fork机制创建一个子进程。这个子进程专门负责生成RDB文件。在生成过程中,Redis会依据特定的格式,将内存中的各类数据,如键值对、过期时间、数据库号等关键信息,逐一写入到RDB文件中。这种严谨的数据写入方式,确保了RDB文件能够完整且准确地记录Redis在触发持久化时刻的内存数据状态。

3.3 完成持久化

当子进程成功完成RDB文件的生成后,Redis会将原有的RDB文件替换为新生成的文件,以此完成整个持久化流程。在这一替换环节,Redis会暂时阻塞所有写操作。这是为了确保在文件替换过程中,不会有新的写操作干扰RDB文件的数据一致性,保证新的RDB文件能准确反映持久化操作完成时的Redis数据状态 。

Redis Server 自动创建 RDB 文件的默认配置在 redis.conf 里,内容如下:

save 900 1 # 服务器在900s(15分钟)之内,对数据进行了至少1次修改
save 300 10  #服务器在300s(5分钟)之内,对数据进行了至少10次修改
save 60 10000 #服务器在60s(1分钟)之内,对数据进行了至少10000次修改

RDB文件格式

RDB文件格式是一个精心设计的结构,由多个关键部分构成,各部分各司其职,共同保障数据的有效存储与准确恢复:

  • RDB文件头部:这部分起始位置包含一个由“REDIS”字样组成的字符串,它就像是RDB文件的身份标识,表明该文件是Redis的持久化数据文件。紧接着的是版本号信息,版本号记录了RDB文件生成时所遵循的Redis版本规范,不同版本的Redis在RDB文件结构和内容编码上可能存在差异,通过版本号可以让Redis在加载文件时准确解析其中的数据。
  • 保存键值对数据:这是RDB文件的核心数据存储部分,以“SELECTDB”命令作为开头标识。随后紧跟一个4字节的整数,该整数用于明确当前数据所属的数据库编号,因为Redis支持多个数据库,通过这种方式可以精准区分不同数据库的数据。在数据库编号之后,便是一系列的键值对数据。每个键值对又细分为3个部分:首先是键的长度,用于准确界定键的内容所占字节数;接着是键的具体内容;最后是对应的值的内容。这样的结构设计确保了数据在存储和读取时的准确性与高效性。
  • 保存过期时间:RDB文件具备保存键值对过期时间的功能,这对于维护数据的时效性至关重要。当Redis重启后,系统能够依据这些保存的过期时间信息,自动删除已经过期的键值对,从而保证数据的一致性和有效性。过期时间的记录以“EXPIRETIME_MS”命令开头,随后是对应的键以及精确到毫秒的过期时间等相关信息。
  • RDB文件尾部:这部分包含一个8字节的校验和,它如同文件的“数据卫士”。校验和通过特定的算法,基于文件的其他部分内容计算得出。在Redis加载RDB文件时,会重新计算文件的校验和,并与文件尾部保存的校验和进行比对。如果两者一致,说明RDB文件在存储或传输过程中未被修改,数据完整可靠;若不一致,则表明文件可能已损坏或被篡改,Redis会采取相应的错误处理措施 。

以下是一个简单的 RDB文件示例:

REDIS0006   // RDB文件头部
SELECTDB 0  // 选择数据库0
$3foo$3bar  // 键为"foo",值为"bar"
$3abc$5hello  // 键为"abc",值为"hello"
EXPIRETIME_MS $3foo$4     // 设置键"foo"的过期时间为4秒
00000000    // RDB文件尾部的校验和

在读取RDB文件时,Redis会按照上述格式逐个解析RDB文件的每个部分,并将数据加载到内存中。如果Redis在重启时检测到了存在RDB文件,它将读取RDB文件,并将其中的数据加载到内存中,以便快速恢复数据。

4. AOF 原理

Append Only File(AOF),将 Redis 执行的每一条写命令追加到 AOF 文件的末尾,每次Redis启动时重新执行 AOF 文件中的命令,从而重新构建数据集。通过这种方式,可以保证 Redis 的数据不会丢失。

AOF 实现原理核心流程如下:

4.1 AOF持久化策略

Redis为用户提供了三种灵活的AOF持久化策略,以满足不同场景下的数据持久化需求:

  1. always(始终):此策略最为严格,每当Redis执行一条写命令时,都会立即将该命令追加到AOF文件中。这一策略确保了数据的实时持久化,最大程度地降低了数据丢失的风险。然而,频繁的磁盘I/O操作可能会对Redis的性能产生一定影响,因为每次写操作都需要与磁盘进行交互。
  2. everysec(每秒):这是一种折中的策略,Redis会每秒执行一次AOF持久化操作。在这一秒的时间间隔内,Redis会将积累的所有写命令一次性追加到AOF文件中。这种方式在性能和数据安全性之间取得了较好的平衡,既不会像always策略那样频繁进行磁盘I/O导致性能大幅下降,又能将数据丢失的风险控制在一秒以内。在大多数生产环境中,everysec策略是较为常用的选择。
  3. no(从不):选择此策略意味着Redis不会主动进行AOF持久化操作。在这种情况下,AOF文件不会自动更新,数据仅存储在内存中。虽然这种方式可以显著提高Redis的写入性能,因为避免了磁盘I/O带来的开销,但同时也伴随着极高的数据丢失风险。一旦服务器出现故障,内存中的数据将全部丢失,因此该策略仅适用于对数据完整性要求极低,且能够容忍数据丢失的特殊场景 。

4.2 AOF文件格式

AOF文件采用的格式是将一系列Redis命令进行序列化处理后的呈现形式。Redis会把需要写入AOF文件的命令,转化为一连串的字符串,然后依次添加到AOF文件的尾部。这些被写入的命令涵盖了诸如SET(设置键值对)、DEL(删除键)、INCR(对数值类型的键执行递增操作)等写操作指令。然而,对于读命令,例如GET(获取键对应的值)、HGET(获取哈希表中指定字段的值)等操作,Redis并不会将它们写入AOF文件。这是因为AOF文件的主要目的是通过记录写操作来重现数据状态,读操作并不影响数据的持久化状态。

4.3 AOF重写

AOF重写机制主要是为应对AOF文件随着时间推移和频繁写操作而不断增大的问题。其核心原理是依据Redis当前内存中的数据状态,重新构建一个全新的AOF文件,并使用这个新文件替换原有的旧文件。在重写过程中,Redis会对一段时间内的写命令进行优化整合,将多条写命令合并重写为一条SET命令或者DEL命令。例如,多个对同一键的连续修改操作,可能会被合并为最终结果对应的一条SET命令。这样一来,有效减小了AOF文件的大小。值得注意的是,在重写过程中,Redis会基于内存中的数据来生成新的AOF文件,并且为了保证数据的一致性,在生成新文件期间会暂停所有写操作。

4.4 完成持久化

当Redis触发AOF持久化操作时,它会先将内存中积累的写命令追加到AOF文件的末尾。完成追加操作后,Redis会调用fsync系统调用,这一操作的关键作用是将刚刚写入AOF文件的内容强制刷新到磁盘中。通过这种方式,确保了即使在系统出现故障或断电等意外情况下,已写入的数据也不会丢失,从而有力地保障了数据的安全性。

AOF 文件格式

AOF文件的格式非常简单,它是一个文本文件,每行都记录了一条Redis命令。每条命令都以”$“开头,表示命令长度,接着是命令的具体内容,以”\r\n”结尾。例如,以下是一条SET命令的AOF格式:

$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n

其中,”表示命令的长度为,表示换行符,5\r\n”表示key的长度为5,”mykey”是key的具体内容,”\r\n”表示换行符,”$7\r\n”表示value的长度为7,”myvalue”是value的具体内容,”\r\n”表示换行符。

在AOF文件中,除了记录命令外,还有一些特殊的标记,如”*\r\n”表示一次多个命令的操作,”$-1\r\n”表示空值,”:0\r\n”表示数字0等。

5. 优缺点

在Redis的持久化机制中,RDB和AOF各有优劣,深入了解这些特性有助于在实际应用中做出更合适的选择。

5.1 RDB 优缺点

优点

RDB生成的是一个完整的数据快照文件,在进行数据备份和恢复操作时,无需像逐条处理数据那样繁琐。通过直接加载这个快照文件,能够快速完成数据的恢复过程,极大地提高了数据备份和恢复的效率,尤其适用于大规模数据的快速还原场景。

缺点

RDB持久化是按照一定时间间隔进行快照的,如果Redis在两次快照之间发生意外宕机,那么从最近一次快照时间点到宕机时刻之间新写入的数据将会丢失。这意味着在数据安全性方面,RDB存在一定的数据丢失风险,不太适合对数据实时完整性要求极高的场景。

5.2 AOF 优缺点

优点

AOF通过记录每一条写命令来持久化数据,这使得它能够精准地重现数据的变化过程。即使Redis遭遇意外宕机,也可以借助AOF文件中记录的写命令,按照顺序重新执行,从而完整地恢复数据,最大限度地保障了数据的完整性,适用于对数据一致性要求严格的应用场景。

缺点

由于AOF需要对每一次写操作进行记录,随着时间的推移和业务量的增长,AOF文件的体积会不断膨胀。文件过大不仅会占用大量的磁盘空间,还可能导致读写性能下降。此外,AOF重写操作虽然能够减小文件大小,但在重写过程中会占用大量的CPU和内存资源,这期间可能会对Redis的正常运行产生一定的影响,降低系统的整体性能。

为了充分发挥RDB和AOF的优势,同时弥补各自的不足,Redis提供了两种混合持久化方式:

  • AOF重写:Redis会在后台启动一个独立进程来执行AOF重写任务。在重写过程中,该进程会遍历AOF文件,识别并删除其中的无效命令,例如那些对最终数据状态没有实际影响的重复命令或已被覆盖的命令。通过这种方式,有效缩小AOF文件的大小,减少磁盘空间占用,同时提升AOF文件的读写性能,降低对系统资源的消耗。
  • AOF和RDB同时开启:在这种模式下,Redis同时利用AOF和RDB两种方式进行数据持久化。当Redis重启时,系统会优先尝试使用AOF文件来恢复数据,因为AOF文件记录了更详细的写操作历史,能够保证数据的最新状态。只有在AOF文件不存在或者由于损坏等原因无法正常使用时,Redis才会转而使用RDB文件来恢复数据。这种双重保障机制,既兼顾了AOF的数据完整性优势,又利用了RDB在数据恢复速度上的长处,为Redis的数据持久化提供了更可靠的解决方案 。

6. AOF 重写机制

AOF 重写机制是 Redis 为解决 AOF 文件体积过大问题而设计的优化策略。当 AOF 文件因持续记录写操作而逐渐膨胀时,AOF 重写机制可以生成一个全新的、更加紧凑的 AOF 文件,从而有效减少文件的大小,显著提升 Redis 的运行性能。

AOF 重写机制的实现原理可以概括为以下三个关键步骤:

1. 触发 AOF 重写

Redis 会周期性检查 AOF 文件的大小。一旦 AOF 文件超出预设的阈值,便会自动触发 AOF 重写。在默认配置下,当 AOF 文件的大小达到 64MB 时,Redis 会启动该机制。如果用户希望调整这一阈值,可以通过修改配置文件中的 auto-aof-rewrite-percentageauto-aof-rewrite-min-size 参数来实现。前者控制 AOF 文件自上次重写后的增长百分比,后者则设定触发重写的最小文件尺寸。

2. 执行 AOF 重写

AOF 重写操作是在一个独立的子进程中执行的。在此过程中,子进程会遍历 Redis 内存中的数据结构,对一定时间段内的写命令进行优化和整合。具体来说,它会将这些命令重写为更简洁的形式,例如将多条命令合并为一条 SET 命令或 DEL 命令,并将优化后的命令写入新生成的 AOF 文件。需要注意的是,在重写过程中,Redis 会比较新旧 AOF 文件中的命令序列。若发现相同的命令序列,Redis 会用旧 AOF 文件中的对应部分替换新 AOF 文件中的内容,以进一步减小文件体积,优化压缩效果。

3. 完成 AOF 重写

当 AOF 重写操作完成后,Redis 会将新生成的 AOF 文件替换掉旧文件。接着,Redis 会通过执行 fsync 系统调用,将新写入的数据强制刷新到磁盘。这一步骤保证了数据在持久化过程中的安全性和完整性,即使遇到系统故障或突然断电等意外情况,已写入的数据也能得到妥善保存。

AOF 重写机制的显著优点在于它能够有效减小 AOF 文件的体积,从而优化 Redis 的性能,提高数据读写效率。然而,这一机制也并非没有缺点。在重写过程中,AOF 的操作会消耗较多的 CPU 和内存资源,尤其在数据量较大的情况下,这种资源消耗可能会对 Redis 性能产生一定影响。因此,在实际应用中,需要根据具体情况合理评估和调整。