Redis持久化——AOF日誌

Mr於發表於2021-04-26

最新:Redis記憶體——記憶體消耗(記憶體都去哪了?)

最新:Redis持久化——如何選擇合適的持久化方式

最新:Redis持久化——AOF日誌

更多文章...

上一篇文章Redis持久化——記憶體快照(RDB)我們總結到使用Redis記憶體快照進行持久化,在t 時刻做了一次快照,然後又在 t+n 時刻做了一次快照,此時如果當機,則會丟失在此期間內修改的資料。但又不能頻繁的進行記憶體快照,那麼有什麼辦法能夠儘可能的減少這種資料丟失呢?Redis提供了另一種持久化的方式——AOF日誌(Append Only File)。

什麼是AOF日誌持久化

執行後寫日誌

與記憶體快照儲存當前記憶體中的資料所不同,AOF持久化是通過儲存Redis伺服器所執行的寫命令來記錄資料庫狀態的。即每執行一個命令,就會把該命令寫到日誌檔案裡。

需要注意的是寫日誌的操作在Redis執行命令將資料寫入記憶體之後,如下圖所示:

AOF-1

這樣做的好處就是不會阻塞當前操作,也可以避免額外的檢查開銷,如果是在命令執行前進行寫日誌的操作,一旦命令語法是錯誤的,不進行檢查的話就會導致寫入到日誌檔案中的命令是錯誤的,在使用日誌檔案恢復資料的時候就會出錯。而在命令執行後在進行日誌的寫入則不會有這個問題。

但是也存在兩個問題,

  1. AOF 雖然避免了對當前命令的阻塞,但卻可能會給下一個操作帶來阻塞風險。因為,AOF 日誌是在主程式中執行的,如果在把日誌檔案寫入磁碟時,磁碟寫壓力大,就會導致寫盤很慢,進而導致後續的操作也無法執行了

  2. 如果剛執行完一個命令,還沒有來得及記日誌就當機了,那麼這個命令和相應的資料就有丟失的風險。如果此時 Redis 是用作快取,還可以從後端資料庫重新讀入資料進行恢復,但是,如果 Redis 是直接用作資料庫的話,此時,因為命令沒有記入日誌,所以就無法用日誌進行恢復了。

AOF 緩衝區

針對上面兩個問題,Redis提供了緩衝區的方式進行AOF日誌的記錄,以達到儘可能的避免阻塞和資料丟失的問題。

Redis在執行完命令進行持久化的時候,並非直接寫入磁碟日誌檔案,而是先寫入AOF緩衝區內,之後再通過某種策略寫到磁碟。

AOF-2

使用快取區的方式進行AOF日誌的記錄,上面提到的兩個問題其實就和日誌從緩衝區寫入磁碟的時機有關係。

三種回寫策略

Redis AOF 機制提供了三種回寫磁碟的策略。

  • Always(同步寫回): 命令寫入 AOF緩衝區後呼叫系統 fsync操作同步到AOF檔案, fsync完成後執行緒返回
  • Everysec(每秒寫回): 命令寫人 AOF緩衝區後呼叫系統 write操作, write完成後執行緒返回。 fsync同步檔案操作由專門執行緒每秒呼叫一次
  • No(作業系統自動寫回): 命令寫入 AOF緩衝區後呼叫系統 write操作,不對AOF檔案做 fsync同步,同步硬碟操作由作業系統負責,通常同步週期最長30秒

但其實可以看出這三種回寫策略都並不能完美的解決問題,

配置為 always時,每次寫入都要同步AOF檔案,硬碟的寫入速度無法與記憶體相提並論,顯然與 Redis髙效能特性背道而馳

配置為no,由於作業系統每次同步AOF檔案的週期不可控,而且會加大每次同步硬碟的資料量,雖然提升了效能,但資料安全性無法保證。

配置為 everysec,是建議的同步策略,也是預設配置,雖然能做到兼顧效能和資料安全性。但極端情況下一會造成1秒內的資料丟失。

在真正使用中,我們可以根據具體對效能和資料完整性的要求,分析這三種回寫策略,選擇適合的策略來進行持久化。

回寫策略 優點 缺點
Always(同步寫回) 可靠性高、資料基本不丟失 效能較差
Everysec(每秒寫回) 效能適中 當機時丟失1秒內的資料
No(作業系統自動寫回) 效能好 當機時丟失資料較多

AOF重寫

日誌檔案越來越大怎麼辦

選擇了合適的回寫策略,AOF這種持久化的方式還有其它問題嗎?

因為AOF持久化是通過儲存被執行的寫命令來記錄資料庫狀態的,所以隨著時間的流逝,AOF檔案中的內容會越來越多,檔案的體積也會越來越大,過大的AOF檔案不僅追加命令會變慢,而且可能對Redis伺服器、甚至整個宿主計算機造成影響,並且AOF檔案的體積越大,使用AOF檔案來進行資料還原所需的時間就越多。

這個時候就要用到AOF重寫機制了

redis> set testKey testValue
OK
redis> set testKey testValue1
OK
redis> del testKey
OK
redis> set testKey hello
OK
redis> set testKey world
OK

AOF 檔案是以追加的方式,逐一記錄接收到的寫命令的。當一個鍵值對被多條寫命令反覆修改時,AOF 檔案會記錄相應的多條命令。如上示例,我們執行完命令後,Redis會在AOF裡面追加5條命令。但實際上只需要set testKey world一條命令就夠了。

