Redis持久化之RDB&AOF


  在說Redis持久化之前,需要搞明白什么是數據庫狀態,因為持久化就是將內存中的數據庫狀態保存到磁盤上。那么什么是數據庫狀態呢?Redis是一個key-value數據庫服務器,一般默認有16個數據庫,可以使用select <index>命令進行切換(0-15),這每個非空的數據庫又可以包含任意多個鍵值對,為了方便起見,我們將數據庫服務器中的非空數據庫以及它們的鍵值對通常為數據庫狀態,所以持久化,說的不是一個數據庫,而是服務器上的所有非空數據庫。

  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詳解

  RDB 就是 Snapshot 快照存儲,是默認的持久化方式。它按照一定的策略周期性地將數據存儲到磁盤,生成名為 dump.rdb 的文件,RDB的執行周期可以通過配置文件中的save來配置。在指定的時間間隔內,執行指定次數的寫操作,會將內存中的數據寫入到磁盤中。即在指定目錄下生成一個 dump.rdb 文件。Redis 重啟會通過加載 dump.rdb 文件恢復數據。實際操作過程是 fork 一個子進程,先將數據集寫入臨時文件,寫入成功后,再替換之前的文件,用二進制壓縮存儲。

  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的優缺點

  優點:

    1、RDB是一個緊湊壓縮的二進制文件,代表Redis在某一個時間點上的數據快照。非常適合用於備份,全量復制等場景。比如每6小時執行bgsave備份,並把RDB文件拷貝到遠程機器或者文件系統中(如hdfs),用於災難恢復。(一旦采用該方式,那么你的整個Redis數據庫將只包含一個文件,這對於文件備份而言是非常完美的。比如,你可能打算每個小時歸檔一次最近24小時的數據,同時還要每天歸檔一次最近30天的數據。通過這樣的備份策略,一旦系統出現災難性故障,我們可以非常容易的進行恢復。)

    2、性能最大化。對於Redis的服務進程而言,在開始持久化時,它唯一需要做的只是fork出子進程,之后再由子進程完成這些持久化的工作,這樣就可以極大的避免服務進程執行IO操作了。

    3、如果數據集很大,RDB的啟動、加載RDB恢復數據的效率遠遠快於AOF方式,如果業務對數據完整性和一致性要求不高,RDB是很好的選擇。

  缺點:

    1 數據的完整性和一致性不高,因為RDB可能在最后一次備份時宕機了。(如果你想保證數據的高可用性,即最大限度的避免數據丟失,那么RDB將不是一個很好的選擇。因為系統一旦在定時持久化之前出現宕機現象,此前沒有來得及寫入磁盤的數據都將丟失。)

    2、備份時占用內存,因為Redis 在備份時會獨立創建一個子進程,將數據寫入到一個臨時文件(此時內存中的數據是原來的兩倍),最后再將臨時文件替換之前的備份文件.RDB方式數據沒辦法做到實時持久化/秒級持久化。因為bgsave每次運行都要執行fork操作創建子進程,屬於重量級操作,頻繁執行成本過高。所以Redis 的持久化和數據的恢復要選擇在夜深人靜的時候執行是比較合理的。

    3、RDB文件使用特定二進制格式保存,Redis版本演進過程中有多個格式的RDB版本,存在老版本Redis服務無法兼容新版RDB格式的問題。

RDB文件恢復數據

  將dump.rdb 文件拷貝到redis的安裝目錄的bin目錄下,重啟redis服務即可。在實際開發中,一般會考慮到物理機硬盤損壞情況,選擇備份dump.rdb 。

二. AOF詳解

  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 key1、 hdel key2、srem keys、set a 111、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的優缺點

  優點:

    1、該機制可以帶來更高的數據安全性,即數據持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事實上,每秒同步也是異步完成的,其效率也是非常高的,所差的是一旦系統出現宕機現象,那么這一秒鍾之內修改的數據將會丟失。而每修改同步,我們可以將其視為同步持久化,即每次發生的數據變化都會被立即記錄到磁盤中。可以預見,這種方式在效率上是最低的。至於無同步,無需多言,我想大家都能正確的理解它。

    2、由於該機制對日志文件的寫入操作采用的是append模式,因此在寫入過程中即使出現宕機現象,也不會破壞日志文件中已經存在的內容。然而如果我們本次操作只是寫入了一半數據就出現了系統崩潰問題,不用擔心,在Redis下一次啟動之前,我們可以通過redis-check-aof工具來幫助我們解決數據一致性的問題。

    3、如果日志過大,Redis可以自動啟用rewrite機制。即Redis以append模式不斷的將修改數據寫入到老的磁盤文件中,同時Redis還會創建一個新的文件用於記錄此期間有哪些修改命令被執行。因此在進行rewrite切換時可以更好的保證數據安全性。

    4、AOF包含一個格式清晰、易於理解的日志文件用於記錄所有的修改操作。事實上,我們也可以通過該文件完成數據的重建。

  缺點:

    1、對於相同數量的數據集而言,AOF文件通常要大於RDB文件。RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。

    2、根據同步策略的不同,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文件中的命令執行一遍,使數據庫恢復到上次關機或故障時的狀態,這樣數據庫的恢復就完成了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM