面試中經常被問到的 Redis 持久化與恢復,該如何解決

面試中經常被問到的 Redis 持久化與恢復,該如何解決

一、前言

本文主要講了 Redis 的持久化相關功能,持久化一直是影響 Redis 性能的高發地,也是面試中經常被問到的。包括 RDB 相關的特定和優缺點,AOF 的優缺點,事實上,由於 RDB 的數據實時性問題,目前用 AOF 比較多了。而持久化恢復也是優先 AOF。

RDB 是舊的模式,現在基本上都使用 AOF。當然,今天兩個都會一起聊聊。

二、 RDB

RDB 流程圖:

面試中經常被問到的 Redis 持久化與恢復,該如何解決

RDB 特點:

1、RDB 是一種快照模式,即——保存的是 key value 數據內容。

2、RDB 有 2 種持久方式,同步 save 模式和異步 bgsave 模式。由於 save 是同步的,所以可以保證數據一致性,而 bgsave 則不能。

3、save 可以在客戶端顯式觸發,也可以在 shutdown 時自動觸發;bgsave 可以在客戶端顯式觸發,也可以通過配置由定時任務觸發,也可以在 slave 節點觸發。

4、save 導致 redis 同步阻塞,基本已經廢棄。bgsave 則不會導致阻塞,但也有缺點:在 fork 時,需要增加內存服務器開銷,因為當內存不夠時,將使用虛擬內存,導致阻塞 Redis 運行。所以,需要保證空閒內存足夠。

5、默認執行 shutdown 時,如果沒有開啟 AOF,則自動執行 bgsave。

6、每次的 RDB 文件都是替換的。

關於優化:

Redis 會壓縮 RDB 文件,使用 LZF 算法,讓最終的 RDB 文件遠小於內存大小,默認開啟。但會消耗 CPU。

RDB 缺點:

1、無法秒級持久化。

2、老版本 Redis 無法兼容新版本 RDB。

RDB 優點:

1、文件緊湊,適合備份,全量複製場景。例如每 6 小時執行 bgsave,保存到文件系統之類的。

2、Redis 加載 RDB 恢復數據遠遠快於 AOF。

三、 AOF

由於 RDB 的數據實時性問題,AOF(append only file) 是目前 Redis 持久化的主流方式。

AOF 特點:

1、默認文件名是 appendonly.aof。和 RDB 一樣,保存在配置中 dir 目錄下。

2、AOF 相比較於 RDB,每次都會保存寫命令,數據實時性更高。

3、AOF 由於每次都會記錄寫命令,文件會很大,因此需要進行優化,稱之為“重寫機制”(下面詳細說)。

4、AOF 每次保存的寫命令都放在一個緩衝區,根據不同的策略(下面詳細說)同步到磁盤。

“重寫機制” 細節:

1、fork 子進程(類似 bgsave)

2、主進程會寫到2個緩衝區,一個是原有的 “AOF 緩存區”,一個是專門為子進程準備的 “AOF 重寫緩衝區”;

3、子進程寫到到新的 AOF 文件中,批量的,默認 32m;寫完後通知主進程。

4、主進程把“AOF 重寫緩衝區”的數據寫到新 AOF 文件中。

5、將新的 AOF 文件替換老文件。

重寫流程圖:

面試中經常被問到的 Redis 持久化與恢復,該如何解決

緩衝區同步策略,由參數 appendfsync 控制,一共3種:

1、always:調用系統 fsync 函數,直到同步到硬盤返回; 嚴重影響redis性能。

2、everysec:先調用 OS write 函數, 寫到緩衝區,然後 redis 每秒執行一次 OS fsync 函數。 推薦使用這種方式。

3、no: 只執行 write OS 函數,具體同步硬盤策略由 OS 決定; 不推薦,數據不安全,容易丟失數據。

四、持久化恢復

AOF 和 RDB 文件都可以用於服務器重啟時的數據恢復,具體流程如下圖:

面試中經常被問到的 Redis 持久化與恢復,該如何解決

從圖中可以看出優先加載 AOF,當沒有 AOF 時才加載 RDB。當 AOF 或者 RDB 存在錯誤,則加載失敗。

五、問題排查和性能優化

Redis 持久化是影響 Redis 性能的高發地,也是面試中常問的問題。

1. fork 操作

當 Redis 做 RDB 或者 AOF 重寫時,必然要進行 fork 操作,對於 OS 來說,fork 都是一個重量級操作。而且,fork 還會拷貝一些數據,雖然不會拷貝主進程所有的物理空間,但會複製主進程的空間內存頁表。對於 10GB 的 Redis 進程,需要複製大約 20MB 的內存頁表,因此 fork 操作耗時跟進程總內存量息息相關,再加上,如果使用虛擬化技術,例如 Xen 虛擬機,fork 會更加耗時。

一個正常的 fork 耗時大概在 20毫秒左右。為什麼呢,假設一個 Redis 實例的 OPS 在 5 萬以上,如果 fork 操作耗時在秒級,那麼僵拖慢幾萬條命令的執行,對生產環境影響明顯。

我們可以在 Info stats 統計中查詢 latestforkusec 指標獲取最近一次 fork 操作耗時,單位微秒。

如何優化:

