(七)Redis 持久化 AOF、RDB

冬先生發表於2024-08-12

Redis 一旦伺服器當機,記憶體中的資料將全部丟失,從後端資料庫恢復這些資料,對資料庫壓力很大,且效能肯定比不上從 Redis 中讀取,會拖慢應用程式。所以,對 Redis 來說,實現資料的 持久化 ,避免從後端資料庫中進行恢復,是至關重要的。

1、AOF 日誌

AOF 日誌是先執行命令,把資料寫入記憶體,然後才記錄日誌以文字形式儲存,如下圖:"*3" 表示命令有三個部分組成,每部分由"$+數字"開頭,"$3 set"表示這部分有三個位元組,指"set"命令,"$7 testkey"表示該部分有七個位元組,即"testkey"命令,以此類推。
AOF 寫後日志只有命令能執行成功,才會被記錄到日誌中,避免額外的檢查開銷,也避免了出現記錄錯誤命令的情況,而且不會阻塞當前的寫操作。說完 優點 風險 ,如果剛執行完命令還沒有來得及記日誌就當機了,就有丟失的風險。其次,AOF 日誌在主執行緒中執行,如果在把日誌檔案寫入磁碟壓力過大,可能會帶來阻塞風險。

AOF 風險與寫回磁碟有關,針對這個問題提供了三種 寫回策略 ,即配置項 appendfsync 的三個可選值:
(1)Always 同步寫回:每個寫命令執行完,立馬同步地將日誌寫回磁碟
(2)Everysec 每秒寫回:每個寫命令執行完,先把日誌寫到 AOF 檔案的記憶體緩衝區,每隔一秒把緩衝區中的內容寫入磁碟
(3)No 作業系統控制的寫回:每個寫命令執行完,先把日誌寫到 AOF 檔案的記憶體緩衝區,由作業系統決定何時將緩衝區內容寫回磁碟

三種策略各有優劣,彙總如下:

選定寫回策略,並非萬事大吉,隨著接收的寫命令越來越多,AOF 檔案會越來越大,帶來效能問題。主要是以下三個方面:
(1)檔案系統本身對檔案大小有限制,無法儲存過大的檔案
(2)如果檔案太大,之後再往裡面追加命令記錄的話,效率也會變低
(3)如果發生當機,AOF 中記錄的命令要一個個被重新執行,檔案太大導致整個恢復過程就會非常緩慢,影響 Redis 正常使用

日誌檔案太大了怎麼辦呢?這個時候,AOF 重寫機制 就登場了。當一個鍵值對被多條寫命令反覆修改時,AOF 檔案會記錄相應的多條命令,而重寫時,只會根據這個鍵值對當前的最新狀態,為它生成對應的寫入命令,這樣一來,一個鍵值對在重寫日誌中只用一條命令就行了,並且在日誌恢復時,只用執行這條命令,就可以直接完成這個鍵值對的寫入了。舉個例子:
AOF 重寫並不會阻塞主執行緒,重寫過程是由後臺執行緒 bgrewriteaof 來完成的,透過記憶體複製和兩處日誌保證資料的完整性。

2、RDB 記憶體快照

記憶體快照 RDB 就是 Redis DataBase 的縮寫,和 AOF 相比,RDB 記錄的是某一時刻的資料,並不是操作,所以在做資料恢復時,我們可以直接把 RDB 檔案讀入記憶體,很快地完成恢復。但同時也面臨兩個問題:
(1)對哪些資料做快照?這關係到快照的執行效率問題。
(2)做快照時,資料還能被增刪改嗎?這關係到 Redis 是否被阻塞,能否同時正常處理請求。

為了提供所有資料的可靠性保證,全量快照會把記憶體中的所有資料都記錄到磁碟中,一個都不少。這樣會花費很多時間,全量資料越多,RDB 檔案就越大,往磁碟上寫資料的時間開銷就越大。對於 Redis 而言,它的單執行緒模型就決定了,我們要儘量避免所有會阻塞主執行緒的操作。Redis 提供了兩個命令來生成 RDB 檔案,分別是 save 和 bgsave:
(1)save:在主執行緒中執行,會導致阻塞。
(2)bgsave:建立一個子程序,專門用於寫入 RDB 檔案,避免了主執行緒的阻塞,這也是 Redis RDB 檔案生成的預設配置。

bgsave 避免主執行緒阻塞,可以正常接收請求,但是,為了保證快照完整性,它只能處理讀操作,不能修改正在執行快照的資料。Redis 就會藉助作業系統提供的寫時複製技術(Copy-On-Write, COW),在執行快照的同時,正常處理寫操作。示意如圖:簡單來說,主執行緒 fork 生成 bgsave 子程序,可共享主執行緒的所有記憶體資料。bgsave 子程序執行讀取主執行緒的記憶體資料,並把它們寫入 RDB 檔案。此時,如果主執行緒對這些資料也都是讀操作(例如圖中的鍵值對 A),則主執行緒和子程序互不影響。如果主執行緒要修改資料(例如圖中的鍵值對 C),則會生成該資料的副本,bgsave 子程序會把這個副本資料寫入 RDB 檔案,而在這個過程中,主執行緒仍然可以直接修改原來的資料。

至此上面提的兩個問題“哪些資料做快照”、“做快照時資料能否修改”就都解決了。新的問題又產生了,快照間隔多久做一次合適?如果在第二次快照前當機,就可能出現資料丟失的問題,如果太頻繁又會出現第一個還沒結束,第二個又開始的情況。雖然 bgsave 執行時不阻塞主執行緒,但是,如果頻繁地執行全量快照,也會給磁碟帶來額外的開銷,並且 bgsave 子程序需要透過 fork 操作從主執行緒建立出來,頻繁操作依然會阻塞主執行緒。

此時,增量快照就登場了,做了一次全量快照後,後續的快照只對修改的資料進行快照記錄,這樣可以避免每次全量快照的開銷。比如 T1 和 T2 時刻如果再做快照,我們只需要將被修改的資料寫入快照檔案就行。雖然我們記住哪些資料被修改了,但“記住”這個操作,需要我們使用額外的後設資料資訊去記錄,這會帶來額外的空間開銷問題。有時改動較小時,又要引入的額外空間區記錄,有些得不償失。此時我們就可以混合使用 AOF 日誌和記憶體快照的方法,在兩次快照之間,使用 AOF 日誌記錄這期間的所有命令操作。如圖,T1 和 T2 時刻的修改,用 AOF 日誌記錄,在第二次做全量快照時,就可以清空 AOF 日誌,因為修改都已經記錄到快照中了。這個方法既能享受到 RDB 檔案快速恢復的好處,又能享受到 AOF 只記錄操作命令的簡單優勢,可謂魚和熊掌兼得。

相關文章