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文件的載入一樣相對簡單,按行讀取,從*後獲得參數數目,而後讀取指定數目的參數後,執行命令。