1. 程式人生 > >redis 持久化方式

redis 持久化方式

目的 .net sts nap auto span 通道 lock 同時

對於persistence持久化存儲,Redis提供了兩種持久化方法:

  • Redis DataBase(簡稱RDB)
    • 執行機制:快照,直接將databases中的key-value的二進制形式存儲在了rdb文件中
    • 優點:性能較高(因為是快照,且執行頻率比aof低,而且rdb文件中直接存儲的是key-values的二進制形式,對於恢復數據也快)
    • 使用單獨子進程來進行持久化,主進程不會進行任何IO操作,保證了redis的高性能
    • 缺點:在save配置條件之間若發生宕機,此間的數據會丟失
    • RDB是間隔一段時間進行持久化,如果持久化之間redis發生故障,會發生數據丟失。所以這種方式更適合數據要求不嚴謹的時候
  • Append-only file (簡稱AOF)
    • 執行機制:將對數據的每一條修改命令追加到aof文件
    • 優點:數據不容易丟失
    • 可以保持更高的數據完整性,如果設置追加file的時間是1s,如果redis發生故障,最多會丟失1s的數據;且如果日誌寫入不完整支持redis-check-aof來進行日誌修復;AOF文件沒被rewrite之前(文件過大時會對命令進行合並重寫),可以刪除其中的某些命令(比如誤操作的flushall)
    • 缺點:性能較低(每一條修改操作都要追加到aof文件,執行頻率較RDB要高,而且aof文件中存儲的是命令,對於恢復數據來講需要逐行執行命令,所以恢復慢)
    • AOF文件比RDB文件大,且恢復速度慢。

除了這兩種方法,Redis在早起的版本還存在虛擬內存的方法,現在已經被廢棄。

一、RDB概述

RDB是在某個時間點將數據寫入一個臨時文件,持久化結束後,用這個臨時文件替換上次持久化的文件,達到數據恢復。

這裏說的這個執行數據寫入到臨時文件的時間點是可以通過配置來自己確定的,通過配置redis在n秒內如果超過m個key被修改這執行一次RDB操作。這個操作就類似於在這個時間點來保存一次Redis的所有數據,一次快照數據。所有這個持久化方法也通常叫做snapshots。

RDB默認開啟,redis.conf中的具體配置參數如下;

  1. #dbfilename:持久化數據存儲在本地的文件
  2. dbfilename dump.rdb
  3. #dir:持久化數據存儲在本地的路徑,如果是在/redis/redis-3.0.6/src下啟動的redis-cli,則數據會存儲在當前src目錄下
  4. dir ./
  5. ##snapshot觸發的時機,save <seconds> <changes>
  6. ##如下為900秒後,至少有一個變更操作,才會snapshot
  7. ##對於此值的設置,需要謹慎,評估系統的變更操作密集程度
  8. ##可以通過“save “””來關閉snapshot功能 (即禁止使用rdb)

#save時間,以下分別表示更改了1個key時間隔900s進行持久化存儲;更改了10個key300s進行存儲;更改10000個key60s進行存儲。
save 900 1
save 300 10
save 60 10000
  1. ##當snapshot時出現錯誤無法繼續時,是否阻塞客戶端“變更操作”,“錯誤”可能因為磁盤已滿/磁盤故障/OS級別異常等
  2. #當後臺RDB進程導出快照(一部分的key-value)到rdb文件這個過程出錯時(即最後一次的後臺保存失敗時),
  3. #redis主進程是否還接受向數據庫寫數據
  4. #該種方式會讓用戶知道在數據持久化到硬盤時出錯了(相當於一種監控);
  5. #如果安裝了很好的redis持久化監控,可設置為"no"
stop-writes-on-bgsave-error yes  
##是否啟用rdb文件壓縮,默認為“yes”,壓縮往往意味著“額外的cpu消耗”,同時也意味這較小的文件尺寸以及較短的網絡傳輸時間 (如果希望RDB進程節省一點CPU時間,設置為no,但是可能最後的rdb文件會很大)
  1. rdbcompression yes
  2. #在redis重啟後,從rdb文件向內存寫數據之前,是否先檢測該rdb文件是否損壞(根據rdb文件中的校驗和check_sum)
  3. rdbchecksum yes

