1. 程式人生 > >Redis的兩種持久化操作RDB-AOF

Redis的兩種持久化操作RDB-AOF

Redis支援RDB和AOF兩種持久化機制,持久化功能有效地避免因程序退出造成的資料丟失問題,當下次重啟時利用之前持久化檔案即可實現資料恢復。

  1. RDB是什麼
RDB持久化是把當前程序資料生成快照儲存到硬碟的過程,觸發RDB持久化過程分為**手動觸發**和**自動觸發**。

1.1.1 觸發機制

手動觸發分別對應save和bgsave命令:

save命令:阻塞當前Redis伺服器,知道RDB過程完成為止,對於記憶體比較大的例項會造成長時間阻塞,先上環境不建議使用。執行save命令對應Redis日誌如下:

DB saved on disk

bgsave命令:Redis程序執行fork操作建立子程序,RDB持久化過程由子程序負責,完成後自動結束。阻塞只發生在fork階段,一段時間很短。執行bgsave名字對應的Redis日誌如下:

Background saving started by pid 3152
DB saved on disk
RDB: 0MB of memory userd by copy-on-write
Background saving terminated with success

bgsave命令是針對save阻塞問題做的優化。因此Redis內部所有涉及到RDB操作都採用bgsave的方式,而save命令可以廢棄。

Redis內部還存在自動觸發RDB的持久化機制,例如一下場景:

1) 使用save相關配置,如‘save m n’表示m秒之內資料集存在n次修改時,自動觸發bgsave。

2)如果從節點執行全量複製操作,主節點自動執行bgsave生成RDB檔案併發送給從節點。

3)執行debug reload命令重新載入Redis時,也會自動觸發save操作。

4)預設情況下執行shutdown命令時,如果沒有開啟AOF持久化功能則自動執行bgsave。

1.1.2 bgsave流程說明

bgsave是主流的觸發RDB持久化方式,下圖是運作流程

Redis的兩種持久化操作RDB-AOF
111.png

執行bgsave命令,Redis父程序判斷當前是否存在正在執行的子程序,如只RDB/AOF子程序,如果存在bgsave命令直接返回。
父程序執行fork操作建立子程序,fork操作過程中父程序會阻塞,通過info stats命令檢視latest_fork_usec選項,可以獲取最近一個fork以操作的耗時,單位為微秒。
父程序仍fork完成後,bgsave命令返回“Background saving started”資訊並不再阻塞父程序,可以繼續響應其他命令。
子程序建立RDB檔案,根據父程序記憶體生成臨時快照檔案,完成後對原有檔案進行原子替換。執行lastsave命令可以獲取最後一次生成尺RDB的時間,對應info統計的rdb_last_savetime選項。
程序傳送訊號給父程序衣示完成,父程序更新統計資訊,具體見info Persistence下的rdb

*相關選項。

1.1.3 RDB檔案處理

儲存:RDB檔案儲存在dir配置指定的目錄下,檔名通過dbfilename配置指定。可以通過執行config set dir {newDir} 和 config set dbfilename {newFileName}執行期動態執行,當下次執行時RDB檔案會儲存到新目錄。

壓縮:Redis預設採用LZF演算法對生成的RDB檔案做壓縮處理,壓縮後的檔案遠遠小於記憶體大小,預設開啟,可以通過引數config set rdbcompression {yes|no}動態修改。

校驗:如果Redis載入損壞的RDB檔案時拒絕啟動,並列印如下日誌:

Short read or OOM loading DB. Unrecoverable error , aborting now.

這時可以使用Redis提供的redis-check-dump工具檢測RDB檔案並獲取對應的錯誤報告

1.1.4 RDB的優缺點

RDB的優點:

RDB是一個緊湊壓縮的二進位制檔案,代表Redis在某一個時間點上的資料快照。非常適合用於備份,全量複製等場景。比如每6小時執行bgsave備份,並把RDB檔案拷貝到遠端機器或者檔案系統中(如hdfs),用於災難恢復。

