Redis數據持久化—RDB持久化與AOF持久化
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文件的替換