網上雖然已經有很多類似的介紹了,但我還是自己總結歸納了一下,自認為內容和細節都是比較齊全的。
好了,下面開始回歸正文:
redis 一共有 2 種持久化方式,分別是 rdb 和 aof,下面我來詳細介紹兩種方式在各個過程所做的事情,特點等等。
rdb 持久化是 redis 預設的持久化方式。
它所生成的 rdb 檔案是乙個壓縮的二進位制檔案,通過該檔案可以還原生成 rdb 檔案時的資料庫狀態
ps:資料庫狀態是指 redis 伺服器的非空資料庫以及他們鍵值對的統稱
有兩個命令可以生成 rdb 檔案,乙個是 s**e、另乙個是 bgs**e。
兩者的區別在於:前者會阻塞 redis 伺服器程序,直到 rdb 檔案建立完畢為止。
而在伺服器程序阻塞期間,伺服器是不能處理任何命令請求的。
後者則不會阻塞伺服器程序,因為是通過 fork 乙個子程序,並讓其去建立 rdb 檔案,而伺服器程序(父程序)繼續則繼續處理命令請求。
當寫完資料庫狀態後,新 rdb 檔案就會原子地替換舊的 rdb 檔案。
此處小提問:如果在執行 bgs**e 期間,客戶端傳送 s**e、bgs**e 或 bgrewriteaof 命令給服務端,服務端會如何處理呢?
答案:在執行 bgs**e 期間,上述三個命令都不會被執行。
詳細原因:前兩個會被直接拒絕,原因是為了避免父子程序同時執行兩個 rdbs**e 呼叫,防止產生競爭條件。rdb 檔案的載入是在伺服器啟動時自動執行的,所以沒有用於載入的命令,期間阻塞主程序。而 bgrewriteaof 命令則是會被延遲到 bgs**e 命令執行之後再執行。
但如果是 bgrewriteaof 命令正在執行,此時客戶端傳送 bgs**e 命令則會被拒絕。
因為 bgrewriteaof 和 bgs**e 都是由子程序執行的,所以在操作方面沒有衝突的地方,不能同時執行的原因是效能上的考慮——並發出兩個子程序,並且這兩個子程序都會同時執行大量 io(磁碟寫入)操作
只要沒有開啟 aof 持久化功能,在啟動時檢測到有 rdb 檔案,就會自動載入。
當伺服器有開啟 aof 持久化功能時,伺服器將會優先使用 aof 檔案來還原資料庫狀態。原因是 aof 檔案的更新頻率通常比 rdb 檔案的更新頻率高。
對於 rdb 持久化而言,我們一般都會使用 bgs**e 來持久化,畢竟它不會阻塞伺服器程序。
在 redis 的配置檔案,有提供設定伺服器每隔多久時間來執行 bgs**e 命令。
redis 預設是如下配置:
s**e 900 1 // 900 秒內,對資料庫至少修改 1 次。下面同理
s**e 300 10
s**e 60 10000
只要滿足其中一種情況,伺服器就會執行 bgs**e 命令。
我們從上面的介紹知道,rdb 持久化通過儲存資料庫狀態來持久化。而 aof 與之不同,它是通過儲存對資料庫的寫命令來記錄資料庫狀態。
比如執行了set key 123
,redis 就會將這條寫命令儲存到 aof 檔案中。
在伺服器下次啟動時,就可以通重載入和執行 aof 檔案中儲存的命令,來還原伺服器關閉前的資料庫狀態了。
總體流程和 rdb 持久化一樣 —— 都是建立乙個 *** 檔案、在伺服器下次啟動時就載入這個檔案來還原資料
那麼,aof 持久化具體是怎麼實現的呢?
aof 持久化功能的實現可以分為 3 個步驟:命令追加、檔案寫入、檔案同步
其中命令追加很好理解,就是將寫命令追加到 aof 緩衝區的末尾。
那檔案寫入和檔案同步怎麼理解呢?剛開始我也一臉懵逼,終於在網上找到了答案,參考見文末,有興趣的讀者可以去看看。
先不賣關子了,簡單一句話解釋就是:前者是緩衝區內容寫到 aof 檔案,後者是將 aof 檔案儲存到磁碟。
ok,明白什麼意思之後,我們稍微詳細看下這兩個東西是什麼鬼。
在《redis設計與實現》中提到,redis 伺服器程序就是乙個事件迴圈,這個迴圈中的檔案事件(socket 的可讀可寫事件)負責接收客戶端的命令請求,以及向客戶端傳送命令結果。
這個方法執行以下兩個工作:
下面說下三個的區別:
而對於操作特性來分析的話,則是如下情況:
模式write 是否阻塞主程序
s**e 是否阻塞主程序
停機時丟失的資料量
阻塞阻塞
最多隻丟失乙個命令的資料
阻塞不阻塞
一般情況下不超過 2 秒鐘的資料
阻塞阻塞
作業系統最後一次對 aof 檔案觸發 s**e 操作之後的資料
既然 aof 持久化是通過儲存寫命令到檔案的,那隨著時間的推移,這個 aof 檔案記錄的內容就越來越多,檔案體積也就越來越大,對其進行資料還原的時間也就越來越久。
針對這個問題,redis 提供了 aof 檔案重寫功能。
通過該功能來建立乙個新的 aof 檔案來代替舊檔案。並且兩個檔案所儲存的資料庫狀態一樣,但新檔案不會包含任何冗餘命令,所以新檔案要比舊檔案小得多。
而為什麼新檔案不會包含任何冗餘命令呢?
那是因為這個重寫功能是通過讀取伺服器當前的資料庫狀態來實現的。雖然叫做「重寫」,但實際上並沒有對舊檔案進行任何讀取修改。
比如舊檔案儲存了對某個 key 有 4 個 set 命令,經過重寫之後,新檔案只會記錄最後一次對該 key 的 set 命令。因此說新檔案不會包含任何冗餘命令
因為重寫涉及到大量 io 操作,所以 redis 是用子程序來實現這個功能的,否則將會阻塞主程序。該子程序擁有父程序的資料副本,可以避免在使用鎖的情況下,保證資料的安全性。
那麼這裡又會存在乙個問題,子程序在重寫過程中,伺服器還在繼續處理命令請求,新命令可能會對資料庫進行修改,這會導致當前資料庫狀態和重寫後的 aof 檔案,所儲存的資料庫狀態不一致。
為了解決這個問題,redis 設定了乙個 aof 重寫緩衝區。在子程序執行 aof 重寫期間,主程序需要執行以下三個步驟:
執行客戶端的請求命令
將執行後的寫命令追加到 aof 緩衝區
將執行後的寫命令追加到 aof 重寫緩衝區
當子程序結束重寫後,會向主程序傳送乙個訊號,主程序接收到之後會呼叫訊號處理函式執行以下步驟:
將 aof 重寫緩衝區內容寫入新的 aof 檔案中。此時新檔案所儲存的資料庫狀態就和當前資料庫狀態一致了
對新檔案進行改名,原子地覆蓋現有 aof 檔案,完成新舊檔案的替換。
當函式執行完成後,主程序就繼續處理客戶端命令。
因此,在整個 aof 重寫過程中,只有在執行訊號處理函式時才會阻塞主程序,其他時候都不會阻塞。
到目前為止,redis 的兩種持久化方式就介紹得差不多了。可能你會有疑惑,在實際專案中,我到底要選擇哪種持久化方案呢?下面,我貼下官方建議:
通常,如果你要想提供很高的資料保障性,那麼建議你同時使用兩種持久化方式。如果你可以接受災難帶來的幾分鐘的資料丟失,那麼你可以僅使用 rdb。
很多使用者僅使用了 aof,但是我們建議,既然 rdb 可以時不時的給資料做個完整的快照,並且提供更快的重啟,所以最好還是也使用 rdb。
在資料恢復方面:
rdb 的啟動時間會更短,原因有兩個:
rdb 檔案中每一條資料只有一條記錄,不會像 aof 日誌那樣可能有一條資料的多次操作記錄。所以每條資料只需要寫一次就行了。
rdb 檔案的儲存格式和 redis 資料在記憶體中的編碼格式是一致的,不需要再進行資料編碼工作,所以在 cpu 消耗上要遠小於 aof 日誌的載入。
注意:上面說了 rdb 快照的持久化,需要注意:在進行快照的時候(s**e),fork 出來進行 dump 操作的子程序會占用與父程序一樣的記憶體,真正的 copy-on-write,對效能的影響和記憶體的耗用都是比較大的。比如機器 8g 記憶體,redis 已經使用了 6g 記憶體,這時 s**e 的話會再生成 6g,變成 12g,大於系統的 8g。這時候會發生交換;要是虛擬記憶體不夠則會崩潰,導致資料丟失。所以在用 redis 的時候一定對系統記憶體做好容量規劃。
目前,通常的設計思路是利用複製(replication)機制來彌補 aof、snapshot 效能上的不足,達到了資料可持久化。即 master 上 snapshot 和 aof 都不做,來保證 master 的讀寫效能,而 sl**e 上則同時開啟 snapshot 和 aof 來進行持久化,保證資料的安全性。
參考:《redis設計與實現》
一文讓你明白CPU上下文切換
我們都知道,linux 是乙個多工作業系統,它支援遠大於 cpu 數量的任務同時執行。當然,這些任務實際上並不是真的在同時執行,而是因為系統在很短的時間內,將 cpu 輪流分配給它們,造成多工同時執行的錯覺。而在每個任務執行前,cpu 都需要知道任務從 載入 又從 開始執行,也就是說,需要系統事先幫...
一文讓你明白CPU上下文切換
來自知乎 我們都知道,linux 是乙個多工作業系統,它支援遠大於 cpu 數量的任務同時執行。當然,這些任務實際上並不是真的在同時執行,而是因為系統在很短的時間內,將 cpu 輪流分配給它們,造成多工同時執行的錯覺。而在每個任務執行前,cpu 都需要知道任務從 載入 又從 開始執行,也就是說,需要...
RF濾波器到底有多重要?一文讓你搞明白
rf 濾波器為何越來越重要?移動無線資料和 4g lte 網路的快速增長導致了對新頻段以及通重載波聚合來組合頻段的需求不斷增長,以容納無線流量。3g 網路只使用了大約五個頻段,lte 網路現在使用的頻段有 40 多個,隨著 5g 的到來,頻段的使用數量還會進一步增加。互聯裝置必須要跨多個頻段來傳送蜂...