Redis學習 RDB和AOF兩種持久化介紹以及實現

人生長恨水發表於2019-04-08

以下筆記以及實驗皆出自於 中華石杉大佬的視訊教學,我跟著做了實驗,並且把課上的筆記整理了一下。

更多閱讀

redis持久化對於災難恢復的意義

redis有一個持久化的功能,在很多的視訊和資料中都有對持久化的介紹,那麼持久化對於什麼樣場景有著重大的意義呢?

故障發生的時候會怎麼樣

在實際的情況中有著這樣的情況,redis突然掛掉了,程式死了,或者所在的機器沒了,遇到了災難性的故障,因為redis的資料存在記憶體中

這時候記憶體中的資料就都沒有了,很重要的快取資料等等,redis會重啟,重啟之後要費很大的勁去恢復,如果單單把資料放到記憶體中,

是沒有任何辦法應對一些災難性的故障的,所以redis中的持久化是很重要的。再通過定期備份資料,是可以恢復一部分資料的。

意義

在企業級的redis叢集架構中,持久化的主要意義就是做災難恢復,資料恢復。

redis中RDB和AOF兩種持久化的介紹

RDB持久化機制,對redis中的資料執行週期性的持久化儲存,而且是完整的資料儲存。 意思是假如每隔一個小時就儲存一份現在redis記憶體中的全部資料。

圖解

AOF機制對每條寫入命令作為日誌,以append-only的模式寫入一個日誌檔案中,在redis重啟的時候,可以通過回放AOF日誌中的寫入指令來重新構建整個資料集

AOF的機制只對一個AOF日誌檔案做寫入(僅追加 append-only)操作,在liunx系統中,中間會經過os cache然後才會寫入磁碟中,AOF檔案存放的是redis的寫入指令

假如記憶體中的資料最多存放1G,那麼AOF檔案的大小也將會被控制在1G範圍之內,當記憶體中的資料超過1G的時候,AOF會利用一種快取淘汰演算法(LRU) 將最不常用的資料清除,而AOF的rewrite操作會基於redis清除後的記憶體中的資料重新構造一個更小的AOF檔案,然後將舊的檔案刪除。

如果redis掛了,伺服器上的記憶體和磁碟上的資料都丟了,可以從雲服務上拷貝回來之前的資料,放到指定的目錄中,然後重新啟動redis,redis就會自動根據持久化資料檔案中的資料,去恢復記憶體中的資料,繼續對外提供服務 如果同時使用RDB和AOF兩種持久化機制,那麼在redis重啟的時候,會使用AOF來重新構建資料,因為AOF中的資料更加完整

RDB和AOF的介紹

RDB持久化機制的優點

  • RDB會生成多個資料檔案,每個資料檔案都代表了某一個時刻中redis的資料,這種多個資料檔案的方式,非常適合做冷備,可以將這種完整的資料檔案傳送到一些遠端的安全儲存上去,比如說Amazon的S3雲服務上去, 在國內可以是阿里雲的ODPS分散式儲存上,以預定好的備份策略來定期備份redis中的資料

  • RDB對redis對外提供的讀寫服務,影響非常小,可以讓redis保持高效能,因為redis主程式只需要fork一個子程式,讓子程式執行磁碟IO操作來進行RDB持久化即可

  • 相對於AOF持久化機制來說,直接基於RDB資料檔案來重啟和恢復redis程式,更加快速

RDB持久化機制的缺點

  • 如果想要在redis故障時,儘可能少的丟失資料,那麼RDB沒有AOF好。一般來說,RDB資料快照檔案,都是每隔5分鐘,或者更長時間生成一次,這個時候就得接受一旦redis程式當機,那麼會丟失最近5分鐘的資料

  • RDB每次在fork子程式來執行RDB快照資料檔案生成的時候,如果資料檔案特別大,可能會導致對客戶端提供的服務暫停數毫秒,或者甚至數秒,所以一般不要間隔太長時間進行持久化操作。

AOF持久化機制的優點

  • AOF可以更好的保護資料不丟失,一般AOF會每隔1秒,通過一個後臺執行緒執行一次fsync操作,最多丟失1秒鐘的資料

  • AOF日誌檔案以append-only模式寫入,所以沒有任何磁碟定址的開銷,寫入效能非常高,而且檔案不容易破損,即使檔案尾部破損,也很容易修復

  • AOF日誌檔案即使過大的時候,出現後臺重寫操作,也不會影響客戶端的讀寫。因為在rewrite log的時候,會對其中的指導進行壓縮,建立出一份需要恢復資料的最小日誌出來。再建立新日誌檔案的時候,老的日誌檔案還是照常寫入。 當新的merge後的日誌檔案ready的時候,再交換新老日誌檔案即可。

  • AOF日誌檔案的命令通過非常可讀的方式進行記錄,這個特性非常適合做災難性的誤刪除的緊急恢復。比如某人不小心用flushall命令清空了所有資料,只要這個時候後臺rewrite還沒有發生,那麼就可以立即拷貝AOF檔案, 將最後一條flushall命令給刪了,然後再將該AOF檔案放回去,就可以通過恢復機制,自動恢復所有資料

