Redis支持RDB和AOF兩種持久化機制,持久化功能有效地避免因進程退出形成的數據丟失問題,當下次重啓時利用以前持久化的文件便可實現數據恢復。理解掌握持久化機制對於Redis運維很是重要。redis
首先介紹RDB、AOF的配置和運行流程,以及控制持久化的相關命令,如bgsave和bgrewriteaof。算法
RDB安全
RDB持久化是把當前進程數據生成快照保存到硬盤的過程,觸發RDB持久化過程分爲手動觸發和自動觸發。bash
觸發機制服務器
手動觸發分別對應save和bgsave命令:網絡
save命令:阻塞當前Redis服務器,直到RDB過程完成爲止,對於內存比較大的實例會形成長時間阻塞,線上環境不建議使用。運行save命令對應的Redis日誌以下:併發
1283:M 04 Apr 21:39:03.035 * DB saved on disk
bgsave命令:Redis進程執行fork操做建立子進程,RDB持久化過程由子進程負責,完成後自動結束。阻塞只發生在fork階段,通常時間很短。運行bgsave命令對應的Redis日誌以下:
app
1283:M 04 Apr 21:40:33.849 * Background saving started by pid 1429 1429:C 04 Apr 21:40:33.874 * DB saved on disk 1429:C 04 Apr 21:40:33.875 * RDB: 6 MB of memory used by copy-on-write 1283:M 04 Apr 21:40:33.929 * Background saving terminated with success
顯然bgsave命令是針對save阻塞問題作的優化。所以Redis內部全部的涉及RDB的操做都採用bgsave的方式,而save命令已經廢棄。運維
除了執行命令手動觸發以外,Redis內部還存在自動觸發RDB的持久化機制,例如如下場景:async
1)使用save相關配置,如"save m n"。表示m秒內數據集存在n次修改時,自動觸發bgsave。
2)若是從節點執行全量複製操做,主節點自動執行bgsave生成RDB文件併發送給從節點。
3)執行debug reload命令從新加載Redis時,也會自動觸發save操做。
4)默認狀況下執行shutdown命令時,若是沒有開啓AOF持久化功能則自動執行bgsave。
注:若是想關閉自動RDB持久化,在配置文件刪除"save m n"的配置
流程說明
bgsave是主流的觸發RDB持久化方式,下面根據下圖瞭解它的運做流程。
1)執行bgsave命令,Redis父進程判斷當前是否存在正在執行的子進程,如RDB/AOF子進程,若是存在bgsave命令直接返回。
2)父進程執行fork操做建立子進程,fork操做過程當中父進程會阻塞,經過info stats命令查看latest_fork_usec選項,能夠獲取最近一個ork操做的耗時,單位爲微秒。
3)父進程fork完成後,bgsave命令返回"Background saving started"信息並再也不阻塞父進程,能夠繼續響應其餘命令。
4)子進程建立RDB文件,根據父進程內存生成臨時快照文件,完成對原有文件執行原子替換。執行lastsave命令能夠獲取最後一次生成RDB的時間,對應info統計的rdb_last_save_time選項。
5)進程發送信號給父進程表示完成,父進程更新統計信息,具體見info Persistence下的rdb_*相關選項。
RDB文件的處理
保存:RDB文件保存在dir配置指定的目錄下,文件名經過dbfilename配置指定。能夠經過執行config set dir {newDir}和config set dbfilename {newFileName}運行期動態執行,當下次運行時RDB會保存到新目錄。
當遇到壞盤或磁盤寫滿等待狀況時,能夠經過config set dir {newDir}在線修改文件路徑到可用的磁盤路徑,以後執行bgsave進行bgsave進程磁盤切換,一樣適用於AOF持久化文件。
壓縮:Redis默認採用LZF算法對生成的RDB文件作壓縮處理,壓縮後的文件遠遠小於內存大小,默認開啓,能夠經過config set rdbcompression {yes|no}動態修改。
雖然壓縮RDB會消耗CPU,但可大幅下降文件的體積,方便保存到硬盤或經過網絡發送給從節點,所以線上建議開啓。
校驗:若是Redis加載損壞的RDB文件時拒絕啓動,並打印以下日誌:
2740:M 04 Apr 22:06:42.835 # Short read or OOM loading DB. Unrecoverable error, aborting now. 2740:M 04 Apr 22:06:42.835 # Internal error in RDB reading function at rdb.c:1666 -> Unexpected EOF reading RDB file
可使用Redis提供的redis-check-rdb工具檢測RDB文件並獲取對應用的錯誤報告。
[redis@rhel7 ~]$ redis-check-rdb dump.rdb [offset 0] Checking RDB file dump.rdb [offset 27] AUX FIELD redis-ver = '4.0.13' [offset 41] AUX FIELD redis-bits = '64' [offset 53] AUX FIELD ctime = '1554386780' [offset 68] AUX FIELD used-mem = '570072' [offset 84] AUX FIELD aof-preamble = '0' [offset 86] Selecting DB ID 0 --- RDB ERROR DETECTED --- [offset 109] Invalid object type: 209 [additional info] While doing: read-type [info] 2 keys read [info] 0 expires [info] 0 already expired
RDB的優缺點
優勢:RDB是一個緊湊壓縮的二進程文件,表明Redis在某個時間點上的數據快照。很是適用於備份,全量複製等場景。好比第6小時執行bgsave備份,並把RDB文件拷貝到遠程機器或者文件系統中(如hdfs),用於災難恢復。Redis加載RDB恢復數據遠遠快於AOF的方式。
缺點:RDB方式數據沒辦法作到實時持久化/秒級持久化。由於bgsave每次運行都要執行fork操做建立子進程,屬於重量級操做,頻繁執行成本太高。RDB文件使用特定二進程格式保存,Redis版本演進過程當中有多個格式的RDB版本,存在老版本Redis服務沒法兼容新版RDB格式的問題。
針對RDB不短途實時持久化的問題,Redis提供了AOF持久化方式來解決。
AOF
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啓時再從新執行AOF文件中的命令達到恢復數據的目的。AOF的主要做用是解決了數據持久化的實時性,目前已是Redis持久化的主流方式。理解掌握好AOF持久化機制對咱們兼顧數據安全性和性能很是有幫助。
使用AOF
開啓AOF功能須要設置參數:appendonly yes,默認不開啓。AOF文件名經過appendfilename參數設置,默認文件名是appendonly.aof。保存路徑同RDB持久化方式一致,經過dir配置指定。AOF的工做流程操做:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啓加載(load),以下圖:
流程以下:
1)全部的寫入命令會追加到aof_buf(緩衝區)中。
2)AOF緩衝區根據對應的策略向硬盤作同步操做。
3)隨着AOF文件愈來愈大,須要按期對AOF文件進行重寫,達到壓縮的目的。
4)當Redis服務器重啓時,能夠加載AOF文件進行數據恢復。
瞭解AOF工做流程以後,下面針對每一個步驟作詳細介紹。
命令寫入
AOF命令寫入的內容直接是文本協議格式。例如set hello world這條命令,在AOF緩衝區會追加以下文本
*3 $3 set $5 hello $5 world
關於AOF的兩個疑惑:
1)AOF爲何直接採用文本協議格式?可能的理由以下:
文本協議具備很好的兼容性。
開啓AOF後,全部寫入命令都包含追加操做,直接採用協議格式,避免了二次處理開銷。
文本協議具備可讀性,方便直接修改和處理。
2)AOF爲何把命令追加到aof_buf中?Redis使用單線程響應命令,若是每次寫AOF文件命令都直接追加到硬盤,那麼性能徹底取決於當前硬盤負載。先寫入緩衝區aof_buf中,還有另外一個好處,Redis能夠提供多種緩衝區同步硬盤的策略,在性能和安全性方面作出平衡。
文件同步
Redis提供了多種AOF緩衝區同步文件策略,由參數appendfsync控制,不一樣的含義以下:
always:命令寫入aof_buf後調用系統fsync操做同步到AOF文件,fsync完成後線程返回
everysec:命令寫入aof_buf後調用系統write操做,write完成後線程返回。fsync同步文件操做由專門線程每秒調用一次
no:命令寫入aof_buf後調用系統write操做,不對AOF文件作fsync同步,同步硬盤操做由操做系統負責,一般同步週期最長30秒
系統調用write和fsync說明:
write操做會觸發延遲寫(delayed write)機制。Linux在內核操做頁緩衝區用來提升硬盤IO性能。write操做在寫入系統緩衝區後直接返回。同步硬盤操做依賴於系統調度機制,例如:緩衝區頁空間寫滿或達到特定時間週期。同步文件以前,若是此時系統故障宕機,緩衝區內數據將丟失。
fsync針對單個文件操做(好比AOF文件),作強制硬盤同步,fsync將阻塞直到寫入硬盤完成後返回,保證了數據持久化。
除了write、fsync、Linux還提供了sync、fdatasync操做。
配置爲always時,每次寫入都要同步AOF文件,在通常的SATA硬盤上,Redis只能支持大約幾百TPS寫入,顯然跟Redis高性能特性背道而馳,不建議配置。
配置爲no,因爲操做系統每次同步AOF文件的週期不可控,並且會加大每次同步硬盤的數據量,雖然提高了性能,但數據安全性沒法保證。
配置爲everysec,是建議的同步策略,也是默認配置,作到兼顧性能和數據安全性。理論上只有在系統忽然宕機的狀況下丟失1秒的數據。(嚴格來講最多丟失1it數據是不許確的)。
重寫機制
隨着命令不斷寫入AOF,文件會愈來愈大,爲了解決這個問題,Redis引入AOF重寫機制壓縮文件體積。AOF文件重寫是把Redis進程內的數據轉化爲寫命令同步到新AOF文件的過程。
重寫後的AOF文件爲何能夠變小?有以下緣由:
1)進程內已經超時的數據再也不寫入文件
2)舊的AOF文件含有有效命令,如del key一、hdel key二、srem keys、set a1十一、set a222等。重寫使用進程內數據直接生成,這樣新的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重寫時文件最小體積,默認爲64M。
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重寫,會輸出以下日誌:
18827:M 04 Apr 23:30:49.519 * Starting automatic rewriting of AOF on 1054% growth 18827:M 04 Apr 23:30:49.520 * Background append only file rewriting started by pid 21365 18827:M 04 Apr 23:30:50.617 * AOF rewrite child asks to stop sending diffs. 21365:C 04 Apr 23:30:50.618 * Parent agreed to stop sending diffs. Finalizing AOF... 21365:C 04 Apr 23:30:50.618 * Concatenating 0.03 MB of AOF diff received from parent. 21365:C 04 Apr 23:30:50.631 * SYNC append only file rewrite performed 21365:C 04 Apr 23:30:50.632 * AOF rewrite: 7 MB of memory used by copy-on-write 18827:M 04 Apr 23:30:50.641 * Background AOF rewrite terminated with success 18827:M 04 Apr 23:30:50.641 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB) 18827:M 04 Apr 23:30:50.641 * Background AOF rewrite finished successfully
當觸發AOF重寫時,內存作了哪些事呢?結合下圖介紹運行流程
流程說明:
1)執行AOF重寫請示
若是當前進程正在執行AOF重寫,請求不執行並返回以下響應:
(error) 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重寫。
重啓加載
AOF和RDB文件均可以用於服務器重啓時的數據恢復。以下圖所示,表示Redis持久化文件加載流程。
流程說明:
1)AOF持久化開啓且存在AOF文件時,優先加載AOF文件,打印以下日誌
18827:M 04 Apr 23:21:01.257 * DB loaded from append only file: 1.207 seconds
2)AOF關閉或AOF文件不存在時,加載RDB文件,打印以下日誌:
7792:M 01 Apr 22:33:58.418 * DB loaded from disk: 0.003 seconds
3)加載AOF/RDB文件成功後,Redis啓動成功
4)AOF/RDB文件存在錯誤時,Redistribute啓動失敗並打印錯誤信息。
文件校驗
加載損壞的AOF文件時會拒絕啓動,並打印以下日誌:
Bad file format reading the append only file: make a backup of your AOF file,then use ./redis-check-aof --fix <filename>
對於錯誤格式的AOF文件,先進行備份,而後採用redis-check-aof --fix命令進行修復,修復後使用diff -u對比數據的差別,找出丟失的數據,有些能夠人工修改補全。
AOF文件可能存在結尾不完整的狀況,好比機器忽然掉電致使AOF尾部命令寫入不全。Redis爲咱們提供了aof-load-truncated配置來兼容這種狀況,默認開啓。加載AOF時,當遇到此問題時會忽略並繼續啓動,同時打印以下警告日誌:
27752:M 04 Apr 23:47:34.655 # !!! Warning: short read while loading the AOF file !!! 27752:M 04 Apr 23:47:34.655 # !!! Truncating the AOF at offset 1377811 !!! 27752:M 04 Apr 23:47:34.655 # AOF loaded anyway because aof-load-truncated is enabled
博文內容摘自《Redis開發與運維》一書。