除了RDB持久化功能之外,Redis還提供了AOF(Append Only File)持久化功能。與RDB持久化經過保存數據庫中的鍵值對來記錄數據庫狀態不一樣,AOF持久化是經過保存Redis所執行的寫命令來記錄數據庫狀態的。web
redis默認狀況是關閉AOF的,因此要使用就要先經過如下方式打開:
第一種方式:
打開 redis.conf 修改如下參數:redis
appendonly yes (默認no,關閉)表示是否開啓AOF持久化: 數據庫
appendfilename 「appendonly.aof」 AOF持久化配置文件的名稱:**緩存
默認狀況下redis安裝目錄會生成 appendonly.aof文件,若是沒有則執行如下方式
第二種方式:
在cmd經過redis-cli鏈接到服務器的命令界面裏
輸入
config set appendonly yes
config set save 「」(可選)安全
執行的第一條命令開啓了 AOF 功能: Redis 會阻塞直到初始 AOF 文件建立完成爲止, 以後 Redis 會繼續處理命令請求, 並開始將寫入命令追加到 AOF 文件末尾。服務器
執行的第二條命令用於關閉 RDB 功能。 這一步是可選的, 若是你願意的話, 也能夠同時使用 RDB 和 AOF 這兩種持久化功能。(若是RDB和AOF同時開啓,則AOF優先加載)網絡
AOF持久化功能的實現能夠分爲命令追加、文件寫入、文件同步三個步驟。app
命令追加:async
當AOF持久化功能打開時,服務器在執行完一個寫命令以後,會以協議格式將被執行的寫命令追加到服務器狀態的aof_buf緩衝區的末尾。函數
AOF文件的寫入與同步:
每當服務器常規任務函數被執行、 或者事件處理器被執行時, aof.c/flushAppendOnlyFile 函數都會被調用, 這個函數執行如下兩個工做:
WRITE:根據條件,將 aof_buf 中的緩存寫入到 AOF 文件。
SAVE:根據條件,調用 fsync 或 fdatasync 函數,將 AOF 文件保存到磁盤中。
兩個步驟都須要根據必定的條件來執行, 而這些條件由 AOF 所使用的保存模式來決定, 如下小節就來介紹 AOF 所使用的三種保存模式, 以及在這些模式下, 步驟 WRITE 和 SAVE 的調用條件。
Redis 目前支持三種 AOF 保存模式,它們分別是:
AOF_FSYNC_NO :不保存。
AOF_FSYNC_EVERYSEC :每一秒鐘保存一次。
AOF_FSYNC_ALWAYS :每執行一個命令保存一次。
不保存(AOF_FSYNC_NO)
在這種模式下, 每次調用 flushAppendOnlyFile 函數, WRITE 都會被執行, 但 SAVE 會被略過。
在這種模式下, SAVE 只會在如下任意一種狀況中被執行:
Redis 被關閉
AOF 功能被關閉
系統的寫緩存被刷新(多是緩存已經被寫滿,或者按期保存操做被執行)
這三種狀況下的 SAVE 操做都會引發 Redis 主進程阻塞。
每一秒鐘保存一次(AOF_FSYNC_EVERYSEC)
在這種模式中, SAVE 原則上每隔一秒鐘就會執行一次, 由於 SAVE 操做是由後臺子線程調用的, 因此它不會引發服務器主進程阻塞。
注意, 在上一句的說明裏面使用了詞語「原則上」, 在實際運行中, 程序在這種模式下對 fsync 或 fdatasync 的調用並非每秒一次, 它和調用 flushAppendOnlyFile 函數時 Redis 所處的狀態有關。
每當 flushAppendOnlyFile 函數被調用時, 可能會出現如下四種狀況:
子線程正在執行 SAVE ,而且:
這個 SAVE 的執行時間未超過 2 秒,那麼程序直接返回,並不執行 WRITE 或新的 SAVE 。
這個 SAVE 已經執行超過 2 秒,那麼程序執行 WRITE ,但不執行新的 SAVE 。注意,由於這時 WRITE 的寫入必須等待子線程先完成(舊的) SAVE ,所以這裏 WRITE 會比平時阻塞更長時間。
子線程沒有在執行 SAVE ,而且:
上次成功執行 SAVE 距今不超過 1 秒,那麼程序執行 WRITE ,但不執行 SAVE 。
上次成功執行 SAVE 距今已經超過 1 秒,那麼程序執行 WRITE 和 SAVE 。
能夠用流程圖表示這四種狀況:
根據以上說明能夠知道, 在「每一秒鐘保存一次」模式下, 若是在狀況 1 中發生故障停機, 那麼用戶最多損失小於 2 秒內所產生的全部數據。
若是在狀況 2 中發生故障停機, 那麼用戶損失的數據是能夠超過 2 秒的。
Redis 官網上所說的, AOF 在「每一秒鐘保存一次」時發生故障, 只丟失 1 秒鐘數據的說法, 實際上並不許確。
每執行一個命令保存一次(AOF_FSYNC_ALWAYS)
在這種模式下,每次執行完一個命令以後, WRITE 和 SAVE 都會被執行。
另外,由於 SAVE 是由 Redis 主進程執行的,因此在 SAVE 執行期間,主進程會被阻塞,不能接受命令請求。
AOF 保存模式對性能和安全性的影響
在上一個小節, 咱們簡短地描述了三種 AOF 保存模式的工做方式, 如今, 是時候研究一下這三個模式在安全性和性能方面的區別了。
對於三種 AOF 保存模式, 它們對服務器主進程的阻塞狀況以下:
不保存(AOF_FSYNC_NO):寫入和保存都由主進程執行,兩個操做都會阻塞主進程。
每一秒鐘保存一次(AOF_FSYNC_EVERYSEC):寫入操做由主進程執行,阻塞主進程。保存操做由子線程執行,不直接阻塞主進程,但保存操做完成的快慢會影響寫入操做的阻塞時長。
每執行一個命令保存一次(AOF_FSYNC_ALWAYS):和模式 1 同樣。
由於阻塞操做會讓 Redis 主進程沒法持續處理請求, 因此通常說來, 阻塞操做執行得越少、完成得越快, Redis 的性能就越好。
模式 1 的保存操做只會在AOF 關閉或 Redis 關閉時執行, 或者由操做系統觸發, 在通常狀況下, 這種模式只須要爲寫入阻塞, 所以它的寫入性能要比後面兩種模式要高, 固然, 這種性能的提升是以下降安全性爲代價的: 在這種模式下, 若是運行的中途發生停機, 那麼丟失數據的數量由操做系統的緩存沖洗策略決定。
模式 2 在性能方面要優於模式 3 , 而且在一般狀況下, 這種模式最多丟失很少於 2 秒的數據, 因此它的安全性要高於模式 1 , 這是一種兼顧性能和安全性的保存方案。
模式 3 的安全性是最高的, 但性能也是最差的, 由於服務器必須阻塞直到命令信息被寫入並保存到磁盤以後, 才能繼續處理請求。
綜合起來,三種 AOF 模式的操做特性能夠總結以下:
AOF 文件保存了 Redis 的數據庫狀態, 而文件裏面包含的都是符合 Redis 通信協議格式的命令文本。
這也就是說, 只要根據 AOF 文件裏的協議, 從新執行一遍裏面指示的全部命令, 就能夠還原 Redis 的數據庫狀態了。
Redis 讀取 AOF 文件並還原數據庫的詳細步驟以下:
1.建立一個不帶網絡鏈接的僞客戶端(fake client)。
2.讀取 AOF 所保存的文本,並根據內容還原出命令、命令的參數以及命令的個數。
3.根據命令、命令的參數和命令的個數,使用僞客戶端執行該命令。
4.執行 2 和 3 ,直到 AOF 文件中的全部命令執行完畢。
完成第 4 步以後, AOF 文件所保存的數據庫就會被完整地還原出來。
注意, 由於 Redis 的命令只能在客戶端的上下文中被執行, 而 AOF 還原時所使用的命令來自於 AOF 文件, 而不是網絡, 因此程序使用了一個沒有網絡鏈接的僞客戶端來執行命令。 僞客戶端執行命令的效果, 和帶網絡鏈接的客戶端執行命令的效果, 徹底同樣。
由於AOF持久化是經過保存被執行的寫命令來記錄數據庫狀態的,因此隨着服務器運行時間的流逝,AOF文件中的內容愈來愈多,文件體積愈來愈大,若是不加以控制,會對redis服務器甚至宿主計算器形成影響。
所謂的「重寫」實際上是一個有歧義的詞語, 實際上, AOF 重寫並不須要對原有的 AOF 文件進行任何寫入和讀取, 它針對的是數據庫中鍵的當前值。
考慮這樣一個狀況, 若是服務器對鍵 list 執行了如下四條命令:
RPUSH list 1 2 3 4// [1, 2, 3, 4]
RPOP list // [1, 2, 3]
LPOP list // [2, 3]
LPUSH list 1 // [1, 2, 3]
那麼當前列表鍵 list 在數據庫中的值就爲 [1, 2, 3] 。
若是咱們要保存這個列表的當前狀態, 而且儘可能減小所使用的命令數, 那麼最簡單的方式不是去 AOF 文件上分析前面執行的四條命令, 而是直接讀取 list 鍵在數據庫的當前值, 而後用一條 RPUSH 1 2 3 命令來代替前面的四條命令。
再考慮這樣一個例子, 若是服務器對集合鍵 animal 執行了如下命令:
SADD animal cat// {cat}
SADD animal dog panda tiger// {cat, dog, panda, tiger}
SREManimal cat// {dog, panda, tiger}
SADD animal cat lion// {cat, lion, dog, panda, tiger}
那麼使用一條 SADD animal cat lion dog panda tiger 命令, 就能夠還原 animal 集合的狀態, 這比以前的四條命令調用要大大減小。
除了列表和集合以外, 字符串、有序集、哈希表等鍵也能夠用相似的方法來保存狀態, 而且保存這些狀態所使用的命令數量, 比起以前創建這些鍵的狀態所使用命令的數量要大大減小。
AOF重寫程序aof_rewrite函數能夠很好完成建立一個新AOF文件的任務,可是這個函數會進行大量寫入操做,會長時間阻塞,因此Redis將AOF重寫程序放到子進程裏執行,這樣作達到兩個目的:
·子進程AOF重寫期間,服務器進程能夠繼續處理命令請求。
·子進程帶有數據庫進程的數據副本,使用子進程而不是線程,能夠避免使用鎖的狀況下保證數據安全。
不過,使用子進程也有一個問題須要解決,就是AOF重寫期間若是有新的寫命令進來,不能漏掉,那樣會數據不一致。
因而Redis服務器設置了一個AOF重寫緩衝區
最後流程變爲:
1.執行客戶端發來的命令
2.將執行的寫命令追加到AOF緩衝區
3.將執行後的寫命令追加到AOF重寫緩衝區
如上圖。
這樣一來能夠保證:
·AOF緩衝區的內容會按期被寫入和同步到AOF文件,對現有AOF文件的處理工做會照常進行。
·從建立子進程開始,服務器執行的全部寫命令會被記錄到AOF重寫緩衝區裏面
當子進程完成AOF重寫工做以後,它會向父進程發送一個信號,父進程收到信號後,會調用一個信號處理函數,並執行如下工做:
1.將AOF重寫緩衝區中的全部內容寫入新的AOF文件中,這時新AOF文件鎖保存的數據庫狀態和服務器當前狀態一致
2.對新的AOF文件進行更名,原子性操做地覆蓋現有的AOF文件,完成新舊AOF文件的替換。
這個信號函數執行完畢之後,父進程就能夠繼續像往常同樣接受命令請求了,在整個AOF後臺重寫過程當中,只有信號處理函數執行時會對服務器進程形成阻塞,其餘時候均可以繼續處理請求,這樣AOF重寫對服務器性能形成的影響降到了最低。
以上就是AOF後臺重寫,也便是BGREWRITEAOF命令的實現原理。
AOF的缺點
·體積大:對於相同的數據集來講,AOF文件的體積一般要大於 RDB文件的體積。
·性能差:根據所使用的Fsync策略,AOF的速度可能會慢於 RDB。在通常狀況下,每秒Fsync的性能依然很是高,而關閉 Fsync可讓 AOF的速度和 RDB同樣快,即便在高負荷之下也是如此。不過在處理巨大的寫入載入時,RDB能夠提供更有保證的最大延遲時間(Latency)。