AOF持久化機制的缺點

  • 對於同一份資料來說,AOF日誌檔案通常比RDB資料快照檔案更大

  • AOF開啟後,支援的寫QPS會比RDB支援的寫QPS低,因為AOF一般會配置成每秒fsync一次日誌檔案,當然,每秒一次fsync,效能也還是很高的

  • 以前AOF發生過bug,就是通過AOF記錄的日誌,進行資料恢復的時候,沒有恢復一模一樣的資料出來。所以說,類似AOF這種較為複雜的基於命令日誌/merge/回放的方式,比基於RDB每次持久化一份完整的資料快照檔案的方式,更加脆弱一些,容易有bug。 不過AOF就是為了避免rewrite過程導致的bug,因此每次rewrite並不是基於舊的指令日誌進行merge的,而是基於當時記憶體中的資料進行指令的重新構建,這樣健壯性會好很多。

RDB和AOF到底該如何選擇

  • 不要僅僅使用RDB,因為那樣會導致你丟失很多資料

  • 也不要僅僅使用AOF,因為那樣有兩個問題,第一,你通過AOF做冷備,沒有RDB做冷備,來的恢復速度更快; 第二,RDB每次簡單粗暴生成資料快照,更加健壯,可以避免AOF這種複雜的備份和恢復機制的bug

  • 綜合使用AOF和RDB兩種持久化機制,用AOF來保證資料不丟失,作為資料恢復的第一選擇; 用RDB來做不同程度的冷備,在AOF檔案都丟失或損壞不可用的時候,還可以使用RDB來進行快速的資料恢復

實現redis持久化

首先實現Redis中的兩種持久化,並且做一些實驗來測試一下兩種持久化是否生效。 AOF持久化,預設是關閉的,預設是開啟RDB持久化

配置檔案路徑以及後面的所有路徑都是以我自己的為主。自己的配置檔案在哪裡自己就在哪去配就可以了,下面的所有的路徑都按自己的路徑走,不要盲目按照我的來,除非你按照我的環境來搭建的。

RDB持久化

如何配置RDB持久化機制

redis.conf檔案,也就是/etc/redis/6379.conf,去配置持久化,

找到以下類似配置項,在vi中使用/save即可查詢指定的save字元,:noh取消高亮

save 60 1000

每隔60s,如果有超過1000個key發生了變更,那麼就生成一個新的dump.rdb檔案,就是當前redis記憶體中完整的資料快照, 這個操作也被稱之為snapshotting(快照)

也可以手動呼叫save或者bgsave命令,同步或非同步執行rdb快照生成

save可以設定多個,就是多個snapshotting檢查點,每到一個檢查點,就會去check一下,是否有指定的key數量發生了變更,如果有, 就生成一個新的dump.rdb檔案

RDB持久化機制的工作流程

  • redis根據配置自己嘗試去生成rdb快照檔案
  • fork一個子程式出來
  • 子程式嘗試將資料dump到臨時的rdb快照檔案中
  • 完成rdb快照檔案的生成之後,就替換之前的舊的快照檔案

dump.rdb,每次生成一個新的快照,都會覆蓋之前的老快照

基於RDB持久化機制的資料恢復實驗

  1. 實驗1,首先使用命令 redis-cli 進入redis命令列,儲存幾條資料並且重啟redis,看剛剛的資料是否還能查詢的到。
$ redis-cli
$ set k1 v1
$ set k2 v2
$ exit
$ redis-cli SHUTDOWN
# 進入/etc/init.d檔案下,使用redis_6379指令碼重啟redis
$ cd /etc/init.d
$ ./redis_6379 start
$ redis-cli
$ get k1
"v1"
$ get k2
"v2"
複製程式碼

執行以上命令後,可以看到如果正常退出的redis,資料還在,這是因為通過redis-cli SHUTDOWN這種方式去停掉redis,其實是一種安全退出的模式, redis在退出的時候會將記憶體中的資料立即生成一份完整的rdb快照

  1. 實驗2,再次在redis中存入幾條資料,這次我們使用kill -9 暴力殺死redis的程式並且刪除/var/run/redis_6379.pid檔案後,看是否還能查詢到資料
