Redis設計與實現筆記(九) | 持久化
redis是基於記憶體的資料庫。總所周知,記憶體是RAM,資料斷電即丟失。對於資料庫來說,這種特性導致了資料永續性就無法保證。redis的持久化的機制解決這種問題,就是把資料庫的資料儲存到硬碟中,進行儲存,避免資料丟失。
redis有兩種持久化的方式:RDB和 AOF
一、RDB
RDB是redis預設的持久化方式。將當前資料庫的狀態,以快照的形式儲存在硬碟上。但如果開啟了AOF的話,redis優先使用AOF進行持久化。因為一般情況下,AOF的持久化的頻率更高,即使出現問題,丟失資料量會更少。
1.1 RDB工作流程
redis伺服器有兩種方式建立rdb檔案:SAVE 、BGSAVE
1.1.1 SAVE
在伺服器收到SAVE命令後,會由伺服器主程序建立rdb檔案。此時,伺服器是阻塞狀態的,客戶端的任何請求都會被拒絕。只有主程序執行完SAVE命令後,才會重新接收客戶端的請求
1.1.2 BGSAVE
在伺服器收到BGSAVE命令後,主程序會建立一個子程序。由子程序完成rdb檔案的建立,並不阻塞伺服器主程序,所以伺服器可以繼續接受客戶端的請求。
在執行BGSAVE期間,伺服器繼續接收請求命令。但如果請求命令是SAVE、BGSAVE、BGREWRITEAOF的話,伺服器如何進行處理呢?針對這個問題,伺服器有如下方式進行解決:
1. 如果客戶端發生的是SAVE 或者 BGSAVE 的話,伺服器直接拒絕本次請求
2.redis伺服器不允許BGREWRITEAOF 和 BGSAVE 同時執行:如果執行BGREWRITEAOF時,客戶端傳送BGSAVE命令,伺服器直接拒絕請求;如果執行BGSAVE時,客戶端傳送BGREWRITEAOF命令,伺服器會把BGREWRITEAOF命令推遲到BGSAVE執行完成後再執行。
二、AOF持久化
AOF是通過記錄資料庫的寫命令來記錄資料庫狀態的。如圖
2.1 AOF持久化的實現
AOF持久化主要分為三個步驟:命令追加、檔案寫入、檔案同步
2.1.1 命令追加
當AOF功能開啟後,伺服器在執行完成一個寫命令後。會以協議格式將被執行的寫命令追加到伺服器的aof_buf緩衝區(aof緩衝區)的末尾。
2.1.2 檔案寫入與檔案同步
redis伺服器程序本身就是一個事件迴圈,主要負責接收客戶端的請求命令和向客戶端傳送命令回覆。在一次事件迴圈期間,可能會執行寫命令,那麼有一些內容就被追加到aof_buf緩衝區裡面。所以在伺服器每次結束一個事件迴圈之前,它呼叫flushAppendOnlyFile函式,把aof緩衝區中的內容寫入和儲存到AOF檔案中。
2.1.3 AOF檔案的載入與資料還原
redis把資料持久化到AOF檔案中了,那麼怎麼還原資料呢?還原資料的步驟:
1.建立偽客戶端:因為redis的命令只有在redis客戶端上下文中才能執行,所以需要一個偽客戶端來執行aof檔案中的命令
2.逐條讀取aof檔案中的命令,並由偽客戶端執行。一直到AOF檔案中的命令處理完成為止。aof檔案載入的流程圖如下:
2.1.4 AOF重寫
aof持久化的原理如上講解,但這種機制卻有一個致命的問題。那就是aof檔案中的資料冗餘,隨著時間推移aof檔案會越來越大,如果不及時解決這個問題會嚴重redis伺服器的效能。舉例子說明:
如果執行以下程式碼:
rpush friuts ‘apple’ "banana" "orange"
lpop friuts
lpop friuts
lpop friuts
執行結束後,其實fruits 列表已經為空了,但是redis的aof會把這四條語句全部記錄下來。就aof還原來說,這四條寫命令是做的是無用功:不僅不會影響資料的最終結果,又消耗儲存aof檔案的儲存資源,降低了資料還原效能。
為了解決這種aof檔案體積膨脹的問題,redis提供了aof檔案重寫的機制。基於當前資料庫的資料創建出一個新的aof檔案,來替換現有的aof檔案。新舊兩個aof儲存的資料庫狀態相同,但是新的aof檔案還沒有記錄下冗餘命令,就可以降低aof檔案的空間了。隨著時間推移,這個新的aof也會不斷記錄下冗餘命令,也會逐漸膨脹,又會再建立新的aof檔案代替它。
2.1.5 AOF重寫的實現
AOF重寫是針對當前資料庫的狀態進行分析的,並不是對現有aof進行處理的。
Redis的aof重寫是在子程序中進行的,這樣設計有兩大好處:
1. 不阻塞伺服器程序(父程序),伺服器可繼續處理請求
2.採用的是子程序,而不是執行緒。那麼就不會有執行緒的資料不安全性問題
aof重寫的具體步驟:當redis伺服器建立aof重寫的子程序時,也會開啟一個AOF重寫緩衝區(注意:不是AOF緩衝區,AOF快取是實現aof持久化的)。在子程序進行AOF重寫時,redis執行完一條寫命令後,會把這條寫命令同時寫入AOF緩衝區和AOF重寫緩衝區。當子程序完成AOF重寫後,子程序會向父程序傳送完成新訊號,父程序把AOF重寫緩衝區的命令(這些命令屬於子程序執行期間,redis伺服器處理的新命令)追加到新建立的aof檔案內,然後對新建立的aof檔案進行改名,原子地完成對現有aof檔案的覆蓋,實現新舊AOF檔案的替換。
針對上面描述,可以知道:
- 子程序只對當前資料庫的狀態寫入新aof檔案中。對於AOF重寫緩衝區的命令,由父程序追加到新AOF檔案中;
- 為什麼要設定兩個緩衝區?aof緩衝區是為了保證對原aof的持久化;aof重寫緩衝區是對aof重寫期間,處理的寫命令進行的記錄。那麼只有重寫緩衝區不就夠了嗎,為什麼需要對原aof檔案的維護?如果不對原aof檔案的維護,一旦重寫失敗,重寫期間的寫命令就得不到記錄,會造成資料的丟失。
三、尚存在的問題
雖然持久化儘可能的保證了資料不會丟失,但這也是100%的保證。也就是說還是會存在少量資料丟失的情況。舉個例子:對於rdb持久化的方式,當某次完成了持久化後,還沒有進行下一次rdb持久化。掉電了或者出現其他的情況,導致伺服器宕機,那麼在最近一次的持久化之後的資料就會丟失。