Redis系列(三):Redis的持久化機制(RDB、AOF)

本篇博客是Redis系列的第3篇,主要講解下Redis的2種持久化機制:RDB和AOF。面試

本系列的前2篇能夠點擊如下連接查看:數據庫

Redis系列(一):Redis簡介及環境安裝安全

Redis系列(二):Redis的5種數據結構及其經常使用命令服務器

1. 爲何須要持久化?

由於Redis是內存數據庫,它將本身的數據存儲在內存裏面,一旦Redis服務器進程退出或者運行Redis服務器的計算機停機,Redis服務器中的數據就會丟失。網絡

爲了不數據丟失,因此Redis提供了持久化機制,將存儲在內存中的數據保存到磁盤中,用於在Redis服務器進程退出或者運行Redis服務器的計算機停機致使數據丟失時,快速的恢復以前Redis存儲在內存中的數據。數據結構

Redis提供了2種持久化方式,分別爲:app

  1. RDB持久化
  2. AOF持久化

接下來,咱們一一詳解。post

2. RDB持久化

RDB持久化是將某個時間點上Redis中的數據保存到一個RDB文件中,以下所示:操作系統

基於RDB持久化的上述性質,因此RDB持久化也叫作快照持久化。線程

該文件是一個通過壓縮的二進制文件,經過該文件能夠還原生成RDB文件時Redis中的數據,以下所示:

2.1 建立RDB文件

Redis提供了2個命令來建立RDB文件,一個是SAVE,另外一個是BGSAVE。

SAVE命令會阻塞Redis服務器進程,直到RDB文件建立完畢爲止,在服務器進程阻塞期間,服務器不能處理任何命令請求,以下所示:

BGSAVE命令會派生出一個子進程,而後由子進程負責建立RDB文件,服務器進程(父進程)繼續處理命令請求,以下所示:

以上描述也是這2個命令的區別,這裏是重點,面試常常會問到

由於BGSAVE命令能夠在不阻塞服務器進程的狀況下執行,因此推薦使用BGSAVE命令。

咱們能夠手動執行該命令,如上面截圖所示,但仍是推薦設置下Redis服務器配置文件的save選項,讓服務器每隔一段時間自動執行一次BGSAVE命令。

咱們能夠經過save選項設置多個保存條件,只要其中任意一個條件被知足,服務器就會執行BGSAVE命令。

save選項設置的默認條件以下所示:

save 900 1

save 300 10

save 60 10000

默認的配置條件表示,只要知足如下3個條件中的任意1個,BGSAVE命令就會被執行:

  • 服務器在900s(即15分鐘)以內,對數據庫進行了至少1次修改
  • 服務器在300s(即5分鐘)以內,對數據庫進行了至少10次修改
  • 服務器在60s(即1分鐘)以內,對數據庫進行了至少10000次修改

當知足條件執行BGSAVE命令時,輸出日誌以下圖所示:

生成的RDB文件會根據Redis配置文件中的名稱和路徑來保存,相關的2個配置以下所示:

最終生成的RDB文件以下所示(截圖爲本機Windows環境,Linux環境下路徑會稍有不一樣):

2.2 載入RDB文件

首先,咱們要明確的是,載入RDB文件的目的是爲了在Redis服務器進程從新啓動以後還原以前存儲在Redis中的數據。

而後,Redis載入RDB文件並無專門的命令,而是在Redis服務器啓動時自動執行的。

並且,Redis服務器啓動時是否會載入RDB文件還取決於服務器是否啓用了AOF持久化功能,具體判斷邏輯爲:

  1. 只有在AOF持久化功能處於關閉狀態時,服務器纔會使用RDB文件來還原數據。
  2. 若是服務器開啓了AOF持久化功能,那麼服務器會優先使用AOF文件來還原數據。

以上判斷邏輯以下圖所示:

默認狀況下,Redis服務器的AOF持久化功能是關閉的,因此Redis服務器在啓動時會載入RDB文件,

啓動日誌以下所示:

2.3 服務器狀態

建立和載入RDB文件,可能存在的服務器狀態有如下3種:

  1. 當執行SAVE命令時,Redis服務器會被阻塞,此時客戶端發送的全部命令請求都會被阻塞,只有在服務器執行完SAVE命令,從新開始接受命令請求以後,客戶端發送的命令請求才會被處理。
  2. 當執行BGSAVE命令時,Redis服務器不會被阻塞,Redis服務器仍然能夠繼續處理客戶端發送的命令請求。
  3. 服務器在載入RDB文件期間,會一直處於阻塞狀態,直到RDB文件載入成功。

3. AOF持久化