1、優先使用物理機或者高效支持 fork 的虛擬化技術,避免使用 Xen。

2、控制 redis 實例最大內存,儘量控制在 10GB 以內。

3、合理配置 Linux 內存分配策略,避免內存不足導致 fork 失敗。

4、降低 fork 的頻率,如適度放寬 AOF 自動觸發時機,避免不必要的全量複製。

2. 子進程開銷

fork 完畢之後,會創建子進程,子進程負責 RDB 或者 AOF 重寫,這部分過程主要涉及到 CPU,內存,硬盤三個地方的優化。

1、CPU 寫入文件的過程是 CPU 密集的過程,通常子進程對單核 CPU 利用率接近 90%。 如何優化呢?既然是 CPU 密集型操作,就不要綁定單核 CPU,因為這樣會和父 CPU 進行競爭。同時,不要和其他 CPU 密集型服務不是在一個機器上。如果部署了多個 Redis 實例,盡力保證統一時刻只有一個子進程執行重寫工作。

2、內存 子進程通過 fork 操作產生,佔用內存大小等同於父進程,理論上需要兩倍的內存完成持久化操作,但 Linux 有 copy on write 機制,父子進程會共享相同的物理內存頁,當父進程處理寫操作時,會把要修改的頁創建對應的副本,而子進程在 fork 操作過程中,共享整個父進程內存快照。 即——如果重寫過程中存在內存修改操作,父進程負責創建所修改內存頁的副本。這裡就是內存消耗的地方。 如何優化呢?儘量保證同一時刻只有一個子進程在工作;避免大量寫入時做重寫操作。

3、硬盤 硬盤開銷分析:子進程主要職責是將 RDB 或者 AOF 文件寫入硬盤進行持久化,勢必對硬盤造成壓力,可通過工具例如 iostat,iotop 等,分析硬盤負載情況。

如何優化:

1、不要和其他高硬盤負載的服務放在一臺機器上,例如 MQ,存儲。

2、AOF 重寫時會消耗大量硬盤 IO,可以開啟配置 no-appendfsync-on-rewrite,默認關閉。表示在 AOF 重寫期間不做 fsync 操作。

3、當開啟 AOF 的 Redis 在高併發場景下,如果使用普通機械硬盤,每秒的寫速率是 100MB左右,這時,Redis 的性能瓶頸在硬盤上,建議使用 SSD。

4、對於單機配置多個 Redis 實例的情況,可以配置不同實例分盤存儲 AOF 文件,分攤硬盤壓力。

3. AOF 追加阻塞

當開啟 AOF 持久化時,常用的同步硬盤的策略是“每秒同步” everysec,用於平衡性能和數據安全性,對於這種方式,redis 使用另一條線程每秒執行 fsync 同步硬盤,當系統資源繁忙時,將造成 Redis 主線程阻塞。

流程圖如下:

面試中經常被問到的 Redis 持久化與恢復,該如何解決

通過上圖可以發現:everysec 配置最多可能丟失 2 秒數據,不是 1 秒;如果系統 fsync 緩慢,將會導致 Redis 主線程阻塞影響效率。

問題定位:

1、發生 AOF 阻塞時,會輸入日誌。用於記錄 AOF fsync 阻塞導致拖慢 Redis 服務的行為。

2、每當 AOF 追加阻塞事件發生時,在 info Persistence 統計中,aofdelayedfsync 指標會累加,查看這個指標方便定位 AOF 阻塞問題。

3、AOF 同步最多運行 2 秒的延遲,當延遲發生時說明硬盤存在性能問題,可通過監控工具 iotop 查看,定位消耗 IO 的進程。

4. 單機多實例部署

Redis 單線程架構無法充分利用多核CPU,通常的做法是一臺機器上部署多個實例,當多個實例開啟 AOF 後,彼此之間就會產生CPU 和 IO 的競爭。

如何解決這個問題呢?

讓所有實例的 AOF 串行執行。

我們通過 info Persistence 中關於 AOF 的信息寫出 Shell 腳本,然後串行執行實例的 AOF 持久化。

整個過程如圖:

面試中經常被問到的 Redis 持久化與恢復,該如何解決

通過不斷判斷 AOF 的狀態,手動執行 AOF 重寫,保證 AOF 不會存在競爭。具體的 Shell 編寫以及 info 信息判斷,可以查看下圖:

面試中經常被問到的 Redis 持久化與恢復,該如何解決

六、總結

本文主要講了 Redis 的持久化相關功能,持久化一直是影響 Redis 性能的高發地,也是面試中經常被問到的。包括 RDB 相關的特定和優缺點,AOF 的優缺點,事實上,由於 RDB 的數據實時性問題,目前用 AOF 比較多了。而持久化恢復也是優先 AOF。

關於持久化的問題排查,就很麻煩了,但無非幾個方面,fork 耗時,子進程的 CPU,內存,硬盤開銷,AOF 的同步阻塞,單機多實例部署。

這些優化,可以通過前面寫的分析進行排查。

寫在最後:

歡迎留言討論,如需Java方面的架構資料,我這裡剛好有一份,怎麼領取→→→關注+轉發 然後私信“架構資料” 即可領取

點關注,不迷路,持續更新!!!


分享到:


相關文章: