Java學習筆記:Redis的持久化

Java正道的光發表於2020-09-23

fork()系統呼叫:

這裡很突兀的來個fork()系統呼叫原因是應為:Redis的單執行緒的,那如果主執行緒去做這種耗時的IO同步操作時,Redis整體的效能會被拖垮的。

fork()它是一個系統呼叫,一般用它來建立一個和當前程式一模一樣的子程式。當在程式中呼叫它時,系統為新的程式分配儲存、資源,將原程式中的值也複製給他。

fork()函式呼叫一次會返回兩次,在父程式得到的返回值是子程式的pid,在子程式中得到的是0,出錯則返回負數。

Redis的實現是通過fork()系統呼叫建立一個子程式。 由這個子程式去負責執行這些耗時的IO操作,父子程式會共享記憶體,然後被共享的這塊記憶體不可寫,新的資料寫入到新的記憶體檔案中

RDB:

寫RDB檔案是Redis的一種持久化方式。在指定的時間間隔內將記憶體中的資料寫入到磁碟,RDB檔案是一個緊湊的二進位制檔案,每一個檔案都代表了某一個時刻(執行fork的時刻)Redis完整的資料快照,恢復資料時,將快照檔案讀入記憶體即可。

RDB持久化的詳細過程:

Redis會通過系統呼叫fork()出一個子程式,父子程式是會共享記憶體的,父程式和子程式共享的這塊記憶體就是在執行fork操作那個時刻的記憶體快照。由linux的copy on write機制將父子程式共享的這塊記憶體標記為只讀狀態。

此時對子程式來說,它的任務就是將這塊只讀記憶體中的資料儲存成RDB檔案。

對父程式來說它是有可能收到寫命令的,當父程式嘗試往這個加了只讀狀態的記憶體地址寫入資料時,就會觸發保護異常,執行linux的 copy on write,也就是將原來記憶體對應的資料頁複製出來一份後,然後對這個副本進行修改。

這裡就會出現一個丟資料的概念:你想,fork出來的子程式將要儲存的資料是執行fork系統呼叫那個時刻的記憶體中的資料,很快這個記憶體就被標記為只讀了,後續的增量資料沒有寫入到這個只讀記憶體中,那就算是RDB成功生成了,然後好巧,Redis掛了,這些增量的資料就會丟(所以得使用AOF輔助)

第二種RDB出現資料的丟失的情況是:RDB過程中,直接失敗了,檔案都沒生成,不光是增量資料,原來的資料都丟了。

RDB相關配置如下

# 把下面的註釋開啟就會禁用掉RDB的持久化策略
# save ""

# 快照相關,指的是在規定的時間內執行了多少次操作才會持久化到檔案
save 900 1 # 900秒內1次
save 300 10 # 300秒內10次
save 60 10000 # 60秒內1萬次

# 持久化出錯了,是否讓redis繼續工作
stop-writes-on-bgsave-error yes

# 是否壓縮RBD檔案(redis會採用LZF壓縮演算法進行壓縮)需要消耗CPU資源
rdbcompression yes

# 儲存rbc檔案時是否檢驗rbd檔案格式
# 使用CRC64演算法進行資料校驗,但是這樣會增加大約 10%的效能消耗
rdbchecksum yes

# dump DB的檔名
dbfilename dump.rdb

# rdb檔案的持久化目錄(當前目錄)
dir ./

 觸發儲存RDB檔案4種情況

  1. 手動執行save命令、bgsave
  2. 滿足配置檔案中配置的save相關配置項時,自動觸發
  3. 手動執行flushall
  4. 關閉redisshutdown

如何讓redis載入rdb檔案?

只需要將rdb檔案放在redis的啟動目錄下,redis其中時會自動載入它

RDB模式的優缺點:

優點:RDB過程中,由子程式代替主程式進行備份的IO操作。保證了主程式仍然提供高效能的服務。適合大規模的資料備份恢復過程。

缺點:

  1. 預設情況下,它是每隔一段時間進行一次資料備份,所以一旦出現最後一次持久化的資料丟失,將丟失大規模的資料。
  2. fork()子程式時會佔用一定的記憶體空間,如果在fork()子程式的過程中,父程式夯住了,那也就是redis卡住了,不能對外提供服務。所以不要讓生成RDB檔案的時間間隔太長,不然每次生成的RDB檔案過大對Redis本身也是有影響的。