AOF持久化是經過保存Redis服務器所執行的寫命令來記錄數據庫數據的,以下圖所示:

默認狀況下,AOF持久化功能是關閉的,若是想要打開,能夠修改下圖所示的配置:

舉個例子,假設Redis中尚未存儲任何數據,咱們執行了以下所示的命令:

而後咱們會發現Redis服務器生成了1個名爲appendonly.aof的文件,打開該文件,咱們能夠看到上面執行的3個寫命令都存儲在該文件中:

3.1 AOF持久化的實現

當AOF持久化功能處於打開狀態時,Redis服務器在執行完一個寫命令以後,會以協議格式(如上面截圖中AOF文件裏保存寫命令的格式)將被執行的寫命令追加到服務器狀態的AOF緩衝區的末尾,而後Redis服務器會根據配置文件中appendfsync選項的值來決定什麼時候將AOF緩衝區中的內容寫入和同步到AOF文件裏面。

appendfsync選項有如下3個值:

  1. always

    從安全性來講,always是最安全的(丟失數據最少),由於即便出現故障停機,數據庫也只會丟失一個事件循環中所產生的命令數據。

    從效率來講,always的效率最慢,由於服務器在每一個事件循環都要將AOF緩衝區中的全部內容寫入到AOF文件,而且同步AOF文件。

  2. everysec

    從安全性來講,everysec模式下,即便出現故障停機,數據庫只會丟失一秒鐘的命令數據。

    從效率來講,everysec模式足夠快,由於服務器在每一個事件循環都要將AOF緩衝區中的全部內容寫入到AOF文件,而且每隔一秒就要在子線程中對AOF文件進行同步。

  3. no

    從安全性來講,no模式下,若是出現故障停機,數據庫會丟失上次同步AOF文件以後的全部寫命令數據,具備不肯定性,由於服務器在每一個事件循環都要將AOF緩衝區中的全部內容寫入到AOF文件,至於什麼時候對AOF文件進行同步,則由操做系統控制。

    從效率來講,no模式和everysec模式的效率差很少。

appendfsync選項的默認值是everysec,也推薦使用這個值,由於既保證了效率又保證了安全性。

3.2 載入AOF文件

由於AOF文件包含了重建數據庫所需的全部寫命令,因此Redis服務器只要讀入並從新執行一遍AOF文件裏面保存的寫命令,就能夠還原Redis服務器關閉以前的數據。

Redis讀取AOF文件並還原數據庫的詳細步驟以下:

  1. 建立一個不帶網絡鏈接的僞客戶端

    由於Redis的命令只能在客戶端上下文中執行,而載入AOF文件時所使用的命令直接來源於AOF文件而不是網絡鏈接,因此服務器使用了一個沒有網絡鏈接的僞客戶端來執行AOF文件保存的寫命令。

    僞客戶端執行命令的效果和帶網絡鏈接的客戶端執行命令的效果徹底同樣。

  2. 從AOF文件中分析並讀取出一條寫命令。

  3. 使用僞客戶端執行被讀取出的寫命令。

  4. 一直執行步驟2和步驟3,直到AOF文件中的全部寫命令都被執行完畢。

以上步驟以下圖所示:

若是Redis服務器開啓了AOF持久化功能,那麼Redis服務器在啓動時會載入AOF文件,

啓動日誌以下所示:

3.3 AOF重寫

由於AOF持久化是經過保存被執行的寫命令來記錄數據庫數據的,因此隨着Redis服務器運行時間的增長,AOF文件中的內容會愈來愈多,文件的體積會愈來愈大,若是不作控制,會有如下2點壞處:

  1. 過多的佔用服務器磁盤空間,可能會對Redis服務器甚至整個宿主計算機形成影響。
  2. AOF文件的體積越大,使用AOF文件來進行數據庫還原所需的時間就越多。

舉個例子,在客戶端執行以下命令:

爲了記錄這個list鍵的狀態,AOF文件就須要保存上面執行的6條命令。

爲了解決AO文件體積愈來愈大的問題,Redis提供了AOF文件重寫功能,即Redis服務器會建立一個新的AOF文件來替代現有的AOF文件,新舊兩個AOF文件所保存的數據庫數據相同,但新AOF文件不會包含任何浪費空間的冗餘命令,因此新AOF文件的體積一般會比舊AOF文件的體積要小不少。

3.3.1 AOF重寫的實現原理

AOF文件重寫並不須要對現有的AOF文件進行任何讀取、分析或者寫入操做,而是經過讀取服務器當前的數據庫數據來實現的。

