Redis原理二:持久化

weixin_34138377發表於2018-09-04

Redis 的資料全部在記憶體裡,如果突然當機,資料就會全部丟失,因此必須有一種機制來保證 Redis 的資料不會因為故障而丟失,這種機制就是 Redis 的持久化機制。

初始RDB和AOF

Redis 的持久化機制有兩種,第一種是快照(RDB),第二種是 AOF 日誌。快照是一次全量備份,是二進位制序列化形式,在儲存上非常緊湊,非常適合做冷備,恢復速度特別快。AOF 日誌是連續的增量備份,記錄的是指令記錄文字,長期執行勢必會變的非常龐大(b畢竟是文字而不是二進位制),因此要定期的rewrite,去掉舊的指令。AOF非常適合來保證資料不丟失(儘可能的不丟失)。


7856459-8b53a4e088ff836a.png

RDB

我們知道 Redis 是單執行緒程式,這個執行緒不僅要負責多個客戶端套接字的併發讀寫操作,還要負責記憶體資料結構的邏輯讀寫。

如果這種單執行緒除了負責上述操作之外,還要進行檔案IO操作,這些檔案IO操作會嚴重拖垮伺服器請求的效能。

那怎麼辦呢?
redis使用作業系統的多程式COW機制來實現快照持久化。

COW

Redis 在持久化時會呼叫 glibc 的函式fork產生一個子程式,快照持久化完全交給子程式來處理,父程式繼續處理客戶端請求。子程式剛剛產生時,它和父程式共享記憶體裡面的程式碼段和資料段。這是 Linux 作業系統的機制,為了節約記憶體資源,所以儘可能讓它們共享起來。在程式分離的一瞬間,記憶體的增長几乎沒有明顯變化。
子程式做資料持久化,它不會修改現有的記憶體資料結構,它只是對資料結構進行遍歷讀取,然後序列化寫到磁碟中。但是父程式不一樣,它必須持續服務客戶端請求,然後對記憶體資料結構進行不間斷的修改。

RDB工作流程

  1. redis根據配置自己嘗試去生成rdb快照檔案
  2. fork一個子程式出來
  3. 子程式嘗試將資料dump到臨時的rdb快照檔案中
  4. 完成rdb快照檔案的生成之後,就替換之前的舊的快照檔案
    dump.rdb,每次生成一個新的快照,都會覆蓋之前的老快照

AOF

AOF日誌記錄對記憶體進行修改的指令記錄,它可以通過對一個空的 Redis 例項順序執行所有的指令,也就是「重放」,來恢復 Redis 當前例項的記憶體資料結構的狀態。

Redis 會在收到客戶端修改指令後,進行引數校驗進行邏輯處理後,如果沒問題,先寫入os cache的,然後每隔一定時間再fsync一下到日誌中。

配置AOF的fsync策略,有三種策略可以選擇 :

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

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

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

AOF rewrite

記憶體畢竟是有限的,有些資料會被LRU演算法清除掉,有些資料會被定期刪除+惰性刪除機制刪除掉,總之redis會不斷淘汰資料。

所以很多記憶體中已經不存在的資料,在AOF日誌中還可能存在著對應的指令,這樣會不斷的膨脹。

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

rewrite流程

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

Redis4.0混合持久化

重啟 Redis 時,我們很少使用 rdb 來恢復記憶體狀態,因為會丟失大量資料。我們通常使用 AOF 日誌重放,但是重放 AOF 日誌效能相對 rdb 來說要慢很多,這樣在 Redis 例項很大的情況下,啟動需要花費很長的時間。

Redis 4.0 為了解決這個問題,帶來了一個新的持久化選項——混合持久化。將 rdb 檔案的內容和增量的 AOF 日誌檔案存在一起。這裡的 AOF 日誌不再是全量的日誌,而是自持久化開始到持久化結束的這段時間發生的增量 AOF 日誌,通常這部分 AOF 日誌很小。

於是在 Redis 重啟的時候,可以先載入 rdb 的內容,然後再重放增量 AOF 日誌就可以完全替代之前的 AOF 全量檔案重放,重啟效率因此大幅得到提升。

相關文章