AOF:

AOF是什麼?

Append Only File,他也是Redis的持久化策略。即將所有的寫命令都以日誌的方式追加記錄下來(只追加,不修改),恢復的時候將這個檔案中的命令讀出來回放。

當我們執行 flushall 命令,清空了redis在記憶體中的資料,appendonly.aof 同樣會記錄下這條命令,所以,我們想恢復資料的話,需要去除 appendonly.aof 裡面的 flushall 命令

AOF相關的配置

# 預設不開啟aof
appendonly no

# aof檔名
appendfilename "appendonly.aof"

# redis通過fsync()呼叫告訴作業系統實際在磁碟上寫入資料
# aof檔案落盤的策略
# appendfsync always 每次發生資料變更,立刻記錄到磁碟,但是導致redis吞吐量降低
# appendfsync everysec 可能會丟失1秒的資料
# appendfsync no
appendfsync everysec

# 當時用bfwriteaof時,fork一個子程式寫aof檔案,就算aof檔案很大,也不會阻塞主程式
# 意外情況:但是當主程式、子程式同時寫aof檔案時,可能會出現由於子程式大量的IO操作阻塞主程式
# 當出現這種意外情況時:設定這個引數為no,可以保證資料不會丟失,但是得容忍主程式被阻塞
# 當出現這種意外情況時:設定這個引數為yes,主程式不會被阻塞主,但是不保證資料安全性
# 綜上:如果應用無法忍受延遲:設定為yes。無法忍受資料丟失:設定為no
no-appendfsync-on-rewrite no

# 在當前aof檔案的體積超過上次aof檔案的體積的100%時,寫新檔案
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb # 最開始的aof檔案體積至少達到60M時才重寫

# 回放aof檔案時,如果最後一條命令存在問題,是否允許忽略
aof-load-truncated yes

# 是否允許AOF和RDB這兩種持久化方式並存
aof-use-rdb-preamble yes

當aof檔案出錯怎麼辦?

redis為我們提供了修復aof檔案的工具

[root@instance-lynj0v9k-19 bin]# redis-check-aof  --fix appendonly.aof

 

aof模式的優缺點 優點:

  1. aof是用追加的形式寫,沒有隨機磁碟IO那樣的定址開銷,效能還是比較高的。
  2. aof可以更好的保護資料不丟失或者儘可能的少丟失:設定讓redis每秒同步一次資料,即使redis當機了,最多也就丟失1秒的資料。
  3. 即使aof真的體積很大,也可以設定後臺重寫,不影響客戶端的重寫。
  4. aof適合做災難性的誤刪除緊急恢復:比如不小心執行了flushall,然後可以在發生rewrite之前 快速備份下aof檔案,去掉末尾的 flushall,通過恢復機制恢復資料

缺點:使用aof一直追加寫,導致aof的體積遠大於RDB檔案的體積,恢復資料、修復的速度要比rdb慢很多。

aof的重寫

AOF採取的是檔案追加的方式,檔案的體積越來越大,為了優化這種現象,增加了重寫機制,當aof檔案的體積到達我們在上面的配置檔案上的闋值時,就會觸發重寫策略,只保留和資料恢復相關的命令

手動觸發重寫

# redis會fork出一條新的程式
# 同樣是先複製到一份新的臨時檔案,最後再rename,遍歷每一條語句,記錄下有set的語句
bgrewriteaof

RDB和AOF的選擇:

  • 如果我們的redis只是簡單的作為快取,那兩者都不要也沒事
  • 如果資料需要持久化,那不要僅僅使用RDB,因為一旦發生故障,你會丟失很多資料
  • 同時開啟兩者: 在這種情況下,redis優先載入的是aof,因為它的資料很可能比rdb更全,但是並不建議只是用aof,因為aof不是那麼的安全,很可能存在潛在的bug

推薦:

  • 建議在從機slave上只備份rdb檔案,而且只要15分鐘備份一次就夠了。
  • 如果啟動了aof,我們儘量減少rewrite的頻率,基礎大小設定為5G完全可以,起步也要3G。
  • 如果我們不選擇aof, 而是選擇了主從複製的架構實現高可用同樣可以,能省掉一大筆IO操作,但是意外發生的話,會丟失十幾分鐘的資料。

相關文章