在說Redis持久化以前,須要搞明白什麼是數據庫狀態,由於持久化就是將內存中的數據庫狀態保存到磁盤上。那麼什麼是數據庫狀態呢?Redis是一個key-value數據庫服務器,通常默認有16個數據庫,可使用select <index>命令進行切換(0-15),這每一個非空的數據庫又能夠包含任意多個鍵值對,爲了方便起見,咱們將數據庫服務器中的非空數據庫以及它們的鍵值對一般爲數據庫狀態,因此持久化,說的不是一個數據庫,而是服務器上的全部非空數據庫。redis
Redis中數據存儲模式有2種:cache-only, persistence;算法
- cache-only 即只作爲「緩存」服務,不持久數據,數據在服務終止後將消失,此模式下也將不存在「數據恢復」的手段,是一種安全性低/效率高/容易擴展的方式;
- persistence 即爲內存中的數據持久備份到磁盤文件,在服務重啓後能夠恢復,此模式下數據相對安全。
對於 persistence 持久化存儲,Redis 有兩種持久化方案,RDB(Redis DataBase)和 AOF(Append-Only File)。其中RDB是一分內存快照,AOF則爲可回放的命令日誌,他們各有特色也相互獨立。4.0開始容許使用RDB-AOF混合持久化的方式,結合了二者的優勢,經過 aof-use-rdb-preamble 配置項能夠打開混合開關。數據庫
RDB 就是 Snapshot 快照存儲,是默認的持久化方式。它按照必定的策略週期性地將數據存儲到磁盤,生成名爲 dump.rdb 的文件,RDB的執行週期能夠經過配置文件中的save來配置。在指定的時間間隔內,執行指定次數的寫操做,會將內存中的數據寫入到磁盤中。即在指定目錄下生成一個 dump.rdb 文件。Redis 重啓會經過加載 dump.rdb 文件恢復數據。實際操做過程是 fork 一個子進程,先將數據集寫入臨時文件,寫入成功後,再替換以前的文件,用二進制壓縮存儲。apache
RDB 持久化默認生成的文件名爲 dump.rdb ,這個能夠經過配置文件配置,RDB文件一個通過壓縮的二進制文件,接下來介紹一些RDB文件結構,RDB文件包含五個部分,分別是【Redis | db_version | databases | EOF | check_sum】開頭的Redis表示這是一個RDB文件,服務器能夠經過這個快速檢查載入的文件是不是RDB文件,db_version 是一個整數,表明RDB文件的版本,databases 部分包含0個或者是任意多個數據庫,以及數據庫中的鍵值對數據,若是數據庫狀態是空,那麼這部分也是空的,這部分的結構以下【SELECTDB | db_number | key_values_pairs】其中 SELECTDB 是一個常量,長度是一字節,當服務器遇到這個的時候,知道接下來要讀入的將是一個數據庫號碼,db_number中保存的是一個數據庫號碼(0-15)這兩部分結合就能夠切換到相應的數據庫,而後讀取鍵值對了,鍵值對的部分的結構有兩種,一種是帶過時值的,一種是不帶過時值的,若是是帶過時值,那麼結構是【EXPIRETIME_MS | ms | TYPE | key | value】第一個常量和 SELECTDB 同樣,第二部分是毫秒爲單位的 UNIX 時間戳,就是鍵值對的過時時間,而後是 TYPE 記錄了 value 的類型,是 String,list,set,zset,hash 等。不帶過時值的鍵值對部分的結構沒有前兩部分,只是【TYPE | key | value】。緩存
RDB持久化能夠經過命令進行手動觸發,也能夠配置好後讓服務器自動觸發,手動觸發可使用Redis命令【SAVE】或者【BGSAVE】,這兩個命令有一些差異:save 命令會阻塞服務器進程,save 命令執行的時候服務器不可以處理任何命令請求,直到 save 命令執行完畢,RDB文件建立完畢。bgsave 命令不會阻塞服務器,而是經過派生出一個子進程,而後由子進程負責建立RDB文件,服務器進程繼續處理命令請求,這裏須要說明一下,bgsave 處理期間,服務器進程雖然可以繼續處理命令請求,可是save,bgsave,bgrewriteaof 這三個命令的處理方式會和平時有所不一樣,在 bgsave 期間,save 命令會被服務器拒絕,服務器禁止 save 和 bgsave 同時執行,避免父進程和子進程同時執行兩個rdbSave(建立RDB文件的實際工做其實是由 rdbSave 函數完成,save 和 bgsave 都會調用這個函數,只是調用的方式不一樣)調用,bgsave 命令在 bgsave 期間也會被拒絕,理由和拒絕 save 的理由同樣,兩個 bgsave 也會產生競爭,bgrewriteaof 命令會被延遲到 bgsave 執行完畢以後執行。 安全
RDB操做借用 copy on write 機制進行寫時複製,父進程 fork 一個子進程,由子進程進行內存遍歷將數據寫入臨時文件,父進程仍處理客戶端請求,待子進程執行完畢,將臨時文件 rename 爲 dump.rdb,所以不管RDB是否成功,dump.rdb 都是完整的。服務器
bgsave 是主流的觸發RDB持久化方式,下圖是運做流程:網絡
Redis內部還存在自動觸發RDB的持久化機制,例如一下場景:架構
1)使用 save 相關配置,如‘save m n’表示m秒以內數據集存在n次修改時,自動觸發 bgsave。併發
2)若是從節點執行全量複製操做,主節點自動執行 bgsave 生成RDB文件併發送給從節點。
3)執行 debug reload 命令從新加載Redis時,也會自動觸發save操做。
4)默認狀況下執行 shutdown 命令時,若是沒有開啓AOF持久化功能則自動執行 bgsave。
Redis沒有專門的RDB文件載入命令,只要Redis服務器開啓,就會檢測RDB文件是否存在,就會自動載入RDB文件,這裏須要說明一點,若是服務器開啓了AOF持久化功能,服務器會優先使用AOF文件來還原數據庫狀態,只有在AOF持久化功能關閉的時候,纔會使用RDB文件來還原數據庫狀態。
接下來講一下,RDB自動保存,前面已經說了,RDB能夠經過手動執行,SAVE命令和BGSAVE命令,也能夠經過配置讓服務器自動執行,那麼如何配置呢?
Redis.conf中能夠配置,默認配置以下:
save 900 1 save 300 10 save 60 10000
以上表示的意思是:
- 900秒以內對服務進行了至少一次修改
- 300秒以內服務器進行了至少10次修改
- 60秒以內對服務器進行了至少10000次修改。
這些條件知足其中的任意一個bgsave命令就會自動執行。
那麼服務器是如何知道我作了多少修改的?服務器中有個dirty計數器和一個lastsave時間戳,當服務器執行一個數據庫修改命令以後,dirty計數器就會進行更新,命令修改了多少次數據庫,dirty就會增長多少,如:【set msg hello】修改了一個,那麼dirty就加一,若是【mset msg word name nihao age 20】那麼dirty就增長三。lastsave屬性記錄上次服務器執行保存操做的時間,是一個unix時間戳,經過這兩個屬性,能夠很簡單的距離上次保存已經多少時間了,以及修改了多少次數據庫,一旦知足以上三個條件,那麼就自動調用bgsave命令,同時更新lastsave屬性和dirty屬性歸零。
至於檢查保存條件是否知足這個工做,是由Redis服務器週期性操做函數serverCron默認間隔100毫秒執行一次檢查,這個函數有不少地方用到,注意一下,這個函數是對正在運行的服務器進行維護的函數,在Redis事件中會有提到(Redis服務器是一個事件驅動程序,什麼是事件驅動呢?就是發生事件的時候纔會動一下,否則就跟死了同樣,事件驅動又分爲文件事件和時間事件,ServerCron就是一種時間驅動,至於文件驅動,其實就是客戶端發過來一個命令,服務器纔會去執行,而後給客戶端返回結果)
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提供的RDB文件檢查工具 redis-check-dump 檢測RDB文件是否完整並獲取對應的錯誤報告。
RDB的優缺點
優勢:
一、RDB是一個緊湊壓縮的二進制文件,表明Redis在某一個時間點上的數據快照。很是適合用於備份,全量複製等場景。好比每6小時執行bgsave備份,並把RDB文件拷貝到遠程機器或者文件系統中(如hdfs),用於災難恢復。(一旦採用該方式,那麼你的整個Redis數據庫將只包含一個文件,這對於文件備份而言是很是完美的。好比,你可能打算每一個小時歸檔一次最近24小時的數據,同時還要天天歸檔一次最近30天的數據。經過這樣的備份策略,一旦系統出現災難性故障,咱們能夠很是容易的進行恢復。)
二、性能最大化。對於Redis的服務進程而言,在開始持久化時,它惟一須要作的只是fork出子進程,以後再由子進程完成這些持久化的工做,這樣就能夠極大的避免服務進程執行IO操做了。
三、若是數據集很大,RDB的啓動、加載RDB恢復數據的效率遠遠快於AOF方式,若是業務對數據完整性和一致性要求不高,RDB是很好的選擇。
缺點:
1 數據的完整性和一致性不高,由於RDB可能在最後一次備份時宕機了。(若是你想保證數據的高可用性,即最大限度的避免數據丟失,那麼RDB將不是一個很好的選擇。由於系統一旦在定時持久化以前出現宕機現象,此前沒有來得及寫入磁盤的數據都將丟失。)
二、備份時佔用內存,由於Redis 在備份時會獨立建立一個子進程,將數據寫入到一個臨時文件(此時內存中的數據是原來的兩倍),最後再將臨時文件替換以前的備份文件.RDB方式數據沒辦法作到實時持久化/秒級持久化。由於bgsave每次運行都要執行fork操做建立子進程,屬於重量級操做,頻繁執行成本太高。因此Redis 的持久化和數據的恢復要選擇在夜深人靜的時候執行是比較合理的。
三、RDB文件使用特定二進制格式保存,Redis版本演進過程當中有多個格式的RDB版本,存在老版本Redis服務沒法兼容新版RDB格式的問題。
RDB文件恢復數據
將dump.rdb 文件拷貝到redis的安裝目錄的bin目錄下,重啓redis服務便可。在實際開發中,通常會考慮到物理機硬盤損壞狀況,選擇備份dump.rdb 。
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啓時再從新執行AOF文件中命令達到恢復數據的目的。Redis 默認不開啓。AOF的主要做用是爲了彌補RDB的不足(數據的不一致性),解決數據持久化的實時性,目前已是Redis持久化的主流方式。
AOF 將「操做 + 數據」以格式化指令的方式追加到操做日誌文件的尾部,在append操做返回後(已經寫入到文件或者即將寫入),才進行實際的數據變動,「日誌文件」保存了歷史全部的操做過程;當server須要數據恢復時,能夠直接replay此日誌文件,便可還原全部的操做過程。AOF相對可靠,它和MySQL中bin.log、apache.log、zookeeper中txn-log簡直殊途同歸。AOF文件內容是字符串,很是容易閱讀和解析。
使用AOF
開啓AOF功能須要設置配置:appendonly yes,默認不開啓。AOF文件經過appendfilename 配置設置,默認文件名是appendonly.aof。保存路徑同RDB持久化方式一致。經過dir配置指定。AOF的工做流程操做:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啓加載(load),工做流程以下:
流程以下:
1) 全部的寫入命令會追加到aof_buf(緩衝區)中。
① AOF命令寫入的內容直接是文本協議格式,爲何直接採用文本協議格式?
理由以下:文本協議具備很好的兼容性;開啓AOF後,全部寫入命令都包含追加操做,直接採用協議格式,避免二次處理開銷;文本協議具備可讀性,方便直接修改和處理。
② AOF爲何把命令追加到aof_buf中?
理由以下:Redis使用單線程響應命令,若是每次寫AOF文件命令都直接追加到硬盤,那麼性能徹底取決於當前硬盤負載。縣寫入緩衝區aof_buf中,還有另外一個好處,Redis能夠提供多種緩衝區同步硬盤的策略,在性能和安全性方面作出平衡。
2) AOF緩衝區根據對應的策略向硬盤作同步操做。
Redis提供了多種AOF緩衝區同步文件策略,由參數appendfsync控制:
always:同步持久化,每次發生數據變化會馬上寫入到磁盤中。性能較差當數據完整性比較好(慢,安全)
everysec:出廠默認推薦,每秒異步記錄一次(默認值)
no:不一樣步
3) 隨着AOF文件愈來愈大,須要按期對AOF文件進行重寫,達到壓縮的目的。
隨着命令不斷寫入AOF,文件會愈來愈大,Redis引入了AOF重寫機制壓縮文件體積。AOF文件重寫是把Redis進程內的數據轉化爲寫命令同步到新AOF文件的過程。
AOF重寫由父進程fork一個子進程,子進程遍歷數據庫內存(並無讀取舊文件)並將數據記錄到臨時文件,父進程繼續接收客戶端請求,將後續寫操做追加到appendonly.aof和AOF重寫緩存,待子進程執行完畢,將緩存內容追加到臨時文件,並rename爲appendonly.aof完成重寫操做.
觸發機制:當AOF文件大小是上次rewrite後大小的一倍且文件大於64M時觸發。這裏的「一倍」和「64M」 能夠經過配置文件修改。
重寫後的AOF文件爲何能夠變下?有以下緣由:
① 進程內已經超時的數據再也不寫文件。
② 舊的AOF文件含有無效命令,如del key一、 hdel key二、srem keys、set a 1十一、set a 222等。重寫使用進程內數據直接生成,這樣新的AOF文件只保留最終數據的寫入命令。
③ 多條寫命令能夠合併爲一個,如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(運行AOF重寫時文件最小體積,默認爲64MB)和auto-aof-rewrite-percentage參數肯定自動觸發時機
4) 當Redis服務重啓時,能夠加載AOF文件進行數據恢復。
RDB的優缺點
優勢:
一、該機制能夠帶來更高的數據安全性,即數據持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不一樣步。事實上,每秒同步也是異步完成的,其效率也是很是高的,所差的是一旦系統出現宕機現象,那麼這一秒鐘以內修改的數據將會丟失。而每修改同步,咱們能夠將其視爲同步持久化,即每次發生的數據變化都會被當即記錄到磁盤中。能夠預見,這種方式在效率上是最低的。至於無同步,無需多言,我想你們都能正確的理解它。
二、因爲該機制對日誌文件的寫入操做採用的是append模式,所以在寫入過程當中即便出現宕機現象,也不會破壞日誌文件中已經存在的內容。然而若是咱們本次操做只是寫入了一半數據就出現了系統崩潰問題,不用擔憂,在Redis下一次啓動以前,咱們能夠經過redis-check-aof工具來幫助咱們解決數據一致性的問題。
三、若是日誌過大,Redis能夠自動啓用rewrite機制。即Redis以append模式不斷的將修改數據寫入到老的磁盤文件中,同時Redis還會建立一個新的文件用於記錄此期間有哪些修改命令被執行。所以在進行rewrite切換時能夠更好的保證數據安全性。
四、AOF包含一個格式清晰、易於理解的日誌文件用於記錄全部的修改操做。事實上,咱們也能夠經過該文件完成數據的重建。
缺點:
一、對於相同數量的數據集而言,AOF文件一般要大於RDB文件。RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。
二、根據同步策略的不一樣,AOF在運行效率上每每會慢於RDB。總之,每秒同步策略的效率是比較高的,同步禁用策略的效率和RDB同樣高效。
AOF文件恢復數據
正常狀況下,將appendonly.aof 文件拷貝到redis的安裝目錄的bin目錄下,重啓redis服務便可。但在實際開發中,可能由於某些緣由致使appendonly.aof 文件格式異常,從而致使數據還原失敗,能夠經過命令redis-check-aof --fix appendonly.aof 進行修復 。
AOF和RDB文件均可以用於服務器重啓時的數據恢復。如圖所示,表示Redis持久化文件加載流程:
Redis 默認開啓RDB持久化方式,在指定的時間間隔內,執行指定次數的寫操做,則將內存中的數據寫入到磁盤中。 RDB 持久化適合大規模的數據恢復但它的數據一致性和完整性較差。 Redis 須要手動開啓AOF持久化方式,默認是每秒將寫操做日誌追加到AOF文件中。
AOF 數據完整性比RDB高,更加安全,能夠將數據更加及時的同步到文件中,可是AOF須要較多的磁盤IO開支。記錄內容多了,會影響數據恢復的效率;AOF文件尺寸較大,文件內容恢復數度相對較慢。
RDB snapshot,安全性較差,它是「正常時期」數據備份以及master-slave數據同步的最佳手段,文件尺寸較小,恢復數度較快。
Redis 針對 AOF文件大的問題,提供重寫的瘦身機制。若只打算用Redis 作緩存,能夠關閉持久化。若打算使用Redis 的持久化。建議RDB和AOF都開啓。其實RDB更適合作數據的備份,留一後手。AOF出問題了,還有RDB。
兩者選擇的標準,就是看系統是願意犧牲一些性能,換取更高的緩存一致性(AOF),仍是願意寫操做頻繁的時候,不啓用備份來換取更高的性能,待手動運行save的時候,再作備份(RDB)。RDB這個就更有些 eventually consistent的意思了。
能夠經過配置文件來指定它們中的一種,或者同時使用它們(不建議同時使用),或者所有禁用,在架構良好的環境中,master一般使用AOF,slave使用snapshot,主要緣由是master須要首先確保數據完整性,它做爲數據備份的第一選擇;slave提供只讀服務(目前slave只能提供讀取服務),它的主要目的就是快速響應客戶端read請求;可是若是你的redis運行在網絡穩定性差/物理環境糟糕狀況下,建議你master和slave均採起AOF,這個在master和slave角色切換時,能夠減小「人工數據備份」/「人工引導數據恢復」的時間成本;若是你的環境一切很是良好,且服務須要接收密集性的write操做,那麼建議master採起snapshot,而slave採用AOF。
補充:RDB+AOF 恢復方案
RDB恢復數據不完整,AOF恢復速度慢,爲了解決這兩大問題,可使用RDB+AOF的方案。
RDB+AOF組合方案是指Redis同時開啓RDB和AOF選項,以AOF爲主記錄日誌,當日志文件達到閾值觸發AOF重寫時,再也不使用原有的重寫機制,而讓Redis服務fork一個子進程執行RDB操做,生成一個臨時RDB文件,主進程依然接受客戶端請求,並將命令寫入AOF文件和一個臨時AOF文件中,待子進程結束,將新生成的RDB臨時文件rename爲dump.rdb,而將臨時AOF文件rename爲appendonlyfile.aof,至此一次RDB+AOF組合的持久化就完成了。持久化生成的RDB和AOF文件都將用來進行數據恢復,恢復策略是首先Redis數據庫加載RDB文件,將數據庫恢復到最新一次快照時的狀態,而後模擬客戶端,將AOF文件中的命令執行一遍,使數據庫恢復到上次關機或故障時的狀態,這樣數據庫的恢復就完成了。