AOF 重寫機制就是在重寫時,Redis 根據資料庫的現狀建立一個新的 AOF 檔案,也就是說,讀取資料庫中的所有鍵值對,然後對每一個鍵值對用一條命令記錄它的寫入。比如說,當讀取了鍵值對“testkey”: “world”之後,重寫機制會記錄 set testkey world這條命令。這樣,當需要恢復時,可以重新執行該命令,實現“testkey”: “world”的寫入。

這樣,重寫後的日誌,從5條變成了1條,而對於可能被修改過成百上千次的鍵值對來說,重寫能節省的空間就更大了。

雖然 AOF 重寫後,日誌檔案會縮小,但是,要把整個資料庫的最新資料的操作日誌都寫回磁碟,仍然是一個非常耗時的過程。這時,我們不得不關注:重寫會不會導致阻塞?這就要看看AOF重寫的過程是怎麼樣的

AOF 重寫過程

因為AOF重寫也是一個非常耗時的過程,又因為Redis單執行緒的特性,同記憶體快照一樣,AOF重寫的過程也是由父程式fork出bgrewriteaof子程式來完成的.

使用子程式(而不是開啟一個執行緒)進行AOF重寫雖然可以避免使用鎖的情況下,保證資料安全性,但是會帶來子程式和父程式一致性問題。
例如在開始重寫之後父程式又接收了新的鍵值對此時子程式是無法知曉的,當子程式重寫完成後的資料庫和父程式的資料庫狀態是不一致的。

如下表:

時間 伺服器程式(父程式) 子程式
T1 執行命令 SET K1 V1
T2 執行命令 SET K1 V1
T3 建立子程式,執行AOF檔案重寫 開始AOF重寫
T4 執行命令 SET K2 V2 執行重寫
T5 執行命令 SET K3 V3 執行重寫
T6 執行命令 SET K4 V4 完成AOF重寫

在T6時刻伺服器程式有了4個鍵,而子程式卻只有1個鍵

為了解決這種不一致性,Redis設定了一個AOF重寫緩衝區。

AOF重寫

在子程式執行AOF重寫期間。伺服器程式需要執行以下3個動作:

  1. 執行客戶端命令
  2. 執行後追加到AOF緩衝區
  3. 執行後追加到AOF重寫緩衝區

子程式完成AOF重寫後,它向父程式傳送一個訊號,父程式收到訊號後會呼叫一個訊號處理函式,該函式把AOF重寫緩衝區的命令追加到新AOF檔案中然後替換掉現有AOF檔案。父程式處理完畢後可以繼續接受客戶端命令呼叫,可以看出在AOF後臺重寫過程中只有這個訊號處理函式會阻塞伺服器程式。
下表是完整的AOF後臺重寫過程:

時間 伺服器程式(父程式) 子程式
T1 執行命令 SET K1 V1
T2 執行命令 SET K1 V1
T3 建立子程式,執行AOF檔案重寫 開始AOF重寫
T4 執行命令 SET K2 V2 執行重寫
T5 執行命令 SET K3 V3 執行重寫
T6 執行命令 SET K4 V4 完成AOF重寫,向父程式傳送訊號
T7 接收到訊號,將T5 T6 T7 伺服器的寫命令追加到新的AOF檔案末尾
T8 用新的AOF替換舊的AOF

這樣就可以保證重寫日誌期間的所有操作也都會寫入新的AOF檔案。

需要注意的是, T7 T8執行的任務會阻塞伺服器處理命令。

總的來說,就是每次 AOF 重寫時,Redis 會先fork出一個子程式用於重寫;然後,使用兩個日誌保證在重寫過程中,新寫入的資料不會丟失。

AOF檔案恢復

Redis 伺服器重啟後,會優先去載入AOF日誌檔案。因為AOF檔案裡面包含了重建資料庫狀態所需的所有寫命令,所以伺服器重新執行一遍AOF檔案裡面儲存的寫命令,就可以還原伺服器關閉之前的資料庫狀態。

而由於Redis命令只能在客戶端上下文中執行,Redis會建立一個沒有網路連線的偽客戶端來執行AOF檔案中的內容。

小結

本文主要總結了Redis AOF 持久化的方式,介紹了它同步磁碟的三種策略,以及日誌檔案過大時如何進行重寫。我們知道Redis持久化方式有AOF和RDB兩種,那麼這兩種持久化方式各自有什麼優點和缺點?真正使用中我們應該如何去選擇合適的持久化方式,又可能遇到哪些問題呢?我們下一篇文章繼續總結

系列文章:

最新:Redis記憶體——記憶體消耗(記憶體都去哪了?)

最新:Redis持久化——如何選擇合適的持久化方式

最新:Redis持久化——AOF日誌

Redis持久化——記憶體快照(RDB)

一文回顧Redis五大物件(資料型別)

Redis物件——有序集合(ZSet)

Redis物件——集合(Set)

Redis物件——列表(List)

Redis物件——雜湊(Hash)

Redis資料結構——quicklist

Redis物件——字串

Redis物件——Redis物件系統簡介

Redis資料結構——壓縮列表

Redis資料結構——整數集合

Redis資料結構——跳躍表

Redis資料結構——字典

Redis資料結構——連結串列

Redis資料結構——簡單動態字串SDS

-----END-----

關注下方公眾號,回覆“Redis”,可得Redis相關學習資料

相關文章