背景:
最近生產環境中某個Set的Redis叢集經常出現短暫的記憶體降低現象,經過檢視日誌是因為在RDB持久化所造成的記憶體突降(日誌中:RDB: 4929 MB of memory used by copy-on-write ),其根本原理是RDB持久化的過程中,Redis藉助作業系統提供的寫時複製技術(Copy-On-Write,COW),在執行bgsave(snapshot)快照的同時,能夠處理正常的寫請求。
1.RDB持久化原理
寫時複製技術:
如果主執行緒要修改一塊資料,那麼這塊資料就會被複制一份,生成該資料的副本。然後bgsave 子程式會把這個副本寫入正在持久化的RDB檔案,而在這個過程中,主執行緒仍然可以直接修改原來的資料。另外,子程式是會複製一份和主程式一模一樣的虛擬頁表來對映記憶體,保證持久化檔案的完整性。
正因為資料會被額外的複製一份,所以會佔用額外的記憶體,當在進行RDB持久化操作的過程中,與此同時如果持續往redis中寫入的資料量越多,就會導致佔用的額外記憶體消耗越大。
那麼在此期間寫入的資料去哪了呢?
寫入的資料還是存在了記憶體當中,並沒有寫入當前的持久化檔案中,等到下次進行RDB持久化時才會把 ” 寫入的資料 ” 落盤到RDB檔案中。
bgsave:fork出的子程式開始根據父程式記憶體資料生成臨時的快照檔案,然後替換原檔案。
這裡解釋一下幾個跟 RDB 相關的引數:
- rdb_changes_since_last_save:自上次 RDB 後,Redis 資料的改動條數
- rdb_bgsave_in_progress:bgsave 是否在進行中,0 否,1 是
- rdb_last_save_time:上次 bgsave 的時間戳
- rdb_last_bgsave_status:上次 bgsave 的狀態
- rdb_last_bgsave_time_sec:上次 bgsave 的持續時間
- rdb_current_bgsave_time_sec:正在執行的 bgsave 耗時,如果沒有正在執行的,則為 -1
- rdb_last_cow_size:上次 RDB 過程中父程式與子程式相比執行了多少修改
根據 rdb_bgsave_in_progress 這一項為 0,可以判斷在執行 info Persistence 命令時,bgsave 已經執行完成了。除了通過命令的方式觸發 RDB 持久化之外,Redis 內部還有自動觸發 RDB 的機制。比如以下場景:
- 配置檔案中增加了類似 "save m n" 的配置,表示 m 秒內有 n 次修改則自動觸發 bgsave。
- 新建立 Redis 主從複製時,主節點會執行一次 bgsave 儲存 RDB 檔案到本地,然後傳送給從節點。
- 執行 shutdown 時,如果沒有開啟 AOF 則自動執行 bgsave。
- 哨兵模式發生主從切換時,會主動進行一次初始化操作,執行bgsave儲存RDB檔案到本地。
2 頻繁執行全量快照的影響
如果頻繁執行全量快照,會帶來兩方面的開銷:
- 頻繁將全量資料寫入磁碟,會給磁碟帶來很大壓力,可能出現前面的沒做完,後面的又開始了。導致惡性迴圈。
- bgsave 子程式需要通過 fork 操作從主執行緒建立出來,雖然,子程式在建立後不在會阻塞主執行緒,但是,fork這個建立過程本身會阻塞主執行緒,而且主執行緒記憶體越大,阻塞時間越長。
3 運維技巧
3.1 RDB 所在分割槽磁碟滿了怎麼辦?
當遇到 RDB 所在分割槽磁碟滿了,可以臨時修改 RDB 路徑,操作如下:
3.2 開啟 RDB 壓縮
Redis 支援對 RDB 進行壓縮,引數為 rdbcompression,設定為 yes 表示開啟(預設開啟的)。壓縮不但可以節省磁碟空間,在建立主從時,也能更快的將全量備份傳給從例項,因此建議開啟壓縮功能。
3.3 RDB 檔案損壞檢測
當發現 Reids RDB 檔案損壞時,可以使用 redis-check-rdb 進行檢測,用法如下:
RDB looks OK! 說明rdb檔案沒有錯誤。
3.4 單機多例項的 RDB 備份
有些情況,我們會在單臺伺服器上部署多個 Redis 例項,但是使用配置檔案中增加 save 的方式又怕幾個例項 RDB 時間衝突,從而影響落盤速度。這種情況,可以使用指令碼結合定時任務觸發 bgsave 進行 RDB 備份。這樣,同機器不同例項的 RDB 備份時間可以自定義錯開,防止 IO 跑滿帶來的問題。(注意一定要設定好持久化的目錄,防止多個例項共用同一目錄)
4 備份建議
那麼 Redis 究竟怎麼備份更好呢?RDB 儘管恢復會快很多,但是可靠性比 AOF 低,但是如果只使用 AOF,又會存在恢復慢的問題,因此,Redis 4.0 提出了混合使用 AOF 日誌和記憶體快照的方法。因此對於 Redis 的備份,建議如下:
- 資料不能丟失時,記憶體快照和 AOF 的混合使用是一個很好的選擇;
- 如果允許分鐘級別的資料丟失,可以只使用 RDB;
- 如果只用 AOF ,優先使用 everysec 的配置選項,因為其介於可靠性和效能之間;
當然,如果有從例項,也優先考慮在從例項上進行備份。