Redis AOF,一篇即懂

序章

除了RDB持久化功能之外,Redis還提供了AOF(Append Only File)持久化功能。與RDB持久化經過保存數據庫中的鍵值對來記錄數據庫狀態不一樣,AOF持久化是經過保存Redis所執行的寫命令來記錄數據庫狀態的。web

AOF如何打開

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如何實現持久化

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 。

能夠用流程圖表示這四種狀況:

image.png

根據以上說明能夠知道, 在「每一秒鐘保存一次」模式下, 若是在狀況 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 模式的操做特性能夠總結以下:

image.png

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持久化是經過保存被執行的寫命令來記錄數據庫狀態的,因此隨着服務器運行時間的流逝,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)。

相關文章
相關標籤/搜索