我们相信,这将会是你看到过的最深入的 Redis 内核专题分享。
此次 Redis 专题共有 4 期内容。本文是第 2 期内容,整理自QCon+案例研习社的演讲内容,视频链接见文尾。后续 2 期内容都将在“百度智能云技术站”公众号首发。
今天分享的主题是 Redis 持久化机制的演进与百度智能云的实践,我会从以下四个部分来分享:
1. Redis 持久化机制
1.1 Redis 持久化机制简介
首先我们先来简单了解什么是持久化?在计算机科学中,持久化的定义是一个系统的状态特征比创造它的过程更持久,而一般普遍的方法是将系统状态作为数据存储在计算机的持久存储器中。
这是一个非常抽象的概念,那结合 Redis 来看,Redis 持久化就是将 Redis 内存中的数据写入持久存储器的过程,比如将内存中的数据写入 SSD。而 Redis 为我们提供了三种方法,第一是 RDB,第二是 AOF,第三则是 RDB 和 AOF 混合持久化。
在详细了解这三个持久化机制之前,我们先重点关注两个问题:1. Redis 持久化的方式。2. Redis 持久化的格式。
1.2 RDB(Redis Database)
RDB,它的全称是 Redis Database,它的定义是将数据集的快照以二进制格式保存在持久存储器中。从定义中可以找到刚才两个问题的答案:1. RDB 持久化的方式:快照。2. RDB 持久化的格式:二进制数据(压缩紧凑)。
RDB 持久化的方式决定了其触发的方式。Redis 提供了三种触发方式,分别是手动触发、自动触发以及被动触发。
1.3 AOF (Append Only File)
AOF 持久化的定义是将 Redis 内存中的更改以 RESP 的格式追加写到持久化的存储机制。从定义中我们可以找到刚才两个问题的答案:1. AOF 持久化的方式:追加写。2. AOF 持久化的格式:RESP 格式(文本协议,可读性高)。
AOF 持久化追加写的 RESP 格式是一种文本协议,虽然可读性高,但是导致 AOF 文件的大小是一个单调递增的过程,因此 Redis 提供了 AOF rewrite 机制,用来解决 AOF 文件大小膨胀的问题。
AOF rewrite 的定义是将 Redis 数据库的快照以 RESP 或者 RDB 的格式写到持久存储器,然后追加该时间点之后的所有 AOF 数据。从定义中我们也可以找到刚才两个问题的答案:1. AOF rewrite的方式:快照和追加写。2. AOF rewrite的格式:二进制或 RESP。
2. RDB 机制的演进与百度智能云的实践
2.1 RDB 机制演进
RDB 持久化的演进已经发展到第十个版本。下图列举了 Redis RDB 持久化机制的所有版本。我们来重点看看这几个版本的变化。
2.2 RDB 持久化流程
2.3 百度智能云 RDB 持久化优化:forkless
3. AOF 机制的演进与实践
3.1 AOF 机制演进
2013 年 Redis 实现了第一版的 aof-rewrite 机制,它有追加增量数据导致阻塞的问题
2015 年 Redis 3.0 的时候进行了优化,采用了父子进程之间通信的方式。
2017 年 Redis 实现了以 RDB 为前缀的 AOF,就是我们刚才所提到的 RDB 和 AOF 混合持久化,它是以 RDB 为前缀。
2022年 Redis 7.0,Redis 实现了Multi AOF 的架构,并且在 AOF 命令中添加了扩展注释功能。扩展注释可以帮助 AOF 实现更多功能,我们后面会讲到,并且这个 Feature 是百度智能云贡献给社区的。
3.2 AOF 重写
3.2.1 Redis 2.8 及以前
在 Redis 2.8 之前,Redis Server 会 fork() 一个子进程,子进程会进行重写操作。在重写操作的期间,Redis 会将它重写期间数据库的更改存在两个地方,第一个是 rewrite buffer,第二个是 aof buffer。rewrite buffer 是为了保存 rewrite 期间的变更,而 aof buffer 是为了进行 AOF 持久化。当子进程完成重写之后,它会将临时文件 rename 成 AOF 文件。此时如果将 rewrite buffer 一次性地全部写给 AOF 文件,就会导致阻塞 Redis。因此在 Redis 3.0 时候,优化了该问题。
从 Redis 3.0 开始, 首先还是 fork() 一个子进程,子进程来做一个 rewrite 操作。与刚才 Redis 2.8 不同的是,它会在重写期间将 rewrite buffer 内容分多次由主进程发给子进程,子进程来将这部分数据刷到 AOF 文件中。这样一来就解决了当子进程完成 AOF 重写之后,一次性发送 rewrite buffer 阻塞的问题。
这似乎是一个不错的方案,但是也有一些问题。我们可以看到 Redis 在重写期间,会将更改分别写到 rewrite buffer 和 aof buffer,而这两次 buffer 会由主进程和子进程分别刷到磁盘。问题就是同一份数据写两份 buffer 并且刷两份磁盘,这是很严重的资源浪费。
因此在 Redis 7.0 的时候,Redis 社区设计了 Multi Part AOF 的架构。它的方式就是将原来的一个 AOF 文件拆为多个 AOF 文件,分别包括一个 base.aof 和多个 incr.aof。base.aof 实际上就是一个全量数据,而 incr.aof 文件则是一些增量数据。
同样,Redis 主进程会 fork() 一个子进程,子进程来进行重写操作。而与之前版本不同的是,子进程只关心它的重写,不需要关心增量,因此它将全部重写的数据写完之后会生成一个 base.aof 文件,这部分数据代表它这个时刻的全量数据。在重写期间 Redis 会有许多更改,这些更改则由主进程递交给 aof buffer,进而由 buffer 主进程再刷到一个 incr.aof 增量文件中。然后 base.aof 和 incr.aof 由一个 manifest 的文件来维护它们的关系。
这看起来是一个比较完美的方案,但是该方案依然仍然会有问题:1. AOF 重写仍然需要调用 fork(),我们前面也分析过了,给出了相关的解决方案。2. Redis 进行 AOF 持久化的时候,会同步调用 write() 和 fsync(),可能会导致请求阻塞。
3.3.2 基于AOF的部分重同步:PSYNC-AOF
百度智能云自研了同步组件 sync-agent 支持异地多活,并且支持了冲突解决。Redis 会将所有更改数据持久化到 AOF 中,而所有持久化的 AOF 中的命令都会带有注释功能,在注释功能中会有一些地域分片信息,因此 sync-agent 解析 AOF 之后就会同步到其他地域。
在这个过程中,其他地域是如何区分到其他地域和本地域的请求,以防止写回环呢?就是通过其他地域转发过来的 AOF 中带有的地域信息来区分。
3.3.4 基于按时间点恢复
了解完 Redis 持久化机制的演进和百度智能云的优化实践后,我们一起来畅想一下 Redis 未来的持久化。当前 Redis 的持久化都是从 DRAM 到 SSD ,而未来持久化的存储介质一定是新的存储介质:持久化内存,它具有高性能低延迟等特点,处于 DRAM 和 SSD 之间,目前在百度智能云已经有一些成功的实践,我们会在此方面继续探索。
最后,回顾一下,最开始我们了解了 Redis 持久化的定义,又分别介绍了 Redis 的两种持久化机制演进和百度智能云的优化实践,现在关于 Redis 持久化机制的 How 和 What 的两个问题,在你心中已经有了一些答案。
方案没有好坏,只有取舍。 是选择 RDB 还是选择 AOF,我相信大家心中自有答案。
本期课程视频链接:https://time.geekbang.org/qconplus/detail/100110469
传送门