1. 程式人生 > 程式設計 >快速理解Redis的持久化

快速理解Redis的持久化

Redis系列文章

為什麼需要持久化

很簡單,因為 Redis基於記憶體的。資料如果不進行持久化,當伺服器重啟或者宕機的時候資料是無法恢復的,所以為了保證資料的安全性,我們需要將記憶體中的資料持久化到磁碟中。

Redis的持久化

Redis 提供了兩種持久化的方式,分別是 RDBAOF

  • RDB : Redis的預設持久化方式,是基於 快照 來實現的,當符合一定條件的時候 Redis 會自動將記憶體中的資料進行快照然後持久化到磁碟中。
  • AOF : Redis預設沒有開啟 AOF 持久化,需要在配置檔案中設定 appendonly true
    開啟。它儲存的是 Redis順序指令序列

RDB方式的持久化

save 阻塞方式

Redis 中有一個命令可以觸發 RDB 持久化,但是這個操作會阻塞 Redis

這個命令是 save,我們都知道 Redis 是單執行緒的,如果持久化進行 特殊的處理 的話,那麼就會阻塞其他命令導致 Redis 短時間內不可用,如果 RDB 檔案很大,那麼刷盤操作將會數十秒,嚴重影響可用性,所以我們一般都不會使用 save 命令。

bgsave 後臺方式

bgsave 顧名思義,就是 後臺進行儲存 。當執行這條命令的時候 Redis 就會進行一些 特殊處理

什麼特殊處理呢?

首先 Redis

的主程式會呼叫 glibc 的函式 fork 產生一個子程式,此時會將檔案 持久化全部交給子程式去處理,那麼這時父程式就可以繼續處理使用者的請求了(bgsave執行之後直接會返回)。當然,在主程式進行 fork 操作的時候可能也會對使用者請求命令 產生短暫的阻塞

凡事有利必有弊,你看 bgsave 這麼好,難道沒有缺點麼? 答案是有的,在哪呢?我們先來瞭解一下 COW 機制吧。

COW

COW (copy on write),也就是 寫時複製 。我們知道 RDB 持久化需要遍歷記憶體中的資料,就像下面那張圖一樣。

因為我們需要的是在子程式產生的那一瞬間的資料(快照),如果此時因為使用者請求在主程式修改了記憶體中的資料,那麼子程式遍歷的記憶體就會被更改,這個時候就不是快照資料

了。

所以這裡就使用了產生快照的一種機制—— COW 。我們知道上面的資料段其實由很多作業系統的組合而成的。COW 其實就是在主程式需要修改記憶體中的資料的時候,首先將需要修改的資料所在的頁進行復制,然後再複製的頁面上進行修改當進行頁的複製的時候就會佔用額外的記憶體,這也是 bgsave 佔用記憶體比 save 多的原因,但是不用過分擔心,因為再儲存期間不會出現大量的使用者請求來修改資料,額外使用的記憶體也不會很多。

自動觸發RDB

Redis 中會有幾種情況下進行 RDB 的持久化,所以即使你在配置檔案中關閉了 RDBRedis 還是會進行 RDB 的持久化。

  • 滿足條件時

    Redis 的配置檔案中有這麼三條配置。

    save 900 1
    save 300 10
    save 60 10000
    複製程式碼

    這其實就是 RDB 中預設開啟的原因,它的格式是這樣的 save seconds changeTimessave 後面的第一個數字是時間,第二個數字是修改次數,這三條配置的意思就是 在900秒內進行了1次修改或者在300秒內進行了10次修改或者在60秒內進行了10000次修改 會進行 RDB 的自動持久化。

  • shutdownRedis 正常關閉的時候會進行 RDB 持久化。

  • flushall 會產生一個空的 RDB 檔案

  • 主從複製進行全量複製的時候(目前做了解就行,我在後面的文章會講到 Redis 叢集)

RDB的優點

  • RDB 檔案,其中做了些壓縮,儲存的是資料,可以快速的進行災難恢復
  • 適合做冷備。

RDB的缺點

  • 容易丟失資料,因為 RDB 需要遍歷整個記憶體中的所有資料,所以進行一次 RDB 操作是一個費事費力的操作,為了保證 Redis 的高效能,你需要儘量減少 RDB 的持久化,所以你可能會丟失一段時間的資料。

AOF方式的持久化

預設情況下 Redis 是沒有開啟 AOF 持久化的,你需要在配置檔案中進行相應的配置。

appendonly yes   # 預設為no這裡設定yes開啟
dir ./           # aof檔案目錄
appendfilename "appendonly-6379.aof" #aof檔名
複製程式碼

