Redis資料持久化—RDB持久化與AOF持久化

白澤來了發表於2021-05-20

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檔案的替換

相關文章