Redis載入RDB恢復資料遠遠快於AOF方式。
RDB的缺點

RDB方式資料沒辦法做到實時持久化/秒級持久化。因為bgsave每次執行都要執行fork操作建立子程序,屬於重量級操作,頻繁執行成本過高。

RDB檔案使用特定二進位制格式儲存,Redis版本演進過程中有多個格式的RDB笨笨,存在老版本Redis服務無法相容新版RDB格式的問題。
針對RDB不適合實時持久化的問題,Redis提供了AOF持久化方式來解決

  1. AOF是什麼
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啟時再重新執行AOF檔案中命令達到恢復資料的目的。AOF的主要作用是解決了資料持久化的實時性,目前已經是Redis持久化的主流方式。

2.1.1 使用AOF

開啟AOF功能需要設定配置:appendonly yes,預設不開啟。AOF檔案通過appendfilename 配置設定,預設檔名是appendonly.aof。儲存路徑同RDB持久化方式一致。通過dir配置指定。AOF的工作流程操作:命令寫入(append)、檔案同步(sync)、檔案重寫(rewrite)、重啟載入(load),工作流程如下:

Redis的兩種持久化操作RDB-AOF
流程如下:
1) 所有的寫入命令會追加到aof_buf(緩衝區)中。
2) AOF緩衝區根據對應的策略向硬碟做同步操作。
3) 隨著AOF檔案越來越大,需要定期對AOF檔案進行重寫,達到壓縮的目的。
4) 當Redis服務重啟時,可以載入AOF檔案進行資料恢復。瞭解AOF工作流程之後,下面針對每個步驟做詳細介紹。
2.1.2 命令寫入

AOF命令寫入的內容直接是文字協議格式。例如set hello world 這條命令,在AOF緩衝區會追加如下文字:

\r\n3\r\nset\r\n5\r\nhello\r\n$5\r\nworld\r\n

介紹關於AOF的連個疑惑:

1) AOF為什麼直接採用文字協議格式?可能的理由如下:

文字協議具有很好的相容性。
開啟AOF後,所有寫入命令都包含追加操作,直接採用協議格式,避免二次處理開銷。
文字協議具有可讀性,方便直接修改和處理。

2) AOF為什麼把命令追加到aof_buf中?Redis使用單執行緒響應命令,如果每次寫AOF檔案命令都直接追加到硬碟,那麼效能完全取決於當前硬碟負載。縣寫入緩衝區aof_buf中,還有另一個好處,Redis可以提供多種緩衝區同步硬碟的策略,在效能和安全性方面做出平衡。

2.1.3 檔案同步

Redis提供了多種AOF緩衝區同步檔案策略,由引數appendfsync控制,不同值的含義如表所示

Redis的兩種持久化操作RDB-AOF

系統呼叫writ和fsync說明:

write操作會處罰延遲寫(delayed write)機制,Linux在核心提供頁緩衝區用來提高硬碟IO效能。write操作在寫入系統緩衝區後直接返回。同步硬碟操作依賴於系統排程機制,列如:緩衝區頁空間寫滿或達到特定時間週期。同步檔案之前,如果此時系統故障宕機,緩衝區內資料將丟失。

fsync針對單個檔案操作(比如AOF檔案),做強制硬碟同步,fsync將阻塞知道寫入硬碟完成後返回,保證了資料持久化。
配置為always時,每次寫入都要同步AOF檔案,在一般的STAT硬碟上,Redis只能支援大約幾百TPS寫入,顯然跟Redis高效能特性背道而馳,不建議配置。
配置為no,由於作業系統每次同步AOF檔案的週期不可控,而且會極大每次同步硬碟的資料量,雖然提升了效能,但資料安全性無法保證。
配置為everysec,是建議的同步策略,也是預設配置,做到兼顧效能和資料安全性,理論上只有在系統突然宕機的情況下丟失1s的資料。(嚴格來說最多丟失1s資料是不準確)

2.1.4 重寫機制

隨著命令不斷寫入AOF,檔案會越來越大,為了解決這個問題,Redis引入了AOF重寫機制壓縮檔案體積。AOF檔案重寫是吧Redis程序內的資料轉化為寫命令同步到新AOF檔案的過程。

重寫後的AOF檔案為什麼可以變下?有如下原因:

1) 程序內已經超時的資料不再寫檔案。

2)舊的AOF檔案含有無效命令,如del key1、 hdel key2、srem keys、set a 111、set a 222等。重寫使用程序內資料直接生成,這樣新的AOF檔案只保留最終資料的寫入命令。

3) 多條寫命令可以合併為一個,如lpush list a、lpush list b、 lpush list c 可以轉化為:lpush list a b c。為了防止但挑明瞭過大造成客戶端緩衝區溢位,對於list、set、hash、zset等型別曹組,以64個元素為界拆分為多條。 

AOF重寫降低了檔案佔用空間,除此之外,另一個目的是:更小的AOF檔案可以更快地被Redis載入。

AOF重寫過程可以手動觸發和自動觸發:

手動觸發:直接呼叫bgrewriteaof命令

自動觸發:更具auto-aof-rewrite-min-size和auto-aof-rewrite-percentage引數確定自動觸發時機

auto-aof-rewrite-min-size:表示執行AOF重寫時檔案最小體積,預設為64MB
auto-aof-rewrite-percentage:代表當前AOF檔案空間(aof_current_size)和上一次重寫後AOF檔案空間(aof_base_size)的值

自動觸發時機=aof_current_size>auto-aof-rewrite-min-size && (aof_current_size-aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage
其中aof_current_size和aof_base_size可以再info Persistence統計資訊中檢視。
當觸發AOF重寫時,內部做了那些事?下面結合圖介紹它的執行流程:

Redis的兩種持久化操作RDB-AOF

流程說明:

1)執行AOF重寫請求。

如果當前程序正在執行AOF重寫,請求不執行並返回如下響應:

ERR Background append only file rewriting already in progress

如果當前程序正在執行bgsave操作,重寫命令延遲到bgsave完成後再執行,返回如下響應:

Background append only file rewriting scheduled

2) 父程序執行fork建立子程序,開銷等同於bgsave過程。

3.1) 主程序fork操作完成後,繼續響應其他命令。所有修改命令依然寫入AOF緩衝區並更具appendfsync策略同步到硬碟,保證原有AOF機制正確性。

3.2) 由於fork操作運用寫時複製技術,子程序只能共享fork操作時的記憶體資料。由於父程序依然響應命令,Redis使用"AOF重寫緩衝區"儲存這部分新資料,防止新AOF檔案生成期間丟失這部分資料。

4)子程序根據記憶體快照,按照命令合併規則寫入到新的AOF檔案。每次批量寫入硬碟資料量由配置aof-rewrite-incremental-fsync控制,預設為32MB,防止單次刷盤資料過多造成硬碟阻塞。

5.1)新AOF檔案寫入完成後,子程序傳送訊號給父程序,父程序更新統計資訊,具體見info persistence下的aof_*相關統計。

5.2)父程序把AOF重寫緩衝區的資料寫入到新的AOF檔案。

5.3)使用新AOF檔案替換老檔案,完成AOF重寫。

2.1.5 重啟載入

AOF和RDB檔案都可以用於伺服器重啟時的資料恢復。如圖所示,表示Redis持久化檔案載入流程:

Redis的兩種持久化操作RDB-AOF

流程說明:

1) AOF持久化開啟且存在AOF檔案時,優先載入AOF檔案,列印如下日誌:

DB loaded from append only file: 5.841 seconds

2) AOF關閉或者AOF檔案不存在時,載入RDB檔案,列印如下日誌:

DB loaded from disk:5.586 seconds

3) 載入AOF/RDB檔案城後,Redis啟動成功。

4) AOF/RDB檔案存在錯誤時,Redis啟動失敗並列印錯誤資訊