Redis數據持久化—RDB持久化與AOF持久化
大家好,我是白澤,今天講一下Redis的持久化,大家都知道Redis數據庫之所以快,很大的原因是因為它運行在服務器的內存中,但一旦服務器進程退出,服務器中的數據庫狀態也會消失,為了解決這個問題,Redis提供了兩種數據持久化的機制:這倆本質上都是將數據庫狀態保存到磁盤里,然后下次取出來加載到內存中還原數據庫,但是實現角度有所不同
RDB持久化
RDB持久化可以手動執行,也可以配置定期自動執行,該功能可以將某個時間點上的數據庫的狀態保存到一個RDB文件中(說白了就是數據庫中一個個的鍵值對),只要導入RDB文件,就能還原到之前時刻數據庫的狀態
RDB文件的創建
有兩個Redis命令可以用於生成RDB文件,一個時SAVE,一個是BGSAVE
- SAVE命令會阻塞Redis服務器進程,直到RDB文件創建完畢,在服務器進程阻塞期間,服務器不能處理任何命令請求
- BGSAVE命令會派生一個子進程,由子進程負責創建RDB文件,服務器進程繼續處理命令請求
RDB文件的載入
只要Redis服務器在啟動時檢測到RDB文件的存在,就會自動載入RDB文件,需要提一下:后面要講的AOF持久化也會對應生成AOF文件,由於AOF文件更新的頻率通常比RDB文件更高,也就意味着AOF文件中保存着更近期數據庫的狀態,因此如果服務器開啟了AOF持久化功能,那么服務器會優先使用AOF文件來還原數據庫狀態
自動間隔性保存
上面提到,BGSAVE命令可以不阻塞服務器進程而通過子進程去執行,所以Redis允許用戶通過設置服務器的save選項,讓服務器每隔一段時間自動執行一次BGSAVE命令,舉個例子如果我們向服務器提供如下配置:
save 900 1
save 300 10
save 60 10000
那么只要滿足下面條件中的任意一個,BGSAVE命令就會被執行:
- 服務器在900秒內,對數據庫至少執行了1次修改
- 服務器在300秒內,對數據庫至少執行了10次修改
- 服務器在60秒內,對數據庫至少執行了10000次修改
當然,save選項的默認配置就是上面給出的3條配置
檢查保存條件是否滿足
Redis服務器維護着一個dirty計數器以及一個lastsave屬性,dirty計數器記錄距離上一次成功執行SAVE或GBSAVE命令之后,服務器對數據庫狀態進行了多少次修改,lastsave是一個UNIX時間戳,記錄上一次SAVE或BGSAVE的時刻
Redis服務器周期性操作函數serverCron默認每100毫秒就會執行一次,該函數用於對正在運行的數據庫進行維護,它其中的一項工作就是檢查save選項設置的保存RDB文件的條件是否滿足,滿足就執行BGSAVE命令(配合dirty和lastsave就可以實現判斷)
AOF持久化
RDB文件保存的是數據庫某個時刻的狀態,說白了也就是一個個的鍵值對;AOF持久化是通過保存Redis服務器所執行的寫命令來記錄數據庫狀態的(好比RDB將數據庫的結果存了起來,而AOF將增、刪、改語句存了起來,還原時RDB是直接拷貝一份結果,AOF是再執行一遍各個語句得到結果)
AOF持久化的實現
-
命令追加:
當AOF持久化功能處於打開狀態時,服務器在執行一個寫命令后,將被執行的寫命令追加到服務器狀態的aof_buf緩沖區的末尾:
struct redisServer {
//...
sds aof_buf; //AOF緩沖區,還記得sds么,簡單動態字符串~
//...
}
-
AOF文件的寫入與同步:
Redis服務器進程就是一個事件循環,這個循環中的文件事件負責接收客戶端的命令請求,以及向客戶端發送命令回復,而事件事件則負責像serverCron函數這樣需要定時運行的函數
因為服務器在處理文件事件時可能會執行寫命令,使得一些內容被增加到aof_buf緩沖區里,所以服務器在每次結束一個事件循環之前, 都會調用flushAppendOnlyFile函數,考慮是否需要將aof_buf緩沖區中的內容寫入和保存到AOF文件中
flushAppendOnlyFile函數的行為由服務器配置的appendfsync的值決定,如下表:
AOF文件的載入與數據還原
因為AOF文件里包含了重建數據庫狀態所需要的所有寫命令,所以服務器只要讀入並重新執行一遍AOF文件里的寫命令,就可以還原服務器關閉之前數據庫的狀態,Redis讀取AOF文件並還原過程為:創建一個不帶網絡連接的偽客戶端,用該客戶端執行AOF文件中的寫命令,還原數據庫
AOF重寫的概念
因為AOF持久化是通過保存被執行的寫命令來記錄數據庫狀態的,隨着服務器運行時間的流逝,AOF文件中的內容會越來越多,舉個極端的例子,服務器執行了一億次寫操作,操作的內容就是對同一鍵的值修改了一億次,顯然其中只有最后那條操作是有效的,其余都是冗余的數據,而此時AOF文件將所有寫操作都記錄了下來,使得AOF文件過大,服務器開銷巨大
為了解決AOF文件體積膨脹的問題,Redis提供了AOF文件重寫功能,通過該功能,Redis服務器可以創建一個新的AOF文件來代替現有的AOF文件,新舊兩個AOF文件保存的數據庫狀態相同,但新AOF文件不會包含任何冗余命令
AOF文件重寫的實現
AOF文件的重寫並不需要對現有的AOF文件進行任何讀取操作,這個功能是通過讀取服務器當前數據庫的狀態來實現的,比如我對一個集合鍵執行了10次插入操作,那么相比記錄10條插入命令,直接記錄一條插入10個數據的集合的命令即可
總結原理就是:首先從數據庫中讀取鍵現在的值,然后用一條命令去記錄鍵值對,代替之前記錄這個鍵值對的多條命令
AOF后台重寫
因為Redis是單線程模型,AOF文件重寫需要掃描整個Redis數據庫,將使得服務器被長時間占用去執行AOF文件重寫,使得服務器失去響應,因此Redis將AOF文件重寫程序放入子進程中執行
一個問題:
在子進程進行AOF文件重寫期間,服務器進程還需要繼續處理命令請求,而新的命令可能會對現有的數據庫狀態進行修改,從而使得服務器當前的數據庫狀態和重寫后的AOF文件所保存的數據庫狀態不一致
解決方案:
針對上述問題,Redis服務器設置了一個AOF重寫緩沖區,在服務器創建AOF文件重寫子進程后開始使用,將期間服務器接收到的新的寫命令除了發給AOF緩沖區aof_buf之外,也發送到AOF重寫緩沖區堆積,在子進程完成AOF文件重寫之后,將AOF重寫緩沖區中的寫命令追加到重寫完成的新AOF文件中,此時新的AOF文件所保存的數據庫狀態和服務器當前的數據庫狀態一致
最后,對新的AOF文件進行改名,原子地覆蓋現有的AOF文件,完成新舊兩個AOF文件的替換