本篇文章主要针对 Redis 的两种持久化机制:AOF(Append-Only File)和 RDB(Redis DataBase)进行初步解析。本文侧重于 Redis 6.x 版本,不同版本可能有细微差异。
AOF 文件结构
本章节主要介绍AOF文件结构并通过一个简单的demo展示了AOF的具体内容。
AOF 简述:什么是 AOF? AOF 是 Redis 的一种持久化方案,用于记录服务器接收到的每一条写操作。这些操作在服务器启动时会被重新执行,从而重构原始的数据集。与 RDB 持久化方案不同,AOF 以日志文件的形式持续地保存数据更改。这种方式提供了更高的数据持久性。
主要特点:
1.数据恢复: AOF 文件中的命令可以在 Redis 启动时回放,以达到恢复数据的目的。
2.日志格式: 命令以 Redis 协议本身的相同格式进行记录。
3.文件增长: 由于 AOF 会以追加的方式记录每一条 Redis 的写命令,因此随着写命令的增多,AOF 文件也会逐渐增大。
4.日志重写(AOFRW): 由于 AOF 会记录每一个 Redis 命令,因此 AOF 文件会随时间不断增大。为了解决这个问题,Redis 提供了 AOF 文件重写功能,以“瘦身” AOF 文件。这通过创建一个新的 AOF 文件来实现,新旧两个 AOF 文件保存的数据是相同的,但新的 AOF 文件没有冗余命令。
工作机制
1.命令追加(Append): 当 AOF 持久化功能处于开启状态时,服务器会在执行完一个写命令后,将该命令以协议格式追加到一个名为 aof_buf 的缓冲区。
2.文件写入(WRITE)与同步(SYNC): aof_buf 缓冲区的内容会被写入到 AOF 文件中。Redis 提供了三种同步策略:
- appendfsync always: 将 aof_buf 缓冲区中的所有内容写入并同步到 AOF 文件。
- appendfsync everysec: 将 aof_buf 缓冲区中的所有内容写入 AOF 文件,如果距离上次同步的时间超过1秒,则进行两次同步操作。
- appendfsync no: 将 aof_buf 缓冲区中的所有内容写入 AOF 文件,但不进行同步,同步时间由操作系统决定。
在 Redis 的源代码中,AOF 持久化主要涉及以下几个关键部分:
- aof.c: 包含 AOF 持久化的主要逻辑。
- aof_rewrite(): 这是 AOF 重写(AOFRW)的核心函数。
- flushAppendOnlyFile(): 负责将缓冲区的数据写入到 AOF 文件。
AOF 文件结构:详细解析 AOF 文件的基本构成
AOF(Append Only File)文件是一个纯文本文件,用于记录 Redis 数据库中的所有写操作。这些写操作以 Redis 命令的形式存在,按照它们被执行的顺序进行记录。
AOF 文件结构较为简单,上图展示了AOF文件结构,键值对以文本指令的形式存储在文件中。
AOF 文件读取与解析
当 Redis 服务器启动时,它会读取 AOF 文件,并按照文件中的命令顺序重新执行这些命令,以此来恢复数据状态。读取和解析 AOF 文件的过程大致如下:
- 打开 AOF 文件。
- 逐行读取文件内容。
- 解析每一行为 Redis 命令和参数。
- 执行解析出的命令。
AOF 其他特性:Redis 7.0、优化
**
**
在 Redis 7.0 中,AOF(Append-Only File)持久化有了一些重要的更新和优化。根据 Redis 7.0-rc1 的发布说明,新版本引入了一种名为 "AOF log shipping" 的特性。这个特性允许 Redis 实例将 AOF 日志文件发送到远程服务器,从而提供了一种新的数据备份和恢复机制。
其次,根据一个 GitHub issue 的评论,Redis 7.0 还优化了 AOF 重写的性能。这主要是通过减少 AOF 重写期间的内存使用量来实现的,从而提高了大数据集下的性能。
AOF 持久化虽然提供了高度的数据安全性,但也可能影响 Redis 的性能。因此,了解如何优化 AOF 的性能是非常重要的。关于 AOF 性能优化的大致方向有以下几种:
1.使用合适的同步策略(fsync): Redis 提供了多种 fsync 策略,包括 always、everysec 和 no。选择合适的 fsync 策略可以显著影响 AOF 的性能。
2.定期重写 AOF 文件: AOF 文件会随着时间的推移而增长,这可能会影响性能。使用 BGREWRITEAOF 命令可以在后台重写 AOF 文件,从而优化其性能。
3.启用 AOF 与 RDB 的混合模式: 通过设置 aof-use-rdb-preamble 为 yes,可以使 AOF 文件包含一个 RDB 前缀,这样可以加速数据恢复速度并减小 AOF 文件的大小。
4.监控和调优: 使用监控工具来跟踪 AOF 的性能指标,如延迟、磁盘 I/O 等,并据此进行调优。
**
**
AOF 实践
**
**
为了在 Redis 中启用 AOF 持久化功能,可以更新 redis.conf 文件:
appendonly yes
appendonly yes
或者在 Redis CLI 中运行以下命令:
CONFIG SET appendonly yes
在我的机器上,执行了如下命令:
127.0.0.1:6379> config set appendonly yesOK127.0.0.1:6379> set a aOK127.0.0.1:6379> set b bOK
在这个 redis 中我插入了两个数据,对应的,我们可以在 AOF 文件中看到两次set的操作记录。
root@vagrant:~/redis/src/appendonlydir# cat appendonly.aof*2$6SELECT$10*3$3set$1a$1a*3$3set$1b$1b
RDB(Redis DataBase Backup)文件结构
本章节主要介绍RDB文件结构并借助 redis-rdb-cli工具解析了 RDB 的具体内容。
RDB 简述:什么是 RDB?
RDB 是 Redis 的另一种持久化方案,用于在特定的时间点对数据集进行快照存储。与 AOF(Append-Only File)持久化方案不同,RDB 生成的是一个二进制文件,该文件包含某一时刻 Redis 数据库中所有数据的副本。
**
**
主要特点
1.数据恢复: 通过加载 RDB 文件,Redis 可以在启动时快速恢复数据。
2.二进制格式: RDB 文件是一个经过压缩的二进制文件,因此占用的磁盘空间相对较小。
3.性能优化: 由于 RDB 是通过子进程进行快照生成的,因此对主进程的性能影响较小。
4.定时快照: 用户可以配置 Redis 在多少秒内有多少次写操作时进行快照,以满足不同的数据持久性需求。
工作机制
1.快照生成: 当满足用户配置的条件时,Redis 会 fork 出一个子进程来生成 RDB 文件。
2.数据加载: 在 Redis 启动或执行 restore 命令时,RDB 文件会被加载到内存中。
3.自动与手动触发: 除了自动触发 RDB 快照外,用户还可以手动通过 SAVE 或 BGSAVE 命令来生成 RDB 文件。
在 Redis 的源代码中,RDB 持久化主要涉及以下几个关键部分:
- rdb.c: 包含 RDB 持久化的主要逻辑。
- rdbSave(): 这是 RDB 快照生成的核心函数。
- rdbLoad(): 负责从 RDB 文件中加载数据到 Redis 数据库。
RDB 文件结构:详细解析
Magic Number (REDIS)
- 长度:5 字节
- 作用:Magic Number 是用于标识一个文件是否为 RDB 格式的文件。这是 RDB 文件的第一个字段,用于快速识别文件类型。
- 示例:REDISMagic Number 是 RDB 文件的“签名”,当 Redis 或其他工具尝试读取一个 RDB 文件时,会首先检查这 5 个字节。如果这些字节不是 "REDIS",则文件会被认为是无效的。
RDB Version Number
**
**
- 长度:4 字节
- 作用:这个字段表示 RDB 文件的版本号,用于处理向后兼容性。
- 示例:0007不同版本的 Redis 可能会有不同的 RDB 格式。通过这个版本号,Redis 可以知道如何正确地解析文件。
Database Blocks
- 包含多个数据库(通常是 16 个),每个数据库都有自己的键值对。
- 每个数据库块开始于一个特定的标识符(例如,FE 表示一个新的数据库)。Database Blocks 是 RDB 文件中的主体部分,包含了所有数据库的数据。每个数据库都有一个或多个键值对,这些键值对以特定的编码(例如,字符串、列表、集合等)存储。
Key-Value Pairs
- 包含键和值的类型、长度和实际数据。
- 类型通常由一个字节表示,例如,string 类型可能由 00 表示。这是 RDB 文件中最核心的部分,它描述了实际的数据。每个键值对都有一个类型标识符和相应的数据。
End Of File (EOF)
- 一个特殊的标识符,表示 RDB 文件的结束。
- 通常是 FF。这是一个特殊的标识符,用于表示 RDB 文件的结束。当解析器读到这个标识符时,它会知道所有的数据库块和键值对都已经读取完毕。
Checksum
- 长度:8 字节
- 作用:用于验证 RDB 文件的完整性。Checksum 是所有前面数据的 CRC64 校验和。这用于在读取文件时检测任何潜在的错误或数据损坏。
下图图是一个实际的 RDB FILE 二进制文件的 16 进制的表示形式。根据上述的RDB文件的整体结构我们可以得到如下的解析结果。
String 类型数据存储结构
存储方式
在 RDB 文件中,String 是最常见的数据结构,有三种主要的存储方式:
-
整数存储:如果字符串是一个 8、16 或 32 位的整数,它会被直接存储为一个整数。这种方式非常高效,因为整数通常会占用更少的存储空间,并且在读取时也更快。
-
LZF 压缩:如果字符串的长度大于 20 个字符,并且 LZF 压缩被启用,那么在存储之前,字符串会被压缩。这种方式在处理大量长字符串时非常有用,可以显著减少存储空间。
-
字节数组存储:在其他所有情况下,字符串会被存储为一个简单的字节数组。这是最直接的存储方式,不涉及任何额外的处理或转换。
长度编码字段(Length Encoding Field)
**
**
长度编码字段是一个非常重要的组成部分,用于指示如何存储接下来的字符串结构。这个字段通常是一个字节长,其中最重要的两位用于指示字符串的存储方式。
-
字节数组存储: 如果最高的两位是 00、01 或 10,字符串会被存储为一个字节数组。这里,接下来的位数(可能是 6 位、14 位或 32 位)用于表示字节数组的实际长度。
-
整数存储: 如果最高的两位是 11,并且接下来的 6 位中的值是 0、1 或 2,那么这表示字符串被存储为一个 8 位、16 位或 32 位的整数。
-
LZF 压缩存储: 如果最高的两位是 11,并且接下来的 6 位中的值是 4,这表示字符串被 LZF 压缩后存储。这里,还会有额外的字段来表示压缩前和压缩后的长度。
下面两个表格是string 二进制存储文件的16进制表示以及字段解析后的含义。
RDB 其他特性:压缩优化、其他配置
压缩优化
**
**
在 Redis 中,RDB 文件默认使用基于 LZF 的压缩算法进行优化。这种压缩算法非常高效,能够在不牺牲读取速度的前提下显著减小 RDB 文件的大小。这对于需要定期备份或迁移数据的场景尤为重要。
在 redis.conf 配置文件中,你可以通过以下选项来启用或禁用 LZF 压缩:
rdbcompression yes # 启用 LZF 压缩rdbcompression no # 禁用 LZF 压缩
启用压缩会稍微增加 CPU 的使用率,因为需要进行压缩和解压缩操作。但通常这个开销是可以接受的,特别是考虑到它带来的存储优势。
RDB 文件名和路径:
默认情况下,RDB 文件保存在 Redis 服务器的当前目录下,文件名为 dump.rdb。你可以通过 dbfilename 和 dir 选项来自定义文件名和路径。
dbfilename mydump.rdb # 自定义 RDB 文件名dir /path/to/redis/data # 自定义 RDB 文件路径
自动保存配置:
通过 save 配置选项,你可以设置在多少秒内,如果有多少次写操作,则自动创建 RDB 文件。
save "900 1" # 在 900 秒内如果有 1 次写操作则保存save "300 10" # 在 300 秒内如果有 10 次写操作则保存
数据完整性校验:
你可以通过 rdbchecksum 选项来启用或禁用数据完整性校验。
rdbchecksum yes # 启用数据完整性校验rdbchecksum no # 禁用数据完整性校验
RDB 文件解析工具:实用工具介绍
解析 RDB 文件可以采用 leonchen83/redis-rdb-cli 这个开源的工具,该工具可以用于解析Redis dump.rdb文件,分析内存,并将数据导出到JSON。
在我的机器上,执行了如下命令:
127.0.0.1:6379> set a aOK127.0.0.1:6379> set b bOK127.0.0.1:6379> lpush mylist c c(integer) 2127.0.0.1:6379> saveOK
在这个 redis 中我插入了两个数据,并创建了一个list对象push了两个数据,接下来我们可以采用 rct 工具进行解析,从rdb文件中看到三个数据对象的值。
root@vagrant:~/redis/src/redis-rdb-cli/bin# ./rct -f json -s /root/redis/src/dump.rdb -o out.json
root@vagrant:~/redis/src/redis-rdb-cli/bin# cat out.json | jq .[{ "key": "mylist", "value": [ "c", "c" ], "db": 0, "type": "list"},{ "key": "a", "value": "a", "db": 0, "type": "string"},{ "key": "b", "value": "b", "db": 0, "type": "string"}]
总结而言,Redis的两种主要持久化机制,AOF(Append-Only File)和 RDB(Redis DataBase Backup),各自拥有独特的优点和局限性。AOF通过实时记录所有写操作指令来提供更高的数据持久性,但这种方式通常需要更多的磁盘空间和更长的数据恢复时间。相对而言,RDB通过在特定时间点创建数据快照来实现数据持久化,这不仅使得数据恢复更快,还通过二进制格式和压缩算法减少了存储需求。不过,RDB方式可能会导致在快照之间进行的数据更改丢失。对于解析和管理这些文件,存在多种工具和配置选项,允许开发者根据具体需求进行灵活的优化。在实际应用场景中,选择哪一种持久化方案(或是两者的结合)应根据数据的大小、访问速度、数据安全性需求以及硬件资源来综合考虑。
参考
-
Redis 7.0 Multi Part AOF的设计和实现这篇文章由阿里云开发者社区发布,专门探讨了 Redis 7.0 中多部分 AOF 的设计和实现。
-
Redis Persistence Deep Dive这篇文章深入探讨了 Redis 的持久化机制,对 aof rdb 的特性进行了很多对比,涉及了相关源码的解析。
-
Xu, Ming, et al. "A Forensic Analysis Method for Redis Database based on RDB and AOF File." J. Comput. 9.11 (2014): 2538-2544.