Redis支持两种持久化方式:rdb与aof,上一篇文章中已经大体介绍了rdb的持久化实现,这篇文章主要介绍aof实现。redis
与rdb方式相比,aof会使用更多的存储空间,由于它将数据以客户端命令的形式进行存储,并使用ascii编码。但它也有相应的优势,如支持append的方式保存db内容的变更,不须要像rdb方式同样一旦内容有变更,便须要从新完整生成文件才能将变更保存到文件中;同时在子进程持久化的过程当中,能够累积客户端的命令到缓存中,最后将缓存内容添加到持久化生成的文件的末尾,几乎能够实现不丢失内容的持久化。缓存
aof的持久化方式不只能够将client端发送来的命令直接添加到aof文件的末尾,还能够将内存中的数据重写为命令的形式。redis中定义aof中的一条完整命令格式以下:app
*count\r\n{$len\r\ncontent\r\n}
以*开头,后面接这条命令中的参数数目count,并以\r\n结束;后面的每个参数都以$开头,接参数长度len,并以\r\n结束,后面跟实际的参数内容,并以\r\n结束。函数
举例,命令RPUSH “key1” 1 2 3 4这条命令在aof文件中的表示以下:编码
*6\r\n$5\r\nRPUSH\r\n$4\r\nkey1\r\n\$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n$1\r\n4\r\n
这表示命令中有6个参数,第1个参数长度为5,值为RPUSH,第2个参数长度为4,值为key1,以此类推。spa
而命令 set “key2” “hello, world”这条命令在aof文件中表示以下:code
*3\r\n$3\r\nset\r\n$4\r\nkey2\r\n$12\r\nhello, world\r\n
对于已经存储在db中的数据,若是须要以aof的方式进行持久化,那么须要将其重写为命令的形式,这个功能经过aof.c源文件中的rewriteAppendOnlyFileRio函数实现。它会遍历全部的db字典,并遍历每个字典中的全部key-value对,进行rewrite。重写规则大体以下:server
举例,若是内存中存在一个”name1” “faker”的key-value对,重写命令以下:blog
*3\r\n$3\r\nset\r\n$5\r\nname\r\n$5\r\nfaker\r\n
若是内存中存在一个list,key为”key1”,内容为1 2 3 4,那么其重写后的命令以下:索引
*6\r\n$5\r\nRPUSH\r\n$4\r\nkey1\r\n\$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n$1\r\n4\r\n
redis中aof持久化使用到了两类缓存,一类缓存用于在子进程运行过程当中,保存客户端的命令,它是server全局结构的一个list成员aof_rewrite_buf_blocks,该list的节点值类型为
typedef struct aofrwblock { unsigned long used, free; char buf[AOF_RW_BUF_BLOCK_SIZE]; } aofrwblock;
当须要将命令保存到aof文件中,而此时server.aof_child_pid != -1(即aof子进程正在运行),命令被添加到aof_rewrite_buf_blocks连接的缓存中。
这个buffer中的数据会经过pipe发送给子进程,发送函数为aofChildWriteDiffData,这个函数在pipe的写事件发生时调用。相应的子进程中会有从pipe接收这些缓存数据的函数aofReadDiffFromParent,这个函数在子进程持久化数据的过程当中被主动调用,并将接收的数据保存到server. aof_child_diff中,在内存数据处理完成后,添加到aof文件末尾。
另外一类缓存是server.aof_buf,这是一个sds类型的缓存,它在aof持久化开启,而且没有aof子进程运行时使用,客户端命令始终首先保存到该缓存中,而后周期性将该缓存添加到aof文件中。
经过缓存命令的方式,保证了aof持久化不会丢失更新。
一个aof持久化文件的完整建立流程以下:
生成一个有效的aof文件后,后续db字典中的数据有变更时,只须要将相应的命令添加到aof文件末尾,便可完成相应的持久化,不须要像rdb同样为了保存新的改动,必须从新完整地对db字典进行处理。
aof文件的载入一样相对简单,按行读取,从*后获得参数数目,而后读取指定数目的参数后,执行命令。