snapshot觸發的時機,是有“間隔時間”和“變更次數”共同決定,同時符合2個條件才會觸發snapshot,否則“變更次數”會被繼續累加到下一個“間隔時間”上。snapshot過程中並不阻塞客戶端請求。snapshot首先將數據寫入臨時文件,當成功結束後,將臨時文件重名為dump.rdb。

  • 以上三條save命令只要發生任一條,bgsave命令都會發生,這就有兩個問題,假設60s內有10000個key發生了改變(寫入、刪除、更新),那麽是否會立即進行持久化呢?在這次持久化之後,假設又過了240s,而在此期間沒有任何的key的改變操作,此時是否要發生一次持久化(因為滿足300s發生了10個key的改變,這裏是改變了10000個key)?
    • 不會立即進行持久化:redis默認每隔100ms使用serverCron函數檢查一次save配置的條件是否滿足,滿足則進行bgsave,這樣的話,如果在100ms內,我已經滿足了bgsave的條件,那麽我真正執行bgsave的時候也要等到serverCron執行過來的時候
    • 不會再發生持久化:redis有兩個參數dirty(記錄上一次bgsave之後的key的修改數,上邊的在240s內例子就是0)和lastsave(上一次成功執行bgsave命令的時間),配置中的每一個save配置的修改數指的就是dirty,而每一個時間段就是以lastsave為起點計算的。
  • 註釋掉所有的save命令,RDB將不起作用
  • rdbcompression yes:配置成這樣是不是每一個字符串在存儲到rdb文件中時,都要進行一次壓縮操作?
    • 不是:設置為yes之後,只有當字符串的長度大於等於21個字節時,才會進行壓縮
  • rdbchecksum yes:這個校驗和存儲在哪裏?為什麽通過比對校驗和可以判斷文件是否損壞?
    • 校驗和(check_sum)存儲在RDB文件的最後八個字節中(詳細的RDB文件結構,查看《Redis這基於實現》"第10章 RDB持久化"),簡單的RDB文件結構如下:技術分享圖片
      • RDB文件開頭的前五個字節"REDIS"是判斷一個文件是不是RDB文件的標準(類似於class文件中的"魔數")
      • 接下來的4個字節:RDB文件版本號(db_version)
      • databases(註意是復數):這裏存放各個庫redisDb中存儲的key-value信息(是整個數據持久化和恢復的核心)
      • EOF(1個字節):RDB文件正文的結束
      • check_sum(8個字節):檢驗和,該值是根據前邊四部分值算出來的,在持久化的時候將該值算出來並寫入rdb文件的末尾;在根據rdb文件恢復數據的時候,再根據rdb文件中的前邊四部分值計算出一個校驗和,然後與當前rdb文件中的check_sum(即後八個字節)的內容進行比對,如果一樣,說明沒損壞,如果不一樣,說明前四部分有數據損壞(即該文件損壞)
  • 在Redis服務器啟動時,redis會自動檢測是否有rdb文件(前提是沒有aof的時候),如果有,則根據rdb文件恢復數據,此時在恢復數據完成之前,會阻塞客戶端對redis的讀寫操作

使用RDB恢復數據:
自動的持久化數據存儲到dump.rdb後。實際只要重啟redis服務即可完成(啟動redis的server時會從dump.rdb中先同步數據)

客戶端使用命令進行持久化save存儲:

  1. ./redis-cli -h ip -p port save
  2. ./redis-cli -h ip -p port bgsave

一個是在前臺進行存儲,一個是在後臺進行存儲。我的client就在server這臺服務器上,所以不需要連其他機器,直接./redis-cli bgsave。由於redis是用一個主線程來處理所有 client的請求,這種方式會阻塞所有client請求。所以不推薦使用。另一點需要註意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不是增量的只同步臟數據。如果數據量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴重影響性能。

二、AOF概述

Append-only file,將“操作 + 數據”以格式化指令的方式追加到操作日誌文件的尾部,在append操作返回後(已經寫入到文件或者即將寫入),才進行實際的數據變更,“日誌文件”保存了歷史所有的操作過程;當server需要數據恢復時,可以直接replay此日誌文件,即可還原所有的操作過程。AOF相對可靠,它和mysql中bin.log、apache.log、zookeeper中txn-log簡直異曲同工。AOF文件內容是字符串,非常容易閱讀和解析。

