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重写?

  1. 手动发送“bgrewriteaof”指令;
  2. 有两个配置项在管制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深度历险