redis原始碼分析(五):資料持久化

karspb發表於2021-09-09

redis 是基於記憶體讀寫的資料庫,所有資料都儲存在記憶體中,所以儲存的資料大小受到了限制。但redis依然提供了固化功能,與mysql,leveldb等資料庫不同的是,redis的儲存功能只是用做備份,恢復的功能,全量資料還是儲存在記憶體中。
redis內部提供了兩種固化資料的方式,aof 和rdb。

1.aof

資料寫入

aof全稱appendOnlyFile,要讓redis支援aof方式,需要將配置檔案的appendonly配置為"yes",並配置appendfsync 為everysec/always/no。everysec是redis推薦的配置,我們預設以這種方式來具體分析。
每次對資料有修改的操作,均會以append的方式寫入server.aof_buf中,每秒鐘將在時間事件中檢查這個緩衝區是否為空,不空則說明有新的更新,將其寫入aof檔案中(注意write函式並非實時寫入硬碟,可以用flush函式來強制重新整理未寫入的快取),並觸發重新整理任務,通知bio模組建立的執行緒中執行。aof的檔案是相對"白話"的,如圖是我本地擷取的aof檔案:

圖片描述

1.png

'*'後跟的是當前的引數個數,'$'後跟的是下一個引數的長度,再後來就是引數了。一個redis中有多個資料庫,我們需要確定資料增改到那個資料庫了,因此第一句為select db的操作。

除了手動配置的重新整理aof規則外,客戶端可以手動觸發bgrewriteaof來覆蓋已有的aof檔案(未開啟aof配置也是支援這個命令),客戶端發起此命令後,redis會fork一個子程式來將記憶體中的資料儲存到檔案中。那麼問題來了,子程式執行過程中,redis主程式依然在提供服務,如果期間寫入新的資料,那麼主子程式間的資料將會不同步,如何解決?期間的資料會儲存在連結串列資料塊server.aof_rewrite_buf_blocks中,當主程式用wait方法檢測到子程式退出以後,會呼叫backgroundRewriteDoneHandler函式,將快取的資料附加到新的aof檔案中。重寫aof可以減小aof檔案的大小,因為部分資料失效,或者來回更改,在everysec的配置中均會儲存下來,但是重寫aof之後,無效資料,或者中間狀態的修改均會被忽略。

資料載入

redis剛啟動的時候會優先檢查是否開啟aof配置(否則rdb),如果是則載入aof配置檔案,每個引數均以'n'結尾,因此讀取/判斷合法十分方便。建立一個fakeClient來模擬客戶端將資料插入記憶體中。這裡有一個疑問,資料載入的同時,服務依然處理客戶端的io任務,這樣不怕aof的舊檔案寫髒使用者新插入的資料麼

2.rdb

資料寫入

使用者可以配置redis中save的引數,格式為save  sec  commit 代表每sec秒,如果有commit次修改,則由服務自身執行一次bgsave操作。save配置可以有多個,相互之間是或的關係。server.dirty用來記錄資料修改次數,bgsave完成以後,會將其置0。
redis的每次寫入都是全量操作,步驟如下

  • 1.建立臨時檔案,寫入redis字串+版本號。

  • 2.寫入select db語句,如果當次db沒有資料,跳到下一個,沒有更多資料庫了,跳到步驟5。

  • 3.建立字典的迭代器,迴圈字典內的元素。

  • 4.redis都是鍵值對資料,分別寫入超時時間(如果有的話),value的型別,key,value,其中key和value儘可能的壓縮編碼。如果迭代器到達結尾,跳到步驟2。

  • 5.大於4的版本加入了校驗碼,校驗和使用crc計算,用臨時檔案覆蓋正式rdb檔案,流程結束。

資料載入

這個也沒有懸念了,怎麼寫入,就怎麼載入;讀取到資料之後直接呼叫dbAdd函式把資料插入到字典server.db[dbnum]->dict中。

對比下aof和rdb。

1.aof檔案相對於直接儲存了語句和中間過程。所以更易於開發者修改檢視資料。追加修改的命令,對io,cpu,記憶體壓力均較小,相對來說更吃儲存空間。
2.aof的缺點就是rdb的優點了。特別是如果rdb的save引數儲存不當,redis會頻繁執行bgsave命令,每次都是全量修改,在資料量大,修改頻繁的時候會是個災難。



作者:msrpp
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2001/viewspace-2816403/,如需轉載,請註明出處,否則將追究法律責任。

相關文章