$ set k3 v3
$ set k4 v4
$ exit
# 通過ps -ef | grep redis 檢視redis的程式
$ ps -ef | grep redis
$ kill -9 62789
# 一定要注意-rf不會提示你刪除,確認好檔案後在進行刪除
$ rm -rf /var/run/redis_6379.pid 
# 再次重啟redis
$ cd /etc/init.d
$ ./redis_6379 start
$ redis-cli
$ get k3
(nil)
$ get k4
(nil)
複製程式碼

這回儲存的新資料都沒有了,是因為還沒有到配置的檢查點的時候,出現了意外,這時候就無法儲存rdb檔案了,也就丟失了檔案,所以這也是RDB的一個缺點。

  1. 實驗3,設定間隔短的儲存點,重啟redis程式,並且重複實驗2的步驟
$ vi /etc/redis/6379.conf
# 找到save 60 1000類似的設定後新增以下內容儲存退出
save 5 1
# 表明每5秒檢查一次,只有有一條插入的資料就生成快照檔案rdb,即儲存了資料。
$ redis-cli SHUTDOWN
$ /etc/init.d/redis_6379 start
# 重複實驗2的步驟,插入新的資料
複製程式碼

實驗3表明RDB的持久化通過我們手動設定,確實儲存了記憶體中的資料,做到了持久化,但是配置中不建議配置間隔這麼短的檢查點

AOF持久化

AOF持久化,預設是關閉的,預設是開啟RDB持久化

如何配置AOF的持久化

在配置檔案中找到 appendonly no,設定為 yes 可以開啟AOF持久化機制,在生產環境裡面,一般來說AOF都是要開啟的, 除非你說隨便丟個幾分鐘的資料也無所謂開啟AOF持久化機制之後,redis每次接收到一條寫命令,就會寫入日誌檔案中,當然是先寫入os cache的, 然後每隔一定時間再fsync一下,而且即使AOF和RDB都開啟了,redis重啟的時候,也是優先通過AOF進行資料恢復的,因為aof資料比較完整

AOF的fsync策略

在redis的配置檔案中找到appendfsync everysec有三種策略提供給我們使用,預設everysec,也是我們經常使用的

  • always: 每次寫入一條資料,立即將這個資料對應的寫日誌fsync到磁碟上去,效能非常非常差,吞吐量很低; 如果一定要確保說redis裡的資料一條都不丟,那就只能這樣了

  • everysec: 每秒將os cache中的資料fsync到磁碟,這個最常用的,生產環境一般都這麼配置,效能很高,QPS還是可以上萬的

  • no:僅僅redis負責將資料寫入os cache就撒手不管了,然後後面os自己會時不時有自己的策略將資料刷入磁碟,不可控了

AOF持久化的資料恢復實驗

  1. 實驗1,先僅僅開啟RDB,寫入一些資料,然後kill -9殺掉redis程式並且刪除/var/run/redis_6379.pid檔案,接著重啟redis,發現資料沒了, 因為RDB快照還沒生成,檢查點不能太短,否則資料就被儲存了,這個就不寫命令了,按照上面的RDB實驗即可。

  2. 實驗2,開啟AOF的開關,啟用AOF的持久化,重啟redis程式,寫入一些資料,觀察AOF檔案中的日誌內容,kill -9殺掉redis程式,重新啟動redis程式, 發現資料被恢復回來了,就是從AOF檔案中恢復回來的,它們其實就是先寫入os cache的,然後1秒後才fsync到磁碟中,只有fsync到磁碟中了,才是安全的, 要不然光是在os cache中,機器只要重啟,就什麼都沒了,持久化檔案我們配置了在 /var/redis/6379 中

$ vi /etc/redis/6379.conf
appendonly yes
$ redis-cli SHUTDOWN
$ /etc/init.d/redis_6379 start
$ redis-cli
$ set mykey1 k1
$ set mykey2 k2
$ exit
$ ps -ef | grep redis
$ kill -9 32523
$ rm -rf /var/run/redis_6379.pid
$ /etc/init.d/redis_6379 start
$ redis-cli
$ get mykey1
"k1"
$ get mykey2
"k2" 
複製程式碼

實驗證明了,在我們開啟了AOF後,可以在意外發生的時候正常的儲存我們的資料,而RDB因為沒有到達檢查點,並沒有儲存我們的資料, 所以生產環境中我們有必要把RDB和AOF兩種持久化都開啟