我們可以簡單的認為AOF就是日誌文件,此文件只會記錄“變更操作”(例如:set/del等),如果server中持續的大量變更操作,將會導致AOF文件非常的龐大,意味著server失效後,數據恢復的過程將會很長;事實上,一條數據經過多次變更,將會產生多條AOF記錄,其實只要保存當前的狀態,歷史的操作記錄是可以拋棄的;因為AOF持久化模式還伴生了“AOF rewrite”。
AOF的特性決定了它相對比較安全,如果你期望數據更少的丟失,那麽可以采用AOF模式。如果AOF文件正在被寫入時突然server失效,有可能導致文件的最後一次記錄是不完整,你可以通過手工或者程序的方式去檢測並修正不完整的記錄,以便通過aof文件恢復能夠正常;同時需要提醒,如果你的redis持久化手段中有aof,那麽在server故障失效後再次啟動前,需要檢測aof文件的完整性。

AOF默認關閉,開啟方法,修改配置文件reds.conf:appendonly yes

  1. ##此選項為aof功能的開關,默認為“no”,可以通過“yes”來開啟aof功能
  2. ##只有在“yes”下,aof重寫/文件同步等特性才會生效
  3. appendonly yes
  4. ##aof文件的存放路徑與文件名稱
  5. appendfilename appendonly.aof
  6. ##指定aof操作中文件同步策略,有三個合法值:always everysec no,默認為everysec
  7. ##always 每一個命令,都立即同步到aof文件中去(很安全,但是速度慢,因為每一個命令都會進行一次磁盤操作)IO開支較大。
  8. ##everysec每秒將數據寫一次到aof文件,redis推薦的方式。如果遇到物理服務器故障,有可能導致最近一秒內aof記錄丟失(可能為部分丟失)。
  9. ##no 將寫入工作交給操作系統,由操作系統來判斷緩沖區大小,統一寫到aof文件(速度快,但是同步頻率低,容易丟數據)
  10. appendfsync everysec
  11. ##在aof-rewrite期間,appendfsync是否暫緩文件同步,"no"表示“不暫緩”,“yes”表示“暫緩”,默認為“no”
  12. # 在RDB持久化數據的時候,此時的aof操作是否停止,若為yes則停止
  13. # 在停止的這段時間內,執行的命令會寫入內存隊列,等RDB持久化完成後,統一將這些命令寫入aof文件
  14. # 該參數的配置是考慮到RDB持久化執行的頻率低,但是執行的時間長,而AOF執行的頻率高,執行的時間短,
  15. # 若同時執行兩個子進程(RDB子進程、AOF子進程)效率會低(兩個子進程都是磁盤讀寫)
  16. # 但是若改為yes可能造成的後果是,由於RDB持久化執行時間長,在這段時間內有很多命令寫入了內存隊列,
  17. # 最後導致隊列放不下,這樣AOF寫入到AOF文件中的命令可能就少了很多
  18. # 在恢復數據的時候,根據aof文件恢復就會丟很多數據
  19. # 所以,選擇no就好
  20. no-appendfsync-on-rewrite no
  21. ##aof文件rewrite觸發的最小文件尺寸(mb,gb),只有大於此aof文件大於此尺寸是才會觸發rewrite,默認“64mb”,建議“512mb”
  22. ## AOF重寫:把內存中的數據逆化成命令,然後將這些命令重新寫入aof文件
  23. # 重寫的目的:假設在我們在內存中對同一個key進行了100次操作,最後該key的value是100,
  24. # 那麽在aof中就會存在100條命令日誌,這樣的話,有兩個缺點:
  25. # 1)AOF文件過大,占據硬盤空間 2)根據AOF文件恢復數據極慢(需要執行100條命令)
  26. # 如果我們將內存中的該key逆化成"set key 100",然後寫入aof文件,
  27. # 那麽aof文件的大小會大幅度減少,而且根據aof文件恢復數據很快(只需要執行1條命令)
  28. # 註意:下邊兩個約束都要滿足的條件下,才會發生aof重寫;
  29. # 假設沒有第二個,那麽在aof的前期,只要稍微添加一些數據,就發生aof重寫
  30. # 當aof的增長的百分比是原來的100%(即是原來大小的2倍,例如原來是100m,下一次重寫是當aof文件是200m的時候),AOF重寫
  31. auto-aof-rewrite-min-size 64mb
  32. ##相對於“上一次”rewrite,本次rewrite觸發時aof文件應該增長的百分比。
  33. ##每一次rewrite之後,redis都會記錄下此時“新aof”文件的大小(例如A),那麽當aof文件增長到A*(1 + p)之後
  34. ##觸發下一次rewrite,每一次aof記錄的添加,都會檢測當前aof文件的尺寸。
  35. auto-aof-rewrite-percentage 100

