Redis设计与实现(十)AOF持久化
上个文章说到了RDB的持久化的过程以及RDB的文件是如何进行保存的。
和RDB不同的是,AOF的操作是通过把指令一个个保存入文件的,进行载入的时候就是一个个的command进行读取,也就是模拟了一遍某时间段内的所有的操作。
由于Redis服务器进程就是一个事件循环,这个循环中的文件事件负责接受客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像ServerCron(100毫秒执行一次,check是否可以进行RDB的BGSAVE)一样.服务器可能在执行文件事件的时候,进行一些写的command,使得一些内容被追加到了aof_buf的缓冲区中去,所以AOF为了捕捉到这些被迫加入缓冲区的command会在处理文件事件结束后执行一个flushAppendOnlyFile的函数用来将aof_buf中的command加入到AOF文件中去。
AOF持久化功能的实现可以分为:命令追加(append),文件写入(write),文件同步(sync)三个步骤。
AOF持久化需要将所有写命令记录在文件中来保存服务器状态,而文件写入操作效率比较低,如果每执行一条写命令都要写一次AOF文件无疑是低效的。为了提高效率,Redis提供了一个中间层 – AOF缓冲区,也就是说当Redis执行一条写命令后,先将该命令追加到AOF缓冲区中,在以后的某个时刻再将AOF缓冲区中的内容同步到文件中。当AOF持久化功能处于打开状态时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾:
在载入AOF的时候
数据还原就是将AOF文件中保存的命令解析并执行,这样就可以将数据库还原为原来的状态。因为在Redis中,命令必须由redisClient实例来执行,所以为了加载AOF文件需要创建一个伪Redis客户端。创建了伪Redis客户端后,执行数据还原的过程就是从AOF文件中读取命令并交给伪Redis客户端执行的过程。数据还原的功能由aof.c文件中的loadAppendOnlyFile
函数完成。
由于aof是通过不断追加写命令来记录数据库状态,所以服务器执行比较久之后,aof文件中的内容会越来越多,磁盘占有量越来越大,同时也是使通过过aof文件还原数据库的需要的时间也变得很久。所以就需要通过读取服务器当前的数据库状态来重写新的aof文件。新的AOF文件不会包含任何浪费空间的冗余命令,所以会比旧的AOF文件小很多。
由于AOF重写是会进行大量写入操作,势必为长时间阻塞主进程,因此redis把重写程序放到子进程执行。
这样做有两点好处:
1)子进程重写期间,主进程可以继续处理命令。
2)子进程带有主进程的数据副本,这样就可以避免与主进程竞争db->dict,这是线程实现不了的。
重写期间,主进程继续处理命令,对数据库状态进行修改,这样使得当前的数据库状态与重写的AOF文件所保存的数据库状态不一致。因此,redis设置了AOF重写缓冲区,在创建子进程后,主进程每执行一个写命令都会写到重写缓冲区。在子进程完成重写后,主进程会将AOF重写缓冲区的数据写入到重写的AOF文件,保证数据状态的一致。