·首先介紹RDB、AOF的配置和運行流程,以及控制持久化的相關命令,如bgsave和bgrewriteaof。
·其次對常見持久化問題進行分析定位和優化。
·最後結合Redis常見 的單機多實例部署場景進行優化。
5.1 RDB
RDB持久化是把當前進程數據生成快照保存到硬盤的過程,觸發RDB持久化過程分為手動觸發和自動觸發。
5.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 3151
* DB saved on disk
* RDB: 0 MB of memory used 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文件並發送給從節點,更多細節見6.3節介紹的復制原理。
3)執行debug reload命令重新加載Redis時,也會自動觸發save操作。
4)默認情況下執行shutdown命令時,如果沒有開啟AOF持久化功能則自動執行bgsave。
5.1.2 流程說明
bgsave是主流的觸發RDB持久化方式,下面根據圖5-1了解它的運作流程。
1)執行bgsave命令,Redis父進程判斷當前是否存在正在執行的子進程,如RDB/AOF子進程,如果存在bgsave命令直接返回。
2)父進程執行fork操作創建子進程,fork操作過程中父進程會阻塞,通過info stats命令查看latest_fork_usec選項,可以獲取最近壹個fork操作的耗時,單位為微秒。
3)父進程fork完成後,bgsave命令返回“Background saving started”信息並不再阻塞父進程,可以繼續響應其他命令。
4)子進程創建RDB文件,根據父進程內存生成臨時快照文件,完成後對原有文件進行原子替換。執行lastsave命令可以獲取最後壹次生成RDB的時間,對應info統計的rdb_last_save_time選項。
5)進程發送信號給父進程表示完成,父進程更新統計信息,具體見info Persistence下的rdb_*相關選項。
5.1.3 RDB文件的處理
保存:RDB文件保存在dir配置指定的目錄下,文件名通過dbfilename配置指定。可以通過執行config set dir{newDir}和config setdbfilename{newFileName}運行期動態執行,當下次運行時RDB文件會保存到新目錄。
運維提示
當遇到壞盤或磁盤寫滿等情況時,可以通過config set dir{newDir}在線修改文件路徑到可用的磁盤路徑,之後執行bgsave進行磁盤切換,同樣適用於AOF持久化文件。
壓縮:Redis默認采用LZF算法對生成的RDB文件做壓縮處理,壓縮後的文件遠遠小於內存大小,默認開啟,可以通過參數config set rdbcompression{yes|no}動態修改。
運維提示
雖然壓縮RDB會消耗CPU,但可大幅降低文件的體積,方便保存到硬盤或通過網絡發送給從節點,因此線上建議開啟。
校驗:如果Redis加載損壞的RDB文件時拒絕啟動,並打印如下日誌:
# Short read or OOM loading DB. Unrecoverable error, aborting now.
這時可以使用Redis提供的redis-check-dump工具檢測RDB文件並獲取對應的錯誤報告。
5.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持久化方式來解決。
5.2 AOF
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啟時再重新執行AOF文件中的命令達到恢復數據的目的。AOF的主要作用是解決了數據持久化的實時性,目前已經是Redis持久化的主流方式。理解掌握好AOF持久化機制對我們兼顧數據安全性和性能非常有幫助。
5.2.1 使用AOF
開啟AOF功能需要設置配置:appendonly yes,默認不開啟。AOF文件名通過appendfilename配置設置,默認文件名是appendonly.aof。保存路徑同RDB持久化方式壹致,通過dir配置指定。AOF的工作流程操作:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啟加載(load),如圖5-2所示。
1)所有的寫入命令會追加到aof_buf(緩沖區)中。
2)AOF緩沖區根據對應的策略向硬盤做同步操作。
3)隨著AOF文件越來越大,需要定期對AOF文件進行重寫,達到壓縮的目的。
4)當Redis服務器重啟時,可以加載AOF文件進行數據恢復。了解AOF工作流程之後,下面針對每個步驟做詳細介紹。
5.2.2 命令寫入
AOF命令寫入的內容直接是文本協議格式。例如set hello world這條命令,在AOF緩沖區會追加如下文本:*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
Redis協議格式具體說明見4.1客戶端協議小節,這裏不再贅述,下面介
紹關於AOF的兩個疑惑:
1)AOF為什麽直接采用文本協議格式?可能的理由如下:
·文本協議具有很好的兼容性。
·開啟AOF後,所有寫入命令都包含追加操作,直接采用協議格式,避免了二次處理開銷。
·文本協議具有可讀性,方便直接修改和處理。
2)AOF為什麽把命令追加到aof_buf中?Redis使用單線程響應命令,如果每次寫AOF文件命令都直接追加到硬盤,那麽性能完全取決於當前硬盤負載。先寫入緩沖區aof_buf中,還有另壹個好處Redis可以提供多種緩沖區同步硬盤的策略,在性能和安全性方面做出平衡。
5.2.3 文件同步
Redis提供了多種AOF緩沖區同步文件策略,由參數appendfsync控制,不同值的含義如表5-1所示。
表5-1 AOF緩沖區同步文件策略
系統調用write和fsync說明:
·write操作會觸發延遲寫(delayed write)機制。Linux在內核提供頁緩沖區用來提高硬盤IO性能。write操作在寫入系統緩沖區後直接返回。同步硬盤操作依賴於系統調度機制,例如:緩沖區頁空間寫滿或達到特定時間周期。同步文件之前,如果此時系統故障宕機,緩沖區內數據將丟失。
·fsync針對單個文件操作(比如AOF文件),做強制硬盤同步,fsync將阻塞直到寫入硬盤完成後返回,保證了數據持久化。除了write、fsync,Linux還提供了sync、fdatasync操作,具體API說明參
見:plete, this may slow down Redis
2)每當發生AOF追加阻塞事件發生時,在info Persistence統計中,aof_delayed_fsync指標會累加,查看這個指標方便定位AOF阻塞問題。
3)AOF同步最多允許2秒的延遲,當延遲發生時說明硬盤存在高負載問題,可以通過監控工具如iotop,定位消耗硬盤IO資源的進程。優化AOF追加阻塞問題主要是優化系統硬盤負載,優化方式見上壹節。
5.4 多實例部署
Redis單線程架構導致無法充分利用CPU多核特性,通常的做法是在壹臺機器上部署多個Redis實例。當多個實例開啟AOF重寫後,彼此之間會產生對CPU和IO的競爭。本節主要介紹針對這種場景的分析和優化。上壹節介紹了持久化相關的子進程開銷。對於單機多Redis部署,如果同壹時刻運行多個子進程,對當前系統影響將非常明顯,因此需要采用壹種措施,把子進程工作進行隔離。Redis在info Persistence中為我們提供了監控子進程運行狀況的度量指標,如表5-2所示。
我們基於以上指標,可以通過外部程序輪詢控制AOF重寫操作的執行,整個過程如圖5-6所示。
流程說明:
1)外部程序定時輪詢監控機器(machine)上所有Redis實例。
2)對於開啟AOF的實例,查看(aof_current_size-aof_base_size)/aof_base_size確認增長率。
3)當增長率超過特定閾值(如100%),執行bgrewriteaof命令手動觸發當前實例的AOF重寫。
4)運行期間循環檢查aof_rewrite_in_progress和aof_current_rewrite_time_sec指標,直到AOF重寫結束。
5)確認實例AOF重寫完成後,再檢查其他實例並重復2)~4)步操作。從而保證機器內每個Redis實例AOF重寫串行化執行。
5.5 本章重點回顧
1)Redis提供了兩種持久化方式:RDB和AOF。
2)RDB使用壹次性生成內存快照的方式,產生的文件緊湊壓縮比更高,因此讀取RDB恢復速度更快。由於每次生成RDB開銷較大,無法做到實時持久化,壹般用於數據冷備和復制傳輸。
3)save命令會阻塞主線程不建議使用,bgsave命令通過fork操作創建子進程生成RDB避免阻塞。
4)AOF通過追加寫命令到文件實現持久化,通過appendfsync參數可以控制實時/秒級持久化。因為需要不斷追加寫命令,所以AOF文件體積逐漸變大,需要定期執行重寫操作來降低文件體積。
5)AOF重寫可以通過auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數控制自動觸發,也可以使用bgrewriteaof命令手動觸發。
6)子進程執行期間使用copy-on-write機制與父進程***享內存,避免內存消耗翻倍。AOF重寫期間還需要維護重寫緩沖區,保存新的寫入命令避免數據丟失。
7)持久化阻塞主線程場景有:fork阻塞和AOF追加阻塞。fork阻塞時間跟內存量和系統有關,AOF追加阻塞說明硬盤資源緊張。
8)單機下部署多個實例時,為了防止出現多個子進程執行重寫操作,建議做隔離控制,避免CPU和IO資源競爭。