上次我们主要讲解的是 RDB 机制,这次我们着重对 AOF 持久化进行详细说明,另外后续我们也会针对 RDB-AOF 混合模式(Redis 4 开始)进行分享…
AOF(Append Only File)顾名思义 只进行追加操作的文件,通过保存 Redis 服务器所执行的 命令来记录数据库的状态,命令数据都是以 Redis 的请求协议格式(RESP 协议)保存在文本中的,协议详情可以参考之前的文章 RESP 协议
- 优势:AOF 提供了多种策略进行 数据持久化,即使是 默认的 每秒钟 fsync 一次,也能保障最多丢失一秒的数据(fsync 是在后台使用 另外的线程进行执行,不影响 业务线程的处理);AOF 采用 append 的形式,即使服务器在一瞬间宕机,也不会对之前数据造成影响,即使宕机瞬间产生数据格式问题,也可以通过
redis-check-aof
工具进行 一致性修复;另外,AOF 采用了 RESP 协议,具有很强的可读性 - 劣势:AOF 由于保存了所有数据处理命令集,在同样大小数据规模的情况下,AOF文件 比 RDB文件 会大很多;同样道理,在数据恢复方面,AOF 会慢一些
RDB-AOF 混合持久化模式
之前我们分析了 RDB 和 AOF 的各个缺点,Redis 4.0 开始支持 RDB-AOF 混合持久化模式(server.aof_use_rdb_preamble
变量,默认关闭);如果持久化打开,AOF rewrite 的时候会直接把 RDB 内容写到 AOF 文件头部,这样做的好处是可以结合 RDB 和 AOF 的优点,快速加载同时避免丢失过多的数据;当然缺点也是有的,AOF 里面的 RDB 部分就是压缩格式不再是 AOF 格式,可读性差
AOF 持久化步骤
AOF 持久化逻辑相对来说比 RDB 会简单一些,主要步骤如下:
命令追加
将命令数据写入到 aof_buf 缓冲区写入缓冲
将 aof_buf 缓冲区的数据写入到 系统 IO 缓冲区(OS cache)同步磁盘
将系统缓冲区数据同步到磁盘
AOF 缓冲区变量存储在 redisServer 结构中
更多的 redisServer 结构内容注释可以对照 server 全局变量查看 redis 服务器 initServerConfig 部分
aof 持久化文件我们可以通过文本直接查看,完全遵循 RESP 协议
AOF 策略配置
AOF 持久化策略配置在 源码中变量为 server.aof_fsync
,默认 aof 不开启,在开启的情况下,保存策略的配置文件对应为 appendfsync
appendfsync always
每次收到写命令就立即强制写入磁盘,最能保证完全持久化,但速度也是最慢的,一般不推荐使用appendfsync everysec
每秒钟强制写入磁盘一次,在性能和持久化方面 折中,是受推荐的方式appendfsync no
完全依赖 OS cache 的写入而不主动执行,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐
AOF 重写
当一个数据库的命令非常多时,AOF 文件就会非常大,为了解决这个问题,Redis引入了AOF重写机制来压缩文件的体积;重写 的方式为:
- 进程内已经超时的数据不在写入文件
- 无效命令不在写入文件
- 多条写的命令合并成一个
所以,AOF 总是记录数据库的最终状态的一个命令集;类似于物理中的位移与路程的关系,位移总是关心的是启动到终点距离,而不关心是如何从起点到达终点
重写分为两个方面
- 手动执行,我们以
BGREWRITEAOF
命令为主线,逐步看一下调用逻辑,命令的入口函数为bgrewriteaofCommand()
- 自动执行,在
serverCron()
函数中 根据一定逻辑进行判定- AOF 文件的当前大小大于执行 BGREWRITEAOF 所需的最小大小
- AOF 文件当前的体积相对于 上次 AOF 文件 的体积 增长体积的百分比超过了100
AOF 重写操作可能会长时间阻塞服务器主进程,所以使用了 fork() 子进程的方式抛入后台处理,不影响父进程的业务服务;但是还会有个问题,虽然解决了阻塞问题,那么如果 在重写期间 又有新的写命令过来呢,而这些命令还会灰度改变服务器状态,从而使当前服务器状态和 AOF 文件状态不同,于是 Redis 很聪明的来了一个 AOF 重写缓冲 的结构 aofrwblock
,重写缓冲区并不是 一整块,是 10MB 大小的 内存块链表
AOF 文件载入
AOF 文件的 载入 和 写入 是相反的过程,逻辑比较简单,因为 Redis 命令一般都是在 客户端中运行的,所以载入 AOF 文件的时候,Redis 服务器创建了一个 关闭监听套接字的 伪客户端,模拟 命令在 客户端中执行
源码解析
由于有些方法里的代码量比较大,我们这里按照 典型的代码片段进行解析,同志们可以根据文章提示的代码位置 和 代码里面的关键词 在源码中搜素,可能数据结构一些元素 看不太懂什么意思,没关系,先混个脸熟,后面看完回头再看过来就明白了
我们按照上面的持久化步骤为主线进行一步一步调试,之前的文章我们提到了,命令都是通过 call()
回调函数调用服务器初始化时注册的命令集合,而后在 call() 执行写命令操作
后会调用 命令追加函数 propagate()
,经过源码调试查看追加函数的主逻辑又是由 feedAppendOnlyFile()
控制,用于往 缓冲区添加写操作命令
上面我们注意到有 调用catAppendOnlyExpireAtCommand()
函数,这个函数的主要逻辑是将过期信息单独封装为 RESP 协议格式的命令,注意时间为绝对时间,并写入 buf,而写操作的命令还是另外调用 catAppendOnlyGenericCommand()
命令格式化函数进行处理
磁盘文件写入函数为 flushAppendOnlyFile()
,调用一般在 serverCron()
和 beforeSleep()
事件循环中
重写主逻辑函数为 rewriteAppendOnlyFileBackground()
上面代码中提到 aof 重写主逻辑函数在 rewriteAppendOnlyFile()
,我们继续追踪…
上面我们看到了一个 遍历数据库 重写 aof 文件 主逻辑函数 rewriteAppendOnlyFileRio()
本文作者: wettper
本文链接: http://www.web-lovers.com/redis-source-aof.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!