參考:《Redis設計與實現》
文章目錄
1、RDB文件左右
數據庫狀態:
服務器中非空數據庫以及它們的鍵值對。
數據庫狀態磁盤化:
Redis是 內存數據庫,它將自己的數據庫狀態存在內存中,所以如果不想辦法將存儲在內存中的數據庫狀態保存到磁盤,一旦進程退出,服務器中的數據庫狀態也會消失不見。
爲了解決上述問題,Redis提供了RDB持久化功能,這個功能可以將Redis在內存中的數據庫狀態保存到磁盤,避免數據的意外丟失
RDB持久化:
可以手動觸發,也可以根據服務器配置選項週期性執行,將某個時間點上的數據庫狀態保存到RDB文件中。
RDB文件:
是一個經過壓縮的二進制文件,通過改文件可以還原生成RDB文件時的數據庫狀態。RDB文件是保存在磁盤上的,所以即使Redis服務器進程退出,只要RDB文件存在,Redis服務器就可以還原數據庫狀態。
2、RDB文件的創建和載入
有兩個命令可以生成RDB文件,分別是SAVE和BGSAVE。
1、SAVE-RDB文件創建
SAVE命令會阻塞Redis進程,直到RDB文件創建完畢爲止,在服務器進程阻塞期間,服務器不會處理任何命令請求。
SAVE的代碼處理實現:
def SAVE():
//創建RDB文件
rdbSave()
2、BGSAVE-RDB文件創建
BGSAVE命令會派生出一個子進程,通過子進程創建RDB文件,服務器進程(父進程)繼續處理命令請求。
BGSAVE的代碼處理實現:
def BGSAVE():
pid = fork() //fork子進程
if pid == 0 :
rdbSave() //RDB文件保存
signal_parent() //給父進程發送信號
else if pid > 0:
handle_request_and_wait_signal() //處理客戶端請求,並輪訓等待子進程的信號
else:
handle_fork_error() //處理錯誤的情況
BGSAVE命令執行期間其他命令的可執行性
- 如果客戶端發送SAVE命令會被服務器拒絕,原因是:避免父進程和子進程同時執行兩個rdbSave調用,產生競爭關係。
- 如果客戶端發送BGSAVE命令會被服務器拒絕,原因是:兩個BGSAVE命令也可能產生競爭關係。
- 如果客戶端發送BGREWRITEAOF(AOF)命令,會被延遲到BGSAVE命令執行結束之後執行,原因:雖然兩個子進程之間不衝突,但是爲了避免兩個子進程同時執行大量的磁盤寫入操作。
- 如果服務器正在執行BGREWRITEAOF命令,客戶端發送BGSAVE命令會被服務器拒絕。
3、RDB文件載入
RDB文件的載入是在服務器啓動時自動執行的,Redis並沒有專門用於載入RDB文件的命令,只要redis服務器啓動時檢測到RDB文件,就會自動載入。
因爲AOF文件的更新頻率比RDB文件更高,所以如果服務器開啓AOF持久化功能,會優先通過AOF文件來還原數據庫狀態。所以整個的執行過程如下:
服務器在載入RDB文件的過程中,會一直處於阻塞狀態,直到載入工作完成爲止。
3、RDB文件自動間隔性保存
1、save選項配置
Redis允許用戶通過設置服務器配置save選項,服務器隔一段時間之後會執行一次BGSAVE命令。
save選項對應的代碼結構:
{
struct saveparam *saveparams //保存對應配置的數組
struct saveparam{
time_t seconds //秒數
int changes //修改數
}
}
save選項默認值和意義:
save 900 1
save 300 10
save 60 10000
##含義
#第一行:服務器在900秒之內至少進行了1次修改
#第二行:服務器在300秒之內至少進行了10次修改
#第三行:服務器在60秒內至少進行了10000次修改
#只要滿足其中一個條件就會觸發BGSAVE操作
2、具體實現
Redis服務器通過dirty計數器(操作了多少個數據庫數字就加上多少的值)和lastsave最後一次保存時間,來實現是否執行BGSAVE命令的判斷:
具體的數據結構如下:
struct redisServer{
long long dirty //dirty計數器
time_t lastsave //上一次進行保存的時間信息
}
Redis 的服務器週期性函數serverCron默認每隔100毫秒執行一次,通過檢查save選項的條件是否滿足,來判斷是否進行BGSAVE函數的執行。
4、RDB文件結構
RDB文件結構如下,鍵值對的value存儲可能採用壓縮,存儲的類型也會導致value的多樣化,可參考原書:
地址:RDB文件結構圖解