AOF(Append Only File):增量长久化形式
与 RDB 长久化通过保留数据库中的键值对来记录数据库状态不同,AOF 长久化是通过保留 Redis 服务器所执行的写命令来记录数据库状态的,如下图:
被写入 AOF 文件的所有命令都是以 Redis 的命令申请协定格局(RESP 协定 REdis Serialization Protocol)保留的,RESP2 是纯文本格式。
1、AOF 长久化的实现
AOF 长久化性能的实现能够分为命令追加(append)、文件写入、文件同步(sync)三个步骤。
(1)命令追加
当 AOF 长久化性能处于关上状态时,服务器在执行完一个写命令之后,会以 RESP 协定格局将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的开端:
struct redisServer {sds aof_buf; // 应用 sds 数据结构实现 AOF 缓冲区}
命令追加在执行完命令之后,表明 AOF 是写后日志,采纳写后日志的益处:
I) 为了防止额定的查看开销,Redis 在向 AOF 外面记录日志的时候,并不会先去对这些命令进行语法查看。所以,如果先记日志再执行命令的话,日志中就有可能记录了谬误的命令,Redis 在应用日志复原数据时,就可能会出错。
而写后日志这种形式,就是先让零碎执行命令,只有命令能执行胜利,才会被记录到日志中,否则,零碎就会间接向客户端报错。所以,Redis 应用写后日志这一形式的一大益处是,能够避免出现记录谬误命令的状况。
II) 除此之外,AOF 还有一个益处:它是在命令执行后才记录日志,所以不会阻塞以后的写操作。但会阻塞之后的写操作。
(2)AOF 文件的写入与同步
Redis 的服务器主过程就是一个事件循环:
Redis 的命令追加在主循环(aeMain 函数)中的每次解决完写命令的执行后,通过 propagate 函数触发。
propagate 办法将以后命令的内容 append 到 redisServer 对象的 aof_buf 变量中。主循环在下一个迭代进入多路复用的 select 办法前,Redis 会通过 flushAppendOnlyFile 办法将 aof_buf 的内容 write 到 AOF 对应的文件中。但 write 操作只是将数据写到缓存中,什么时候从缓存真正落地到磁盘上,取决于操作系统
。只有显示调用 fsync() 办法能力强制地让操作系统落地数据到磁盘。
AOF 写入与同步的语义
写入 AOF 文件指的是:调用 write()写到操作系统缓存
同步 AOF 文件指的是:调用 fsync()强制缓存落地到磁盘文件
flushAppendOnlyFile 函数的行为由服务器配置的 appendfsync 选项决定:
- always:主循环的每个迭代的 flushAppendOnlyFile 函数中都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,并且间接触发 fsync 办法同步 AOF 文件,强制数据落地到磁盘。因为每个命令都在写入磁盘后才返回,故容错能力最高,即便呈现故障宕机,也只会失落一个事件循环中所产生的命令数据。
- everysec:服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,并且每隔一秒异步地触发一次 fsync 办法。fsync 办法的执行者是 bio 线程池中的某个线程。
flushAppendOnlyFile 函数只是作为生产者将 fsync 工作放入队列,由 bio 线程生产并执行
。 - no:将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,但不显示调用 fsync,由操作系统决定什么时候落地磁盘。这种模式下,Redis 无奈决定增量的落地工夫,因而容错能力不可控。
综上,以上 3 种策略将 aof_buf 缓冲区中的所有内容写入到 AOF 文件的机会都一样,不同的是 fsync 强制落盘的机会
。由名字能够看出,appendfsync
是同步 AOF 文件策略。
2、AOF 文件的载入与数据还原
Redis 会创立一个不带网络连接的伪客户端(fake client):因为 Redis 的命令只能在客户端上下文中执行,而载入 AOF 文件时所应用的命令间接来源于 AOF 文件而不是网络连接,所以服务器应用了一个没有网络连接的伪客户端来执行 AOF 文件保留的写命令。
3、AOF 重写
(1)为了解决 AOF 文件体积收缩的问题
AOF 文件过大会带来性能问题。这里的“性能问题”,次要在于以下三个方面:
- 一是,文件系统自身对文件大小有限度,无奈保留过大的文件;
- 二是,如果文件太大,之后再往里面追加命令记录的话,效率也会变低;
- 三是,如果产生宕机,AOF 中记录的命令要一个个被从新执行,用于故障复原,如果日志文件太大,整个复原过程就会十分迟缓,这就会影响到 Redis 的失常应用。
尽管 Redis 将生成新的 AOF 文件替换旧 AOF 文件的性能命名为“AOF 文件重写”,但实际上,AOF 文件重写并不需要对现有的 AOF 文件进行任何读取、剖析或者写入操作,这个性能是通过读取服务器以后的数据器状态来实现的。因为生成的新 AOF 文件只蕴含还原以后数据库状态所必须的命令,所以新 AOF 文件不会节约任何硬盘空间。
(2)作为一种辅助性的保护伎俩,Redis 不心愿 AOF 重写造成服务器无奈解决申请,所以 Redis 决定将 AOF 重写程序放到子过程里执行,这样做能够同时达到两个目标:
- 子过程进行 AOF 重写期间,服务器过程(父过程)能够持续解决命令申请。
子过程带有服务器过程的数据正本,应用子过程而不是线程,能够在防止应用锁的状况下,保证数据的安全性
。
(3)因为子过程在进行 AOF 重写期间,服务器过程还须要持续解决命令申请,这就可能导致重写后的 AOF 文件和服务器的以后数据库状态不统一。为了解决此问题,Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区在服务器创立子过程之后开始应用,当 Redis 服务器执行完一个写命令之后,它会同时将这个写命令发送给 AOF 缓冲区和 AOF 重写缓冲区,如下图:
这也就是说,在子过程执行 AOF 重写期间,服务器过程须要执行以下三个工作:
- 执行客户端发来的命令
- 将执行后的写命令追加到 AOF 缓冲区
- 将执行后的写命令追加到 AOF 重写缓冲区
这样一来,从创立子过程开始,服务器执行的所有写命令都会被记录到 AOF 重写缓冲区外面。
(4)当子过程实现 AOF 重写工作后,它会 向父过程发送一个信号
,父过程在接到该信号之后,会调用一个信号处理函数,并执行以下工作:
- 将 AOF 重写缓冲区中的所有内容写入到新 AOF 文件中,这时新 AOF 文件所保留的数据库状态将和服务器以后的数据库状态统一。
- 对新的 AOF 文件进行改名,原子地笼罩现有的 AOF 文件,实现新旧两个 AOF 文件的替换。
4、何时触发 AOF 重写?
- 手动发送“bgrewriteaof”指令;
- 有两个配置项在管制 AOF 重写的触发机会:
1)auto-aof-rewrite-min-size: 示意运行 AOF 重写时文件的最小大小,默认为 64MB
2)auto-aof-rewrite-percentage: 这个值的计算方法是:以后 AOF 文件大小和上一次重写后 AOF 文件大小的差值,再除以上一次重写后 AOF 文件大小。也就是以后 AOF 文件比上一次重写后 AOF 文件的增量大小,和上一次重写后 AOF 文件大小的比值。
AOF 文件大小同时
超出下面这两个配置项时,会触发 AOF 重写。
参考资料
1、redis 设计与实现
2、redis 深度历险