Redis數據持久化—RDB持久化與AOF持久化

Redis數據持久化—RDB持久化與AOF持久化

大家好,我是白澤,今天講一下Redis的持久化,大家都知道Redis資料庫之所以快,很大的原因是因為它運行在伺服器的記憶體中,但一旦伺服器進程退出,伺服器中的資料庫狀態也會消失,為了解決這個問題,Redis提供了兩種數據持久化的機制:這倆本質上都是將資料庫狀態保存到磁碟里,然後下次取出來載入到記憶體中還原資料庫,但是實現角度有所不同

RDB持久化

RDB持久化可以手動執行,也可以配置定期自動執行,該功能可以將某個時間點上的資料庫的狀態保存到一個RDB文件中(說白了就是資料庫中一個個的鍵值對),只要導入RDB文件,就能還原到之前時刻資料庫的狀態

RDB文件的創建

有兩個Redis命令可以用於生成RDB文件,一個時SAVE,一個是BGSAVE

  1. SAVE命令會阻塞Redis伺服器進程,直到RDB文件創建完畢,在伺服器進程阻塞期間,伺服器不能處理任何命令請求
  2. 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命令就會被執行:

  1. 伺服器在900秒內,對資料庫至少執行了1次修改
  2. 伺服器在300秒內,對資料庫至少執行了10次修改
  3. 伺服器在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持久化的實現

  1. 命令追加:

    當AOF持久化功能處於打開狀態時,伺服器在執行一個寫命令後,將被執行的寫命令追加到伺服器狀態的aof_buf緩衝區的末尾:

struct redisServer {
    //...
	sds aof_buf;	//AOF緩衝區,還記得sds么,簡單動態字元串~
    //...
}
  1. 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文件的替換

Tags: