redis是基於內存的數據庫,提供了內存數據持久化到文件的兩種方式,一種是寫RDB文件方式,另外一種是寫AOF文件,默認執行的是RDB文件持久化方式。redis
當在redis.config配置文件中開啓AOF持久化機制時,redis在啓動時,會優先載入AOF文件。其中服務器在載入文件的過程當中處於阻塞狀態。下圖爲redis啓動時載入持久化文件的流程。數據庫
1、RDB文件的建立與載入。安全
建立RDB文件有兩種命令的方式,Save與BGSAVE,其中Save命令會阻塞redis服務器進程。致使這段期間服務器不能接受客戶端的請求,BGSAVE命令會建立子進程來執行RDB文件的建立。因此BGSAVE不會阻塞服務器進程。SAVE與BGSAVE命令都會以不一樣的方式來調用rdb.c/rdbSave函數來完成RDB文件的建立工做。僞代碼以下:服務器
def SAVE():app
rdbSave()//建立RDB文件。函數
def BGSAVE():性能
pid = fork()//建立子進程。操作系統
if(pid == 0)3d
rdbSave()//子進程負責建立RDB文件。server
signal_parent()//完成後向父進程發送信號。
if(pid > 0)
handle_request_and_wait_signal()//父進程繼續處理命令請求,並經過輪詢等待子進程的信號。
else
handle_fork_error().
那麼在服務器端執行BGSAVE命令期間,若是客戶端發送了SAVE命令,BGSAVE,BGREWRITEAOF 命令時,則服務端拒絕客戶端的命令請求。拒絕save命令是防止父進程與子進程同時執行,rdbSave期間產生競爭條件。BGSAVE一樣是爲了防止競爭條件的產生。至於BGREWRITEAOF則是爲了性能的考慮,防止兩個進程同時對磁盤進行寫入操做。
二。redis如何同步RDB文件 。
一般狀況下,redis經過讀取配置文件按期保存數據庫的狀態到RDB文件。
例如默認配置文件配置以下圖所示:
上圖分別表示在900秒內至少執行1次數據庫修改,300秒內至少10次數據庫修改,60秒內至少10000次數據庫修改操做,只要知足任何一條,數據庫執行RDB文件的同步操做。
當服務器啓動時會讀取配置文件的以上配置信息,並將上面的配置信息寫入到redisServer結構中的saveparams屬性中。
struct redisServer{
......
long long dirty;//距離最近一次寫入RDB文件全部數據庫文件修改的總次數。
time_t lastsave;//上一次執行保存RDB文件的時間
struct saveparam *saveparams;
.......
}
其中saveparam結構以下:
struct saveparam{
time_t seconds;//秒數
int changes;//修改數
}
其中dirty和lastsave的做用就是爲了redis 週期性執行函數serverCron(每一個100毫秒執行一次)根據saveparams的配置信息檢查條件是否知足來執行RDB文件的同步操做。下圖爲按期執行保存的僞代碼。
那麼RDB文件保存的數據主要是哪些呢,經過下面的介紹咱們將瞭解到RDB文件保存到數據庫的狀態信息主要是key,value信息。下面咱們須要瞭解RDB文件的組織結構。
三 、RDB文件的結構
RDB文件整體結構以下圖所示:
其中REDIS與EOF 爲常量部分。db_version爲RDB文件的版本,不一樣的版本對應的格式會有區別。本文討論的是RDB文件的第六版本。check_sum 主要作RDB文件的校驗和計算,防止文件篡改或者缺損,它的值是根據前面的格式內容進行計算而得。
databases部分分兩種狀況,databases爲空的狀況,即數據庫中沒有數據的狀況。和數據庫不爲空的狀況。
數據庫爲空的狀況以下圖所示:
數據庫不爲空的狀況,以下圖所示:
其中database0或database3的 格式的組成以下圖所示:
其中SELECTDB爲常量,db_number 爲 對應的數據庫。鍵值對爲對應的數據庫鍵值,其中包括帶過時時間的鍵值對與不帶過時時間的鍵值對。
不帶過時時間的鍵值對的結構以下圖所示:
其中TYPE記錄了value的類型,值爲常量,佔一個字節,值爲如下常量中的一種。
帶有過時時間的鍵值對的結構以下:
其中:EXPIRETIME_MS表示的是以毫秒爲單位的過時時間對應的時間戳。ms表示的是對應的時間值。如 下圖所示:
Redis除了提供RDB文件的持久化方式外,還提供了AOF持久化機制,與RDB保存數據庫的鍵值對的方式不一樣的是,AOF提供的持久化機制保存的是redis執行的命令 。以下圖所示:
若對redis空數據庫執行以下寫命令:
則AOF方式將以redis命令請求協議的方式保存到AOF文件中。對於上面執行的三條命令。AOF文件內容格式以下圖所示。
其中第一行SELECT DB是由redis服務器自動添加上去的。
AOF 文件持久化的實現。
AOF 文件持久化的實現方式分爲三個步驟,AOF命令追加,寫AOF文件,同步AOF文件。
其中AOF命令追加是將客戶端請求的命令,以命令協議的方式追加到服務器狀態的redisServer的aof_buf緩衝區的末尾。以下圖所示:
AOF文件的寫入是將追加到AOF緩衝區的命令寫入到AOF文件。這個操做是在文件事件處理程序中來作的。REDIS服務器進程是個事件循環,在接收到客戶端發送的命令請求時,文件事件會從考慮是否將aof_buf緩衝區中的內容寫入和保存到AOF文件。
事件處理函數以下圖所示:
其中flushAppendOnlyFile()函數的行爲由配置文件選項:appendfsync 來決定。appendfsync的值爲下表中的任何一項,默認appendfsync值爲everysec:
其中,這裏的寫入表示的是將aof_buf緩衝區的內容寫入到AOF文件,此時這個寫入並無真正寫入到磁盤文件,操做系統爲了提升文件寫入的效率,在調用write()函數時,會將數據暫且寫入到內存的一塊緩衝區。待緩衝區滿或者強制時間到達或者強制刷新緩衝區時纔將數據同步到文件。同步表示的就是將內存緩衝區中的數據同步到磁盤。
經過上面的分析,咱們能夠看出來在效率與安全性上 得出:
always的安全性 最高,在每一個文件時間中都會寫入AOF及同步AOF文件。最多會丟失一個事件循環的命令數據。性能最差,由於每一個事件循環都要寫文件及同步文件。
everysec的安全性次之。寫入的性能同no。
最後是no的安全性最弱,會丟失上一次同步開始後的數據,寫入的性能最好,同everysec。
AOF文件的載入與還原過程以下圖所示:
AOF重寫
由於AOF文件隨着命令請求的不斷執行,會逐漸變的愈來愈大。例如以下圖所示:
爲了保存list鍵的數據狀態,須要保存6條命令,
其實list的狀態咱們能夠用一條命令來保存,如:
RPUSH list "C" "D" "E" "F" "G"
其實AOF重寫是保存的是當前數據庫的鍵值對的狀態。它根據值所對應的類型,執行對應的命令操做。 由於在進行AOF命令重寫時會進行大量的寫入操做,這時若是在服務器進程中來進行重寫操做,會阻塞服務器進程,致使其沒法處理客戶端的請求,爲了不這種狀況的發生,AOF重寫是在子進程中來完成的。子進程會拷貝服務器進程的數據副本,這樣作的好處是保證再也不使用鎖的狀況下保證數據的安全性。一樣,使用子進程帶來的一個問題是當子進程進行AOF重寫時,服務器進程在這段期間會接收客戶端的命令請求,這致使重寫後的AOF文件與服務器的狀態不一致的狀況。爲了解決這個狀況。redis引入了重寫緩衝區的概念,這個重寫緩衝區在redis建立AOF子進程重寫後使用。重寫期間,命令會被寫入到緩衝區與重寫緩衝區。當子進程完成了AOF文件的重寫,會像父進程發送一個重寫完成的信號,父進程在接收到信號後,會調用信號處理函數,執行如下操做:
1.重寫緩衝區的內容同步到新的AOF文件中。
2.重命名新的AOF文件,覆蓋原有的AOF文件。
此時,信號處理函數再父進程執行,會短暫阻塞服務器進程。
下圖爲AOF執行重寫的過程。