AOF rewrite操作

redis中的資料其實有限的,很多資料可能會自動過期,可能會被使用者刪除,可能會被redis用快取清除的演算法清理掉,redis中的資料會不斷淘汰掉舊的, 就一部分常用的資料會被自動保留在redis記憶體中

所以可能很多之前的已經被清理掉的資料,對應的寫日誌還停留在AOF中,AOF日誌檔案就一個,會不斷的膨脹,到很大很大

所以AOF會自動在後臺每隔一定時間做rewrite操作,比如日誌裡已經存放了針對100w資料的寫日誌了; redis記憶體只剩下10萬; 基於記憶體中當前的10萬資料構建一套最新的日誌,到AOF中; 覆蓋之前的老日誌; 確保AOF日誌檔案不會過大,保持跟redis記憶體資料量一致

redis 2.4之前,還需要手動,開發一些指令碼,crontab,通過BGREWRITEAOF命令去執行AOF rewrite,但是redis 2.4之後,會自動進行rewrite操作

在redis的配置檔案中我們可以配置rewrite的策略

  • auto-aof-rewrite-percentage 100,這個屬性代表著百分比,增長的百分比到達100%,就是一倍,基於上一次rewrite後檔案的大小
  • auto-aof-rewrite-min-size 64mb,這個屬性代表增長後的總大小要大於64mb

舉例

比如說上一次AOF rewrite之後,是128mb 然後就會接著128mb繼續寫AOF的日誌,如果發現增長的比例,超過了之前的100%,256mb,就可能會去觸發一次rewrite 但是此時還要去跟min-size,64mb去比較,256mb > 64mb,才會去觸發rewrite

rewrite工作流程

  • redis fork一個子程式
  • 子程式基於當前記憶體中的資料,構建日誌,開始往一個新的臨時的AOF檔案中寫入日誌
  • redis主程式,接收到client新的寫操作之後,在記憶體中寫入日誌,同時新的日誌也繼續寫入舊的AOF檔案
  • 子程式寫完新的日誌檔案之後,redis主程式將記憶體中的新日誌再次追加到新的AOF檔案中
  • 用新的日誌檔案替換掉舊的日誌檔案

AOF rewrite過程詳解

AOF的破損檔案的修復

如果redis在append資料到AOF檔案時,機器當機了,可能會導致AOF檔案破損

用redis-check-aof --fix命令來修復破損的AOF檔案,這個命令存在於redis的安裝包下src檔案中

這個破損檔案的修復就是把檔案中最後面不完整命令給刪除掉,所以修復也會導致部分資料丟失,但是總好過所有資料全部丟失。

AOF和RDB同時工作

  • 如果RDB在執行snapshotting操作,那麼redis不會執行AOF rewrite; 如果redis再執行AOF rewrite,那麼就不會執行RDB snapshotting
  • 如果RDB在執行snapshotting,此時使用者執行BGREWRITEAOF命令,那麼等RDB快照生成之後,才會去執行AOF rewrite
  • 同時有RDB snapshot檔案和AOF日誌檔案,那麼redis重啟的時候,會優先使用AOF進行資料恢復,因為其中的日誌更完整
  1. 實驗3,
  • 在有rdb的dump和aof的appendonly的同時,rdb裡也有部分資料,aof裡也有部分資料,這個時候其實會發現,rdb的資料不會恢復到記憶體中
  • 我們模擬讓aof破損,手動刪除aof檔案後面的指令變成不完整,然後fix,有一條資料會被fix刪除
  • 再次用fix修復後的aof檔案去重啟redis,發現資料只剩下一條了

為什麼rdb不會恢復到記憶體中呢,這是因為redis會優先使用aof檔案來恢復資料,所以在有aof檔案的情況下,rdb檔案是不會被恢復的

總結

總體來說redis在我們的專案中作為快取是非常重要的,為我們的mysql等資料庫分擔了很多的壓力,而快取中的資料又是重中之重,所以redis中的持久化對我們來講就是必須要會的一點知識

這章節講述了以下幾點

  • 持久化的意義?

  • 對於出現災難性的意外如何應對?

採用持久化和週期性備份來應對災難性的出現

  • 兩種持久化的優劣對比

通過上面的兩種的持久化方式來看,資料恢復完全依賴於底層的磁碟的持久化,如果rdb和aof上都沒有資料,或者這兩個檔案都丟了,那麼就真的沒有了 所以在有條件的情況下,需要對適合做冷備的rdb檔案週期性的備份也算是做最後的防線吧

相關文章