AOF是文件操作,對於變更操作比較密集的server,那麽必將造成磁盤IO的負荷加重;此外linux對文件操作采取了“延遲寫入”手段,即並非每次write操作都會觸發實際磁盤操作,而是進入了buffer中,當buffer數據達到閥值時觸發實際寫入(也有其他時機),這是linux對文件系統的優化,但是這卻有可能帶來隱患,如果buffer沒有刷新到磁盤,此時物理機器失效(比如斷電),那麽有可能導致最後一條或者多條aof記錄的丟失。通過上述配置文件,可以得知redis提供了3中aof記錄同步選項:

  • always:每一條aof記錄都立即同步到文件,這是最安全的方式,也以為更多的磁盤操作和阻塞延遲,是IO開支較大。
  • everysec:每秒同步一次,性能和安全都比較中庸的方式,也是redis推薦的方式。如果遇到物理服務器故障,有可能導致最近一秒內aof記錄丟失(可能為部分丟失)。
  • no:redis並不直接調用文件同步,而是交給操作系統來處理,操作系統可以根據buffer填充情況/通道空閑時間等擇機觸發同步;這是一種普通的文件操作方式。性能較好,在物理服務器故障時,數據丟失量會因OS配置有關。

其實,我們可以選擇的太少,everysec是最佳的選擇。如果你非常在意每個數據都極其可靠,建議你選擇一款“關系性數據庫”吧。
AOF文件會不斷增大,它的大小直接影響“故障恢復”的時間,而且AOF文件中歷史操作是可以丟棄的。AOF rewrite操作就是“壓縮”AOF文件的過程,當然redis並沒有采用“基於原aof文件”來重寫的方式,而是采取了類似snapshot的方式:基於copy-on-write,全量遍歷內存中數據,然後逐個序列到aof文件中。因此AOF rewrite能夠正確反應當前內存數據的狀態,這正是我們所需要的;*rewrite過程中,對於新的變更操作將仍然被寫入到原AOF文件中,同時這些新的變更操作也會被redis收集起來(buffer,copy-on-write方式下,最極端的可能是所有的key都在此期間被修改,將會耗費2倍內存),當內存數據被全部寫入到新的aof文件之後,收集的新的變更操作也將會一並追加到新的aof文件中,此後將會重命名新的aof文件為appendonly.aof,此後所有的操作都將被寫入新的aof文件。如果在rewrite過程中,出現故障,將不會影響原AOF文件的正常工作,只有當rewrite完成之後才會切換文件,因為rewrite過程是比較可靠的。*

觸發rewrite的時機可以通過配置文件來聲明,同時redis中可以通過bgrewriteaof指令人工幹預。

redis-cli -h ip -p port bgrewriteaof

因為rewrite操作/aof記錄同步/snapshot都消耗磁盤IO,redis采取了“schedule”策略:無論是“人工幹預”還是系統觸發,snapshot和rewrite需要逐個被執行。

AOF rewrite過程並不阻塞客戶端請求。系統會開啟一個子進程來完成。

三.總結:

AOF和RDB各有優缺點,這是有它們各自的特點所決定:

1) AOF更加安全,可以將數據更加及時的同步到文件中,但是AOF需要較多的磁盤IO開支,AOF文件尺寸較大,文件內容恢復數度相對較慢。

*2) snapshot,安全性較差,它是“正常時期”數據備份以及master-slave數據同步的最佳手段,文件尺寸較小,恢復數度較快

  • 如果既配置了RDB,又配置了AOF,則在進行數據持久化的時候,都會進行,但是在根據文件恢復數據的時候,以AOF文件為準,RDB文件作廢
    • 需要註意:數據的恢復是阻塞操作(此間所到來的任何客戶端讀寫請求都失效)
  • bgsave和bgrewriteaof(後臺aof重寫)這兩個命令不可以同時發生
    • 如果bgsave在執行,此間到來的bgrewriteaof在bgsave執行之後,再執行
    • 如果bgrewriteaof在執行,此間到來的bgsave丟棄
  • RDB和AOF可以同時配置,但是最後還原數據庫的時候是以aof文件來還原的

--------------------- 本文來自 xiaozhu0301 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/xiaozhu0301/article/details/79021436?utm_source=copy

redis 持久化方式