上面我们介绍了RDB的持久化机制,它是将数据库的快照(snapshot)以二进制的方式保存到磁盘中,而AOF则是以协议文本的方式,将所有对数据库进行过写入的命令及参数记录到AOF文件中,以此达到记录数据库状态的目的。Redis将所有对数据库进行写入的命令及参数记录到AOF文件,来达到记录数据库状态的目的,为了方便起见,我们一般称这种记录过程为同步。



      同步命令到AOF文件的过程可以分为三个阶段:

       (1)第一阶段即命令传播,Redis将执行完的命令、命令的参数、命令的参数个数等信息发送到AOF程序中。

       (2)第二阶段即缓存追加,AOF程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的AOF缓存中。

       (3)第三阶段即文件的写入和保存,AOF程序将缓存中的内容写入到AOF文件的末尾,如果给定的AOF保存条件被满足的话,它会调用fsync或者fdatasync函数,然后将内容从内存保存到磁盘中。


      而Redis目前支持三种AOF保存模式,它们是:

       (1)AOF_FSYNC_NO,表示不保存

       (2)AOF_FSYNC_EVERYSEC ,表示每秒钟保存一次

       (3)AOF_FSYNC_ALWAYS,表示每执行一个命令保存一次

      

       AOF文件保存了Redis的数据库状态,而文件里面包含的都是符合Redis通讯协议格式的命令文本,其实也就是说根据AOF文件里的协议,重新执行一遍里面指示的所有命令,就可以还原Redis的数据库状态了。而Redis读取AOF文件并还原数据库的具体步骤如下:

       (1)创建一个不带网络连接的伪客户端(fake   client)

       (2)读取AOF所保存的文本,并且根据内容还原出命令、命令的参数以及命令的个数

       (3)根据命令、命令的参数和命令的个数,使用伪客户端执行该命令

       (4)执行2和3,直到AOF文件所有的命令被执行完毕


       因为Redis的命令只能在客户端的上下文中被执行,而AOF还原时所使用的命令来自于AOF文件,而不是挽留过,所以程序使用了一个没有网络连接的伪客户端来执行命令。伪客户端执行命令的效果,和带网络连接的客户端执行命令的效果是完全一样的。为了避免对数据的完整性产生影响,在服务器载入数据的过程中,只有和数据库无关的订阅与发布功能可以正常使用,其他命令一律返回错误。



       AOF文件通过同步Redis服务器所执行的命令可以实现数据库状态的记录,但是随着时间的流逝,AOF文件也会变得越来越大。而且对某些被频繁操作的键,对它们调用的命令也可能会很频繁,AOF文件的体积就会急剧膨胀。为了解决这种问题,Redis就需要对AOF文件进行重写,也就是创建一个新的AOF文件来代替原有的AOF文件,新AOF文件和原有AOF文件保存的数据库状态完全一样,但是新的AOF文件的体积小于等于原有的AOF文件的体积。

       AOF重写并不需要对原有的AOF文件进行任何的写入和读取,它针对的是数据库中键的当前值。根据键的类型,使用适当的写入命令来重现键的当前值,这就是AOF重写的实现原理。

       AOF重写程序可以很好地完成创建一个新AOF文件的任务,但是在执行这个程序的时候,调用者线程会被阻塞。但是Redis不希望AOF重写造成服务器无法处理请求,所以Redis决定将AOF重写程序放到后台的子进程里面去执行,这样的好处有两个:

        ①就是当子进程进行AOF重写期间,主进程可以继续处理命令请求

        ②子进程带有主进程的数据部分,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。

       当然,使用子进程还有一个问题需要解决,那就是子进程在进行AOF重写期间,主进程还需要继续处理命令,而新的命令你给可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。为了解决这个问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis主进程在接收到新的写命令之后,除了会将这个写命令的协议内容追加到现有的AOF文件之外,还会追加到这个缓存中。


       也就是说,当子进程在执行AOF重写时,主进程需要做三件事:

        ①处理命令请求。

        ②将写命令追加到现有的AOF文件中。

        ③将写命令追加到AOF重写缓存中。

        这样的主要好处就是:

        ①现有的AOF功能会继续执行,即使在AOF重写期间发生停机,也不会有任何数据丢失。

        ②所有对数据库进行修改的命令你给都会被记录到AOF重写缓存中。

        当子进程完成了AOF重写之后,它会向父进程发送一个完成信号,父进程在接收到完成信号之后,会调用一个信号处理函数,并且它完成如下的工作:

        ①将AOF重写缓存中的内容全部写入到新的AOF文件中。该步骤完成后,现有的AOF文件、新的AOF文件和数据库三者的状态就完全一致了。

        ②对新的AOF文件进行改名,覆盖原有的AOF文件。该步骤完成后,程序就完成了新旧两个AOF文件的交替。

         

          在整个AOF后台重写过程中,只有最后的写入缓存和改名操作会造成主进程阻塞,在其他时候,AOF后台重写不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到了最低。

      

          当如下条件满足的时候,Redis会自动触发自动的AOF重写:

           (1)没有bgsave命令在进行。

           (2)没有bgrewriteaof在进行。

           (3)当前aof文件大小大于server.aof_rewrite_min_size,注意它的默认值为1MB。

           (4)当前aof文件大小和最后一次aof重写后的大小之间的比率大于等于指定的增长百分比,默认的增长百分比为100%,也就是说当前AOF文件比最后一次AOF重写时的大小大一倍的时候,那么触发自动的AOF重写功能。

           


             我们也可以使用bgrewriteaof来手动触发AOF的自动重写。


            总结即:

             (1)AOF文件通过保存所有修改数据库的命令来记录数据库的当前状态。

             (2)AOF文件中的所有命令都以Redis通讯协议的格式保存。

             (3)AOF重写的目的是用更小的体积来保存数据库状态,整个重写过程不影响Redis主进程处理的命令请求。

             (4)AOF的重写工作是针对数据库的当前值来进行的,它不会读取、也不会使用原有的AOF文件。

             (5)AOF重写可以由用户手动触发,也可以由服务器自动触发。