1. 程式人生 > >《Redis設計與實現》閱讀筆記6-AOF持久化

《Redis設計與實現》閱讀筆記6-AOF持久化

10 AOF(Append Only File)

10.1 AOF是什麼

以日誌的形式來記錄每個寫操作,將Redis執行過的所有寫指令記錄下來(讀操作不記錄), 只許追加檔案但不可以改寫檔案,redis啟動之初會讀取該檔案重新構建資料,換言之,redis重啟的話就根據日誌檔案的內容將寫指令從前到後執行一次以完成資料的恢復工作

10.1.1 Aof儲存的是appendonly.aof檔案

檔名由redis.conf中配置,預設為appendonly.aof,生成的位置,若未進行配置,預設產生在redis當前工作路徑下

10.1.2 開啟AOF功能

在redis.conf中修改預設的appendonly no,改為yes

10.1.3 AOF恢復資料庫

將有資料的aof檔案複製一份儲存到對應目錄,在需要恢復資料庫時,將aof放在啟動路徑下,啟動redis。 注:若AOF檔案和RDB檔案同時存在,預設使用AOF檔案進行啟動。若資料庫是由於flushall等語句丟失,需要人為修改AOF檔案,刪掉這句話

10.1.4 AOF檔案的修復

若由於突然斷電等異常導致AOF檔案寫入亂碼,又或者人為修改出錯,我們除了可以人為比對進行修改外,也可以使用redis自帶的修復工具

redis-check-aof --fix *.aof

10.2 持久化的實現

AOF持久化功能分為命令追加,檔案寫入,檔案同步三個步驟

10.2.1 命令追加

在redisServer結構中有個aof_buf的屬性,是一個緩衝區,被用於儲存協議格式的寫命令。

struct redisServer{
    //...

    //AOF緩衝區
    sds aof_buf;

    //...
};

10.2.2 AOF檔案寫入與同步

為了提高檔案的寫入效率,在現代作業系統中,當用戶呼叫write函式時,將某些資料寫入到檔案時,作業系統會將這些資料暫時儲存在一個記憶體緩衝區中,等到緩衝區被填滿或超過某個限定時,才會真正將資料資料寫入磁碟。

這樣雖然提高了效率,但帶來了安全問題(宕機會導致記憶體緩衝區的資料丟失)。

系統提供了fsync和fdatasync兩個同步函式,可以強制讓作業系統立即將緩衝區的資料寫到磁盤裡面

appendfsync後面的引數有三種always,everysec(預設),no

  • 每修改同步:appendfsync always 每次事件迴圈中都立即將緩衝區的資料所有內容寫入AOF檔案,並同步AOF檔案,效能是三種方式中最差的,但資料完整性比較好,宕機只會丟失最後一次事件迴圈裡面的資料 。

  • 每秒同步:appendfsync everysec 每次事件迴圈中都立即將緩衝區的資料所有內容寫入AOF檔案,如果上次同步AOF檔案的時間距離現在超過一秒鐘,那麼就再次對AOF檔案進行同步。效率上來講,這種模式足夠快,宕機也只會丟失最近一秒內的資料

  • 不同步:appendfsync no 將aof_buf緩衝區所有內容寫入到AOF檔案,但並不對AOF檔案進行同步,何時同步由作業系統來決定。寫入速度最快,因為這個模式將資料積累一段時間後再同步,所以單次同步時間最長,將時間平攤開和everysec 模式差不多,但宕機會丟失緩衝區的所有資料。

10.3 重寫

  • AOF採用檔案追加方式,檔案會越來越大為避免出現此種情況,新增了重寫機制,當AOF檔案的大小超過所設定的閾值時,Redis就會啟動AOF檔案的內容壓縮,只保留可以恢復資料的最小指令集.可以使用命令bgrewriteaof

  • 原理:AOF檔案持續增長而過大時,會fork出一條新程序來將檔案重寫(先寫臨時檔案最後再rename),遍歷新程序的記憶體中資料。重寫aof檔案的操作,並沒有讀取舊的aof檔案,而是將整個記憶體中的資料庫內容用命令的方式重寫了一個新的aof檔案,這點和快照有點類似

  • 觸發條件:Redis會記錄上次重寫時的AOF大小,預設配置是當AOF檔案大小是上次rewrite後大小的一倍且檔案大於64M時觸發

10.3.1 AOF檔案重寫的實現

  • 首先會建立新的AOF檔案,接著遍歷資料庫,遍歷的過程中會直接忽略空的資料庫,不空的情況下,遍歷資料庫中的所有鍵。

  • 將鍵在新的AOF檔案中進行重寫,如果鍵帶有過期時間,會把過期時間也一起重寫。

  • 接著關閉檔案,開始重寫值,遍歷所有的鍵,對每個鍵進行相應的查詢操作(get獲取字串鍵對應的值,lrange獲取列表鍵對應的值…)

  • 將得到的值,對應鍵在新的AOF檔案中進行重寫操作

注意:實際中,為避免客服端的輸入緩衝區溢位,重寫程式在處理列表,雜湊表,集合,有序集合這四種可能帶有多個元素的鍵時,會先檢查鍵所包含的元素數量,如果這個數量超過了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量值,那麼重寫會分多條操作來進行重寫,這個常量的值為64、也就是例如一個集合的元素有192個,那麼重寫的sadd語句會有三句,才能重寫完這一個集合鍵。

10.3.2 AOF後臺重寫

因為Redis是單執行緒的,而重寫AOF檔案,伴隨著大量的寫入操作,所以重寫會導致執行緒被長時間阻塞,導致伺服器將長時間無法處理客服端發來的命令請求

所以Redis將AOF的重寫程式放到了子程序裡執行,這樣做的目的有兩個

  • 子程序進行AOF重寫期間,服務期程序(父程序)可以繼續處理命令請求

  • 子程序帶有父程序的資源與資料副本,子程序不是執行緒,可以在不使用鎖的情況下,保證資料的安全性

但同時這樣的機制也帶來了問題,在子程序進行AOF重寫的時候,如果父程序對資料庫進行了新增或修改,就導致伺服器當前的資料庫狀態與重寫後AOF檔案所儲存的資料庫狀態不一致

為了解決這個問題,Redis又設定一個AOF重寫緩衝區,這個緩衝區在伺服器建立子程序以後才使用,當Redis執行完一個命令,他會將寫命令同時發給AOF緩衝區與AOF重寫緩衝區

這樣就保證:

  • AOF緩衝區的內容會定期被寫入和同步到AOF檔案,對現有AOF檔案的處理工作照常進行
  • 從建立子程序開始,伺服器執行的所有寫命令都會被記錄到AOF重寫緩衝區裡面

當重寫完成以後,子程序會向父程序傳送一個訊號,父程序接收到這個訊號之後,會呼叫訊號處理函式

  • 將AOF重寫緩衝區的所有內容寫入到新AOF檔案中,這是新AOF檔案所儲存的資料庫狀態和伺服器當前的資料狀態一致
  • 對新的AOF檔案進行改名,原子性的覆蓋現有的AOF檔案,完成新舊AOF檔案的替換
  • 整個重寫過程只有訊號處理函式會對伺服器程序造成阻塞

10.4 與RDB持久化方式的對比

10.4.1 優劣

  • 優點

    • AOF檔案資料損失一般最多丟失最近一秒內的資料,比RDB的最大丟失小很多
  • 缺點

    • 相同資料集的資料而言aof檔案要遠大於rdb檔案,恢復速度慢於rdb
    • aof執行效率要慢於rdb,每秒同步策略效率較好,不同步效率和rdb相同

10.4.2 總結

  • RDB持久化方式能夠在指定的時間間隔能對你的資料進行快照儲存
  • AOF持久化方式記錄每次對伺服器寫的操作,當伺服器重啟的時候會重新執行這些命令來恢復原始的資料,AOF命令以redis協議追加儲存每次寫的操作到檔案末尾.Redis還能對AOF檔案進行後臺重寫,使得AOF檔案的體積不至於過大
  • 只做快取:如果你只希望你的資料在伺服器執行的時候存在,你也可以不使用任何持久化方式.
  • 同時開啟兩種持久化方式
    • 在這種情況下,當redis重啟的時候會優先載入AOF檔案來恢復原始的資料,因為在通常情況下AOF檔案儲存的資料集要比RDB檔案儲存的資料集要完整.
    • RDB的資料不實時,同時使用兩者時伺服器重啟也只會找AOF檔案。那要不要只使用AOF呢? 建議不要,因為RDB更適合用於備份資料庫(AOF在不斷變化不好備份),快速重啟,而且不會有AOF可能潛在的bug,留著作為一個萬一的手段。
  • 因為RDB檔案只用作後備用途,建議只在Slave上持久化RDB檔案,而且只要15分鐘備份一次就夠了,只保留save 900 1這條規則。如果Enalbe AOF,好處是在最惡劣情況下也只會丟失不超過兩秒資料,啟動指令碼較簡單隻load自己的AOF檔案就可以了。代價一是帶來了持續的IO,二是AOF rewrite的最後將rewrite過程中產生的新資料寫到新檔案造成的阻塞幾乎是不可避免的。只要硬碟許可,應該儘量減少AOF rewrite的頻率,AOF重寫的基礎大小預設值64M太小了,可以設到5G以上。預設超過原大小100%大小時重寫可以改到適當的數值。如果不Enable AOF ,僅靠Master-Slave Replication 實現高可用性也可以。能省掉一大筆IO也減少了rewrite時帶來的系統波動。代價是如果Master/Slave同時倒掉,會丟失十幾分鐘的資料,啟動指令碼也要比較兩個Master/Slave中的RDB檔案,載入較新的那個。