開啟 AOF 持久化之後,Redis 會根據 AOF 持久化策略 來進行相應的持久化操作,具體配置是在 appendfsync 配置。

# appendfsync always  每次進行修改操作就進行寫盤
appendfsync everysec  每秒進行一些寫盤
# appendfsync no      從不
複製程式碼

Redis一共提供了三個引數,一般考慮設定 everysec 每秒寫盤,這樣既能少影響效率也能減少資料丟失量。

AOF原理

AOF 日誌中存放的是對於 Redis 的操作指令,有了 AOF 日誌我們就可以使用它進行對 Redis 的重放。

比如此時我們 AOF 日誌中記錄了 set hello worldsadd userset FancisQ 這兩條命令,我們就可以對一個空的 Redis 例項進行此 AOF 檔案的重放,最終這個空的 Redis 就有了上面兩條記錄。

你可能會發現 Redis 中的這兩個持久化方式很像 MySQL 中的 bin logredo log,但是你需要注意的是 Redis 中的 AOF先執行命令再存日誌的。這和 MySQL 中的 WAL 機制截然相反。

為什麼呢? 我覺得有兩點。

  1. Redis 是弱事務的,我們不需要保證資料的強一致。在 MySQL 中我們使用了 redo log 兩階段提交 來保證了 save-crash 能力,而在 Redis 中我們顯然不需要這麼做,假設這條命令執行完之後還沒來得及寫日誌就宕機了,那就沒了,因為弱事務,我們大可不必保證資料必須存在。
  2. 為了避免錯誤指令的日誌儲存,如果先寫日誌也就意味著我們一開始沒有做相應的 邏輯處理和引數校驗 ,所以這樣會 先記錄到很多錯誤指令 ,但是我們知道 AOF 檔案是需要 瘦身 的,這些錯誤指令會給 AOF 瘦身帶來很多麻煩。

AOF重寫

上面提到的 瘦身 其實就是 AOF重寫 ,我們知道 AOF 檔案中儲存的是指令順序,當 Redis 長時間執行時會產生很多指令。

比如 set a b,set a c,set a d.....

其實上面三條就是對 key 為 a 的資料進行操作了,在 RDB 中它可能只存了 a = d ,但是因為 AOF 的指令機制,它必須存在三條,但前面的是無意義的,這樣會浪費很多空間並且給 AOF 重放帶來麻煩。

所以 Redis 會在 AOF檔案過大(符合某種條件)的時候進行自動的 AOF 重寫。對應的在配置檔案中有這樣兩條配置。

# 下面兩條需要同時滿足
# 表示當前 aof 檔案超過上次 aof檔案大小的百分之多少時會進行重寫,如果沒有重寫過則以啟動時的大小為標準
auto-aof-rewrite-percentage 100 
# 檔案大於多少的時候進行重寫
auto-aof-rewrite-min-size 64mb
複製程式碼

那麼,AOF 是如何重寫的呢?

bgrewriteaof

這是一條 AOF 重寫命令(上述的重寫過程其實就是 bgrewriteaof),和 bgsave 一樣,Redis 也是會 fork 一個子程式,讓子程式去負責 AOF 檔案的重寫。大致流程如下:

AOF的優缺點

  • 缺點: 在同等資料量的情況下,AOF 檔案的大小要比 RDB 檔案大得多,如果使用它進行記憶體狀態恢復需要花費很長時間
  • 優點: 持久化快,能減少資料丟失的量,在配置 everysec 的情況下最多隻會丟失秒級的資料。

Redis混合持久化

Redis 4.0之前,我們一般會開啟 AOF 然後再需要恢復記憶體狀態的時候棄用 AOF日誌重放 ( 如果使用RDB的話會丟失大量資料 )。但如果 Redis 例項很大,AOF 檔案也很大的時候會導致 Redis 重啟非常慢。

為瞭解決這個問題,在 Redis 4.0之後我們可以將 RDB檔案和 AOF增量日誌儲存在一起。如果這個時候我們進行記憶體狀態恢復可以先使用前面的 RDB 部分,然後再使用 RDB 持久化之後產生的增量AOF日誌 來進行記憶體狀態恢復以減少時間。

如何選擇 RDB 和 AOF

  • 一般來說如果對資料的安全性還是有一定要求的話,應該同時使用兩種持久化功能
  • 如果可以承受分鐘級別的資料丟失可以僅僅使用 RDB
  • AOF 儘量使用 everysec 配置,既能保證資料安全又能保證效能效率。
  • RDB 下關閉 save seconds changeTimes 這個自動持久化機制,或者合理使用引數。

思維圖譜

感謝閱讀。 給你們分享一下思維導圖 (#^.^#)