仍然以上面的list鍵爲例,舊的AOF文件保存了6條命令來記錄list鍵的狀態,但list鍵的結果是「C」 "D" "E" "F" "G"這樣的數據,因此AOF文件重寫時,能夠用一條RPUSH list 「C」 "D" "E" "F" "G"命令來代替以前的六條命令,這樣就能夠將保存list鍵所需的命令從六條減小爲一條了。

按照上面的原理,若是Redis服務器存儲的鍵值對足夠多,AOF文件重寫生成的新AOF文件就會減小不少不少的冗餘命令,進而大大減少了AOF文件的體積。

綜上所述,AOF文件重寫功能的實現原理爲:

首先從數據庫中讀取鍵如今的值,而後用一條命令去記錄鍵值對,代替以前記錄這個鍵值對的多條命令。

3.3.2 AOF後臺重寫

由於AOF文件重寫會進行大量的文件寫入操做,因此執行這個操做的線程將被長時間阻塞。

由於Redis服務器使用單個線程來處理命令請求,因此若是由服務器進程直接執行這個操做,那麼在重寫AOF文件期間,服務器將沒法處理客戶端發送過來的命令請求。

爲了不上述問題,Redis將AOF文件重寫功能放到子進程裏執行,這樣作有如下2個好處:

  1. 子進程進行AOF文件重寫期間,服務器進程(父進程)能夠繼續處理命令請求。
  2. 子進程帶有服務器進程的數據副本,使用子進程而不是線程,能夠在避免使用鎖的狀況下,保證數據的安全性。

AOF後臺重寫的步驟以下所示:

  1. 服務器進程建立子進程,子進程開始AOF文件重寫

  2. 從建立子進程開始,服務器進程執行的全部寫命令不只要寫入AOF緩衝區,還要寫入AOF重寫緩衝區

    寫入AOF緩衝區的目的是爲了同步到原有的AOF文件。

    寫入AOF重寫緩衝區的目的是由於子進程在進行AOF文件重寫期間,服務器進程還在繼續處理命令請求,

    而新的命令可能會對現有的數據庫進行修改,從而使得服務器當前的數據庫數據和重寫後的AOF文件所

    保存的數據庫數據不一致。

  3. 子進程完成AOF重寫工做,向父進程發送一個信號,父進程在接收到該信號後,會執行如下操做:

    1.將AOF重寫緩衝區中的全部內容寫入到新AOF文件中,這樣就保證了新AOF文件所保存的數據庫數據和服務器當前的數據庫數據是一致的。

    2.對新的AOF文件進行更名,原子地覆蓋現有的AOF文件,完成新舊兩個AOF文件的替換。

Redis提供了BGREWRITEAOF命令來執行以上步驟,以下圖所示:

執行完成後,打開appendonly.aof文件,發現保存list鍵的命令從六條變爲了一條:

除了手動執行BGREWRITEAOF命令外,Redis還提供了2個配置項用來自動執行BGREWRITEAOF命令:

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

該配置表示,當AOF文件的體積大於64MB,而且AOF文件的體積比上一次重寫以後的體積大了至少一倍(100%),Redis將自動執行BGREWRITEAOF命令。

4. RDB持久化、AOF持久化的區別

經過上面的講解,咱們會發現Redis提供的2種持久化方法是有區別的,能夠總結爲如下4點:

  1. 實現方式
  2. 文件體積
  3. 安全性
  4. 優先級

接下來一一講解。

4.1 實現方式

RDB持久化是經過將某個時間點Redis服務器存儲的數據保存到RDB文件中來實現持久化的。

AOF持久化是經過將Redis服務器執行的全部寫命令保存到AOF文件中來實現持久化的。

4.2 文件體積

由上述實現方式可知,RDB持久化記錄的是結果,AOF持久化記錄的是過程,因此AOF持久化生成的AOF文件會有體積愈來愈大的問題,Redis提供了AOF重寫功能來減少AOF文件體積。

4.3 安全性

AOF持久化的安全性要比RDB持久化的安全性高,即若是發生機器故障,AOF持久化要比RDB持久化丟失的數據要少。

由於RDB持久化會丟失上次RDB持久化後寫入的數據,而AOF持久化最多丟失1s以內寫入的數據(使用默認everysec配置的話)。

4.4 優先級

因爲上述的安全性問題,若是Redis服務器開啓了AOF持久化功能,Redis服務器在啓動時會使用AOF文件來還原數據,若是Redis服務器沒有開啓AOF持久化功能,Redis服務器在啓動時會使用RDB文件來還原數據,因此AOF文件的優先級比RDB文件的優先級高。

5. 源碼及參考

Josiah L. Carlson 《Reids實戰》

黃健宏 《Redis設計與實現》

相關文章
相關標籤/搜索