写点什么

【Redis 技术进阶之路】「原理分析系列开篇」探索事件驱动枚型与数据特久化原理实现(数据持久化的实现 AOF)

作者:码界西柚
  • 2025-03-21
    江苏
  • 本文字数:6088 字

    阅读完需:约 20 分钟

【Redis技术进阶之路】「原理分析系列开篇」探索事件驱动枚型与数据特久化原理实现(数据持久化的实现AOF)

【专栏简介】

随着数据需求的迅猛增长,持久化和数据查询技术的重要性日益凸显。关系型数据库已不再是唯一选择,数据的处理方式正变得日益多样化。在众多新兴的解决方案与工具中,Redis 凭借其独特的优势脱颖而出。

【技术大纲】

为何 Redis 备受瞩目?原因在于其学习曲线平缓,短时间内便能对 Redis 有初步了解。同时,Redis 在处理特定问题时展现出卓越的通用性,专注于其擅长的领域。深入了解 Redis 后,您将能够明确哪些任务适合由 Redis 承担,哪些则不适宜。这一经验对开发人员来说是一笔宝贵的财富。



在这个专栏中,我们将专注于 Redis 的 6.2 版本进行深入分析和介绍。Redis 6.2 不仅是我个人特别偏爱的一个版本,而且在实际应用中也被广泛认为是稳定性和性能表现都相当出色的版本

【专栏目标】

本专栏深入浅出地传授 Redis 的基础知识,旨在助力读者掌握其核心概念与技能。深入剖析了 Redis 的大多数功能以及全部多机功能的实现原理,详细展示了这些功能的核心数据结构和关键算法思想。读者将能够快速且有效地理解 Redis 的内部构造和运作机制,这些知识将助力读者更好地运用 Redis,提升其使用效率。


将聚焦于 Redis 的五大数据结构,深入剖析各种数据建模方法,并分享关键的管理细节与调试技巧。

【目标人群】

Redis 技术进阶之路专栏:目标人群与受众对象,对于希望深入了解 Redis 实现原理底层细节的人群

1. Redis 爱好者与社区成员

Redis 技术有浓厚兴趣,经常参与社区讨论,希望深入研究 Redis 内部机制、性能优化和扩展性的读者。

2. 后端开发和系统架构师

在日常工作中经常使用 Redis 作为数据存储和缓存工具,他们在项目中需要利用 Redis 进行数据存储、缓存、消息队列等操作时,此专栏将为他们提供有力的技术支撑。

3. 计算机专业的本科生及研究生

对于学习计算机科学、软件工程、数据分析等相关专业的在校学生,以及对 Redis 技术感兴趣的教育工作者,此专栏可以作为他们的学习资料和教学参考。


无论是初学者还是资深专家,无论是从业者还是学生,只要对 Redis 技术感兴趣并希望深入了解其原理和实践,都是此专栏的目标人群和受众对象


让我们携手踏上学习 Redis 的旅程,探索其无尽的可能性!



AOF 持久化

Redis 不仅集成了高效的 RDB(Redis Database)持久化机制,还独具匠心地引入了 AOF(Append Only File)持久化技术,为数据持久性提供了更为灵活与详尽的解决方案。与 RDB 侧重于定期生成数据库快照的方式不同,AOF 机制另辟蹊径,通过记录 Redis 服务器执行的所有写操作命令(如 SET、INCR 等),以日志的形式逐条追加到文件中,从而实现对数据库状态的全面追踪与记录,如下图展示的那样。



这种设计不仅使得数据恢复过程更加细致入微,能够精确到每一次写操作的执行,还赋予了用户根据实际需求调整日志记录频率与策略的能力,比如通过配置命令同步的时机(每秒、每命令或手动触发),来平衡数据安全性与磁盘 I/O 开销。

案例说明

举例来说,倘若我们在一个空白的数据库上执行以下写入操作指令,随后,该数据库中将会精准地填充上三个精心构建的键值对,从而赋予其初始的数据结构与内容。


redis>SET msg "hello world"OK
redis>SADD seqNo 1 2 3(integer)3
redis>RPUSH numbers 128 256 512(integer)3
复制代码


RDB 持久化机制通过捕捉并保存数据库中特定键(如msgseqNonumbers)的键值对快照至 RDB 文件中,以此来实现数据库状态的持久化。相比之下,AOF 持久化则采取了截然不同的策略,它专注于记录 Redis 服务器实际执行的写操作命令(如SETSADDRPUSH等),并将这些命令序列以追加的方式写入 AOF 文件中,从而实现对数据库状态变化的精确追踪与保存。

AOF 文件格式

被写入 AOF 文件的所有命令均严格遵循 Redis 的命令请求协议格式进行保存,由于 Redis 的命令请求协议采用了易于阅读的纯文本格式,这一特性使得我们无需任何特殊工具即可直接打开 AOF 文件,进而直观地查看并理解文件中所记录的 Redis 操作命令及其具体内容,如下所示:


*2\r\n$6\r\nSELECT\r\n$1\r\no\r\n*3\r\n$3\r\nSET\r\n$3\r\nmsg\r\n$11\r\hello world\r\n*5\r\n$4\r\nSADD\r\n$6\r\nseqNo\r\n$5\r\napple\r\n$6\r\nbanana\r\ns6\r\ncherry\r\n*5\r\n$5\r\nRPUSH\r\n$7\r\nnumbers\r\n$3\r\n128\r\n$3\r\n256\r\ns3\r\n512\r\n
复制代码


在这份 AOF(Append Only File)文件中,除了那些由服务器自动插入的、专门用于指定数据库的 SELECT 命令之外,其余所有内容均源自我们之前通过客户端精心发送的指令。

AOF 启动的日志

当服务器启动时,它具备一项强大的功能,即通过加载并执行 AOF 文件中精心保存的指令序列,来精准地还原至服务器上次关闭前的数据库状态。这一过程不仅确保了数据的持续性和完整性,还极大地简化了数据恢复的操作流程。


[18321] 05 Aug 12:58:50.448 Server started,Redisversion 5.0.12[18321] 05 Aug 12:58:50.449DB loaded from append only file:0.000 seconds[18321] 05 Aug 12:58:50.449 The server is now ready to accept connections on port6379
复制代码


以上展示的日志,详细记录了服务器载入 AOF 文件并逐步还原数据库状态的全过程,每一步都清晰地反映了数据恢复工作的进展与成功。

AOF 持久化的实买现

AOF 持久化机制的实现精妙地划分为三个核心步骤:命令追加(append)、文件写入以及文件同步(sync)。首先,通过命令追加,服务器将接收到的每一个写操作指令以文本形式即时添加到 AOF 文件的末尾,确保了操作的即时记录。紧接着,在文件写入阶段,这些追加的命令被安全地存储至磁盘之中,构建起数据的持久化存储基础。

命令追加

当 AOF(Append-Only File)持久化功能被激活启用时,服务器在完成一个写命令的执行流程后,会自动遵循协议的既定格式,将被成功执行的写命令追加至其内部维护的aof_buf缓冲区末端。这一操作确保了所有执行的写操作都能被准确地记录并保留,为后续的数据恢复与一致性校验提供了坚实保障。


struct redisServer{    //AOF 缓冲区    sds aof_buf;}
复制代码


举个具体的例子来说,当客户端向服务器发送如下命令时:


redis>SET KEY VALUEOK
复制代码


"SET KEY VALUE",这一指令旨在要求服务器在数据库中设置(或更新)一个键值对,其中键为KEY ,值为VALUE


服务器接收到该命令后,会首先解析并执行它,即在内部数据结构中创建或更新相应的键值对。紧接着,如果 AOF 持久化功能处于启用状态,服务器还会按照 AOF 协议的规定,将这条SET命令以文本形式(如"SET KEY VALUE\r\n")追加到aof_buf缓冲区中,以便之后能够将这些命令写入 AOF 文件,实现数据的持久化存储。


* 3\r\ns3\r\nSET\r\ns3\r\nKEY\r\ns5\r\nVALUE\r\n
复制代码


再举一例,当客户端向服务器发送如下指令时:


redis>RPUSH NUMBERS ONE TWO THREE(integer)3
复制代码


随后,服务器在执行了 RPUSH 命令之后,会将以下协议内容优雅地追加至 AOF(Append Only File)缓冲区的末尾:


*5\r\n$5\r\nRPUSH\r\n$7\r\nNUMBERS\r\n$3\r\nONE\r\n$3\r\nTWo\r\n$5\r\nTHREE\r\n
复制代码

AOF 文件的写入与同步

Redis 服务器进程核心是一个事件循环,分为文件事件和时间事件两类。文件事件处理客户端命令请求与回复,而时间事件则定期执行如serverCron等定时任务。


服务器在每个事件循环结束时,检查 aof_buf 缓冲区,若包含待写入命令,则调用 flushAppendOnlyFile 函数决定是否将其内容追加并保存到 AOF 文件中。伪代码如下:


def eventLoop():    while True:        # 处理文件事件,包括接收命令请求和发送命令回复        processFileEvents()        # 可能因处理命令请求而追加内容到AOF缓冲区        # 处理时间事件        processTimeEvents()        # 考虑并可能将AOF缓冲区内容写入并保存到AOF文件        flushAppendonlyFile()
复制代码


这段代码清晰地展示了 Redis 事件循环的核心部分,包括处理文件事件(涉及客户端交互)、处理时间事件(如定期任务),以及在每次循环结束时考虑将 AOF 缓冲区内容写入 AOF 文件的过程。

flushAppendOnlyFile

flushAppendOnlyFile函数的行为受服务器配置的appendfsync选项值控制,不同值对应不同行为,如表 11-1 所示。此函数根据appendfsync的设置,决定何时将 AOF 缓冲区内容写入并同步到 AOF 文件中。

appendfsync 值产生不同的持久化行为

若用户未显式设置appendfsync选项,其默认值为everysec。欲了解更多关于appendfsync选项的信息,请参阅 Redis 项目附带的示例配置文件redis.conf


文件的写入和同步

为提高文件写入效率,现代操作系统在用户调用write函数写入数据时,会先将数据暂存于内存缓冲区。待缓冲区满或超时后,再批量写入磁盘。此方法虽高效,但存在安全风险:系统崩溃时,缓冲区数据可能丢失。为此,系统提供了fsyncfdatasync函数,可立即将缓冲区数据同步至硬盘,保障数据安全。

案例分析

举个例子,服务器在处理文件事件时,连续执行了三个写入命令。


SADD databases "Redis""MongoDB""MariaDB"SET date "2013-9-5"INCR click_counter 10086
复制代码


那么,AOF buf(缓冲区)将包含这三个命令的协议化内容。


*5\r\n$4\r\nSADD\r\n$9\r\ndatabases\r\n$5\r\nRedis\r\n$7\r\nMongoDB\r\n$7\r\nMariaDB\r\n3\r\n$3\r\n$ET\r\n$4\r\ndate\r\n$8\r\n2013-9-5\r\n*3\r\n$4\r\nINCR\r\n$13\r\nclick counter\r\n$5\r\n10086\r\n
复制代码


flushAppendonlyFile被调用,且appendfsync设为everysec且距上次同步已超一秒,服务器将先将aof_buf内容写入 AOF 文件,并同步文件。

AOF 文件的载入与数据还原

AOF 文件包含重建数据库所需的所有写命令,通过读取并执行这些命令,服务器能还原至关闭前的数据库状态。Redis 读取 AOF 文件还原数据库状态的步骤简述如下:读取 AOF,执行命令,恢复状态。



  1. 载入 AOF 时,Redis 创建无网络连接的伪客户端执行 AOF 中的命令,效果等同于真实客户端执行,确保命令在客户端上下文中执行。

  2. 从 AOF 文件中解析并读取一条写命令。

  3. 使用伪客户端执行被读出的写命令。

  4. 循环执行步骤 2 和 3,直至 AOF 文件中所有写命令处理完成。

  5. 完成上述步骤后,AOF 文件所记录的数据库状态将被完整还原。

AOF 重写

AOF 持久化机制通过不断累积写命令来确保数据的安全性与一致性,然而,随着时间的推移,这些累积的命令会导致 AOF 文件体积逐渐增大。这一趋势不仅可能对 Redis 服务器的性能产生负面影响,还可能波及到宿主机的整体性能,如增加磁盘 I/O 负担、消耗更多的存储空间等。


redis>RPUSH list  "A" "B“”(integer) 2redis>RPUSH list "C"(integer)3redis>RPUSH list "D""E (integer)5redis>LPOP list“A”“B”redis>LPOP listredis>RPUSH list "F""G"(integer)5
复制代码


在仅仅为了记录单一LIST键状态变更的情况下,AOF(Append Only File)文件便需承载多达六条命令的记录,这凸显了其在细致追踪数据变动方面的精确性。为了有效应对 AOF 文件体积不断增大的挑战,Redis 巧妙地引入了 AOF 文件重写(rewrite)机制。这一机制的核心在于,Redis 服务器能够智能地生成一个新的 AOF 文件,用以替换当前庞大的旧文件。


新旧 AOF 文件虽在物理上独立,却共同承载着相同的数据库状态,其关键差异在于:新文件经过深度优化,剔除了所有不必要的冗余命令,仅保留了恢复数据库状态所必需的最小命令集。

AOF 文件重写的实现

Redis 的“AOF 文件重写”实际上不涉及旧 AOF 文件的直接读写操作,而是通过读取服务器当前的数据库状态来创建全新的 AOF 文件,以此替换旧文件。为应对 AOF 文件体积膨胀,Redis 提供重写功能,创建更小的新 AOF 文件替换旧文件,两者保存相同数据库状态,但新文件去除冗余命令,体积显著减小。


redis>RPUSH list  "A" "B“”(integer) 2redis>RPUSH list "C"(integer)3redis>RPUSH list "D""E (integer)5redis>LPOP list“A”“B”redis>LPOP listredis>RPUSH list "F""G"(integer)5
复制代码


优化服务器记录list键状态,高效方法是直接从数据库读list值,用一条RPUSH list "C" "D" "E" "E" "G"命令替代原六条,减少至一条命令。


除列表和集合键外,所有类型键均可通过读取当前值,以单条命令记录,替代多条旧命令,实现 AOF 重写功能,减少文件命令数量。


在目前版本中,REDIS_AOF_REWRITE_ITEMS_PER_CMD 常量的值为 64,这也就是说,如果一个集合键包含了超过 64 个元素,那么重写程序会用多条 SADD 命令来记录这个集合,并且每条命令设置的元素数量也为 64 个:


SADD <set-key><eleml><elem2>...<elem64>SADD <set-key><elem65><elem66>...<elem128>SADD <set-key><elem129><elem130>...<elem192>
复制代码


另一方面如果一个列表健包含了超过 64 个项,那么重写程序会用多条 RPUSH 命令来保存这个列表,并且每条命令设置的项数量也为 64 个:


RPUSH <1ist-key><iteml><item2>...<item64>RPUSH <list-key><item65><item66>...<item128>RPUSH <list-key><item129><item130>..<item192>
复制代码


重写程序使用类似的方法处理包含多个元素的有序集合键,以及包含多个健值对的哈希表健。

AOF 后台重写

AOF 重写程序aof_rewrite能有效生成新 AOF 文件,但因其涉及大量写入操作,调用时会长时间阻塞线程。鉴于 Redis 采用单线程处理命令,直接调用aof_rewrite会导致服务器无法响应客户端请求,直至重写完成。

AOF 文件重写时的服务器进程和子进程

子进程负责 AOF 重写时,父进程(服务器)能继续处理命令。子进程拥有父进程数据副本,无需锁即可保证数据安全,因此选用子进程而非线程。子进程重写 AOF 时,服务器处理新命令可能修改数据库状态,导致与重写后 AOF 文件状态不一致,需解决此问题。



为解决数据不一致,Redis 设立 AOF 重写缓冲区。子进程创建后,服务器执行写命令时,同步将命令发送至 AOF 缓冲区及 AOF 重写缓冲区,确保数据一致性。


在子进程 AOF 重写期间,服务器需执行三项工作,服务器同时将命令发送给 AOF 文件和 AOF 重写缓冲区:



  1. 执行客户端命令。

  2. 将写命令追加至 AOF 缓冲区。

  3. 并追加至 AOF 重写缓冲区。


AOF 缓冲区内容定期写入并同步至 AOF 文件,保持对 AOF 文件的正常处理。自子进程创建起,所有写命令均被记录至 AOF 重写缓冲区。

AOF 子线程完结处理步骤

子进程完成 AOF 重写后,向父进程发信号。父进程接收信号后,调用处理函数执行后续工作:


  1. 将 AOF 重写缓冲区内容写入新 AOF 文件,确保其与服务器当前数据库状态同步。

  2. 原子性地对新 AOF 文件重命名,无缝替换旧文件,完成新旧 AOF 文件的平滑过渡。


此信号处理函数执行完成后,父进程恢复常规命令处理流程,无缝衔接。在整个 AOF 后台重写流程中,仅信号处理函数执行期间短暂阻塞父进程,其余时间均保持高效运作,从而最大限度地减少对服务器性能的影响。

最后总结

通过文件同步步骤,系统确保 AOF 文件中的数据与磁盘上的物理数据保持完全一致,即使面对系统故障也能有效保障数据的完整性和可恢复性。这一系列流程紧密衔接,共同构筑了 AOF 持久化功能的坚固基石。


  • AOF 文件记录数据库状态,通过存储所有修改数据库的写命令请求实现,这些命令遵循 Redis 命令协议格式。命令先存入 AOF 缓冲区,再定期写入并同步至 AOF 文件。appendfsync选项显著影响 AOF 持久化的安全性和 Redis 性能。通过载入并执行 AOF 文件中的命令,可恢复数据库状态。

  • AOF 重写生成体积更小但状态相同的新 AOF 文件,通过直接读取数据库键值对实现,不涉及旧 AOF 文件的读写操作。执行BGREWRITEAOF时,Redis 维护一个重写缓冲区记录写命令,子进程据此创建新 AOF 文件。完成后,将重写缓冲区内容追加至新文件,确保状态一致,最后替换旧文件完成重写。


发布于: 刚刚阅读数: 6
用户头像

码界西柚

关注

🏆 InfoQ写作平台-签约作者 🏆 2020-03-25 加入

👑 优酷资深工程师 | INTJ | 狮子座 | 高洞察力理性自律小i人 📕 个人著作《深入浅出Java虚拟机—JVM原理与实战》 💻 10年开发经验,参与过多个大型互联网项目,定期分享技术干货和项目经验

评论

发布
暂无评论
【Redis技术进阶之路】「原理分析系列开篇」探索事件驱动枚型与数据特久化原理实现(数据持久化的实现AOF)_数据库_码界西柚_InfoQ写作社区