當前位置: 妍妍網 > 碼農

阿裏二面,Redis宕機了,如何恢復數據?

2024-05-09碼農

作者:柯柏技術筆記
連結:https://juejin.cn/post/7342480215533404170

# 背景

有個同學阿裏二面,面試官問:redis宕機了,如何恢復數據?這位同學當時一臉懵,不知道如何回答。

分析分析這個問題,redis宕機,要想恢復數據,首先redis的數據有沒有做持久化,用的是哪種策略,這種策略的機制是什麽,有趣點是什麽,以及你們是從什麽方面考慮用著中機制的

其實面試官就是想考察,你們業務中redis的持久化策略,以及你對持久化策略有沒有了解過,還是就直接使用,不管數據會回遺失,反正遺失了都是運維的鍋,那你這樣基本上GG了

# 為什麽要做持久化

Redis是個基於記憶體的資料庫。那服務一旦宕機,記憶體中的數據將全部遺失。通常的解決方案是從後端資料庫恢復這些數據,但後端資料庫有效能瓶頸,如果是大數據量的恢復,

  1. 會對資料庫帶來巨大的壓力,嚴重可能導致mysql宕機

  2. 資料庫的效能不如Redis。導致程式響應慢。所以對Redis來說,實作數據的持久化,避免從後端資料庫中恢復數據,是至關重要的。


# 持久化策略

官方 支持的持久化有四種,如下:

  1. RDB(Redis 資料庫):RDB 永續性以指定的時間間隔執行數據集的時間點快照。

  2. AOF(僅追加檔):AOF 永續性記錄伺服器接收到的每個寫操作。然後可以在伺服器啟動時再次重播這些操作,從而重建原始數據集。命令使用與 Redis 協定本身相同的格式進行記錄。

  3. RDB + AOF:您還可以在同一個例項中組合 AOF 和 RDB。

  4. 無永續性:您可以完全禁用永續性。這種策略,一般很少有人使用吧

下面我們對這幾種策略,進行詳細梳理下

# RDB


RDB 就是 Redis DataBase 的縮寫,中文名為快照/記憶體快照,RDB持久化是把當前行程數據生成快照保存到磁盤上的過程,由於是某一時刻的快照,那麽快照中的值要早於或者等於記憶體中的值。

預設情況下,Redis 將數據集的快照保存在磁盤上名為 dump.rdb 的二進制檔中。

Redis 提供了兩個命令來生成 RDB 檔,分別是 save 和 bgsave。

  • save:在主執行緒中執行,會導致阻塞;

  • bgsave:建立一個子行程,專門用於寫入 RDB 檔,避免了主執行緒的阻塞,這也是 Redis RDB 檔生成的預設配置。

  • 一般透過 bgsave 命令來執行全量快照,這既提供了數據的可靠性保證,也避免了對 Redis 的效能影響。

    # redis.conf中配置RDB

    記憶體快照雖然可以透過技術人員手動執行SAVE或BGSAVE命令來進行,但生產環境下多數情況都會設定其周期性執行條件。

    # 周期性執行條件的設定格式為save<seconds> <changes># 預設的設定為:save900 1save300 10save60 10000# 以下設定方式為關閉RDB快照功能save""

    以上三項預設資訊設定代表的意義是:

  • 如果900秒內有1條Key資訊發生變化,則進行快照;

  • 如果300秒內有10條Key資訊發生變化,則進行快照;

  • 如果60秒內有10000條Key資訊發生變化,則進行快照。


  • # Copy-On-Write, COW

    redis在執行bgsave生成快照的期間,將記憶體中的數據同步到硬碟的過程可能就會持續比較長的時間,而實際情況是這段時間Redis服務一般都會收到數據寫操作請求。那麽如何保證快照的完整性呢?

    可能會說,為了保證快照完整性,redis只能處理讀操作,不能修改正在執行快照的數據。你想如果這樣?為了快照而暫停寫操作,同時候你的業務會受到很大的影響,是不可接受的,那有其他方案嗎?

    Redis 就會借助作業系統提供的寫時復制技術(Copy-On-Write, COW),在執行快照的同時,正常處理寫操作。


    bgsave 子行程是由主執行緒 fork 生成的,可以共享主執行緒的所有記憶體數據。bgsave 子行程執行後,開始讀取主執行緒的記憶體數據,並把它們寫入 RDB 檔。

    此時,如果主執行緒對這些數據也都是讀操作(例如圖中的鍵值對 A),那麽,主執行緒和 bgsave 子行程相互不影響。但是,如果主執行緒要修改一塊數據(例如圖中的鍵值對 C),那麽,這塊數據就會被復制一份,生成該數據的副本(鍵值對 C’)。然後,主執行緒在這個數據副本上進行修改。同時,bgsave 子行程可以繼續把原來的數據(鍵值對 C)寫入 RDB 檔。

    寫時復制機制保證快照期間數據可修改

    這既保證了快照的完整性,也允許主執行緒同時對數據進行修改,避免了對正常業務的影響。

    # 快照的頻率如何把握

    對於快照來說,所謂「連拍」就是指連續地做快照。這樣一來,快照的間隔時間變得很短,即使某一時刻發生宕機了,因為上一時刻快照剛執行,遺失的數據也不會太多。但是,這其中的快照間隔時間就很關鍵了。如下圖:

    為了盡可能保證在宕機的情況下,保證數據盡量不遺失,比如:一秒一次快照,那遺失的數據也是一秒。這看上去很美好,其實為帶來很大的問題,如果頻繁地執行全量快照,也會帶來兩方面的開銷

  • 一方面,頻繁將全量數據寫入磁盤,會給磁盤帶來很大壓力,多個快照競爭有限的磁盤頻寬,前一個快照還沒有做完,後一個又開始做了,容易造成惡性迴圈。

  • 另一方面,bgsave 子行程需要透過 fork 操作從主執行緒建立出來。雖然,子行程在建立後不會再阻塞主執行緒,但是,fork 這個建立過程本身會阻塞主執行緒,而且主執行緒的記憶體越大,阻塞時間越長。如果頻繁 fork 出 bgsave 子行程,這就會頻繁阻塞主執行緒了

  • 那這個頻率怎麽控制呢?這需要根據業務自身的情況,決定快照的頻率。比如筆者:我們目前的使用的策略是,關閉系統的自動快照功能,就是 設定 save "" , 定時淩晨連線redis,手動執行bgsave,進行快照生成。可能有人說,如果執行這樣的策略,數據遺失就是一天的,對,你說的對,但是我們的業務遺失一天的數據也沒關系,這是業務能容忍的 ,在生產的情況下,redis的穩定性相當高,基本上不會宕機,出現宕機的情況,也是因為伺服器自身的問題,導致機器重新開機,redis產生數據遺失。

    # 優缺點

    優點

  • RDB檔是某個時間節點的快照,預設使用LZF演算法進行壓縮,壓縮後的檔體積遠遠小於記憶體大小,適用於備份、全量復制等場景;

  • Redis載入RDB檔恢復數據要遠遠快於AOF方式;

  • 缺點

  • RDB方式即時性不夠,無法做到秒級的持久化;

  • 每次呼叫bgsave都需要fork子行程,fork子行程屬於重量級操作,頻繁執行成本較高;

  • RDB檔是二進制的,沒有可讀性,AOF檔在了解其結構的情況下可以手動修改或者補全;

  • 總結:rdb數據恢復速度非常快,就是無法做到秒級的持久化

    那有其他方式做到秒級的持久化嗎?Aof


    # AOF

    AOF 永續性記錄伺服器接收到的每個寫操作。然後可以在伺服器啟動時再次重播這些操作,從而重建原始數據集。命令使用與 Redis 協定本身相同的格式進行記錄

    Redis 是先執行命令,把數據寫入記憶體,然後才記錄日誌


    # AOF日誌內容

    我們以 Redis 收到「set testkey 1」命令後記錄的日誌為例,看看 AOF 日誌的內容,

    日誌格式說明*3表示當前命令有三個部份,每部份都是由$+數位開頭,後面緊跟著具體的命令、鍵或值。這裏,數位表示這部份中的命令、鍵或值一共有多少字節。例如,$3 set表示這部份有 3 個字節,也就是set命令

    redis.conf中配置AOF


    預設情況下,Redis是沒有開啟AOF的,可以透過配置redis.conf檔來開啟AOF持久化,關於AOF的配置如下:

    # appendonly參數開啟AOF持久化appendonlyno# AOF持久化的檔名,預設是appendonly.aofappendfilename "appendonly.aof"# AOF檔的保存位置和RDB檔的位置相同,都是透過dir參數設定的dir ./# 同步策略# appendfsync alwaysappendfsync everysec# appendfsync no# aof重寫期間是否同步no-appendfsync-on-rewrite no# 重寫觸發配置auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb# 載入aof出錯如何處理aof-load-truncated yes# 檔重寫策略aof-rewrite-incremental-fsync yes

    寫回策略

    AOF 機制給我們提供了三個選擇,也就是 AOF 配置項 appendfsync 的三個可選值。

  • Always,同步寫回:每個寫命令執行完,立馬同步地將日誌寫回磁盤;

  • Everysec,每秒寫回:每個寫命令執行完,只是先把日誌寫到 AOF 檔的記憶體緩沖區,每隔一秒把緩沖區中的內容寫入磁盤;

  • No,作業系統控制的寫回:每個寫命令執行完,只是先把日誌寫到 AOF 檔的記憶體緩沖區,由作業系統決定何時將緩沖區內容寫回磁盤。

  • 針對避免主執行緒阻塞和減少數據遺失問題,這三種寫回策略都無法做到兩全其美。我們來分析下其中的原因。

  • 「同步寫回」可以做到基本不丟數據,但是它在每一個寫命令後都有一個慢速的落盤操作,不可避免地會影響主執行緒效能;

  • 雖然「作業系統控制的寫回」在寫完緩沖區後,就可以繼續執行後續的命令,但是落盤的時機已經不在 Redis 手中了,只要 AOF 記錄沒有寫回磁盤,一旦宕機對應的數據就遺失了;

  • 「每秒寫回」采用一秒寫回一次的頻率,避免了「同步寫回」的效能開銷,雖然減少了對系統效能的影響,但是如果發生宕機,上一秒內未落盤的命令操作仍然會遺失。所以,這只能算是,在避免影響主執行緒效能和避免數據遺失兩者間取了個折中。

  • 我把這三種策略的寫回時機,以及優缺點匯總在了一張表格裏,以方便你隨時檢視。


    根據系統對高效能和高可靠性的要求,來選擇使用哪種寫回策略了。總結一下就是:

  • 想要獲得高效能,就選擇 No 策略;

  • 如果想要得到高可靠性保證,就選擇 Always 策略;

  • 如果允許數據有一點遺失,又希望效能別受太大影響的話,那麽就選擇 Everysec 策略。

  • 雖然AOF策略,能保證秒級數據遺失,但是隨著redis的長時間執行,aof檔會越來越大,如果宕機,進行數據恢復的時候速度是特別慢,影響業務,那有什麽好的發案處理嗎?aof日誌重寫

    # AOF日誌重寫

    AOF 檔是以追加的方式,逐一記錄接收到的寫命令的。當一個鍵值對被多條寫命令反復修改時,AOF 檔會記錄相應的多條命令。但是,在重寫的時候,是根據這個鍵值對當前的最新狀態,為它生成對應的寫入命令。這樣一來,一個鍵值對在重寫日誌中只用一條命令就行了,而且,在日誌恢復時,只用執行這條命令,就可以直接完成這個鍵值對的寫入了。


    重寫機制具有「多變一」功能。所謂的「多變一」,也就是說,舊日誌檔中的多條命令,在重寫後的新日誌中變成了一條命令,例如:

    我們對列表先後做了 6 次修改操作後,列表的最後狀態是[「D」, 「C」, 「N」],此時,只用 LPUSH u:list 「N」, 「C」, 「D」這一條命令就能實作該數據的恢復,這就節省了五條命令的空間。對於被修改過成百上千次的鍵值對來說,重寫能節省的空間當然就更大了。

    不過,雖然 AOF 重寫後,日誌檔會縮小,但是,要把整個資料庫的最新數據的操作日誌都寫回磁盤,仍然是一個非常耗時的過程。那這個過程,會阻塞主執行緒嗎

    # AOF重寫會阻塞嗎


    AOF重寫過程是由後台行程bgrewriteaof來完成的。 主執行緒fork出後台的bgrewriteaof子行程,fork會把主執行緒的記憶體拷貝一份給bgrewriteaof子行程,這裏面就包含了資料庫的最新數據。 然後,bgrewriteaof子行程就可以在不影響主執行緒的情況下,逐一把拷貝的數據寫成操作,記入重寫日誌。

    優缺點

    優點

    數據能做到秒級遺失,也就是說使用了aof這種機制,能做到最多遺失一秒的數據

    缺點

    恢復數據比較慢,雖然aof日誌重寫,可以減小檔,但是速度還是很慢

    那有沒有一種機制,能做到秒級遺失,恢復速度又比較快呢?RDB和AOF混合方式

    # RDB和AOF混合方式

    Redis 4.0 中提出了一個混合使用 AOF 日誌和記憶體快照的方法。簡單來說,記憶體快照以一定的頻率執行,在兩次快照之間,使用 AOF 日誌記錄這期間的所有命令操作。

    這樣一來,快照不用很頻繁地執行,這就避免了頻繁 fork 對主執行緒的影響。而且,AOF 日誌也只用記錄兩次快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現檔過大的情況了,也可以避免重寫開銷。

    如下圖所示,T1 和 T2 時刻的修改,用 AOF 日誌記錄,等到第二次做全量快照時,就可以清空 AOF 日誌,因為此時的修改都已經記錄到快照中了,恢復時就不再用日誌了。

    記憶體快照和AOF混合使用

    這個方法既能享受到 RDB 檔快速恢復的好處,又能享受到 AOF 只記錄操作命令的簡單優勢,頗有點「魚和熊掌可以兼得」的感覺,建議你在實踐中用起來。


    # 總結

    Rdb、Aof兩種持久化機制各有優缺點,需要根據自己的實際業務來衡量,到底使用哪種機制,最能滿足當下業務,我的建議

  • 數據不能遺失時,記憶體快照和 AOF 的混合使用是一個很好的選擇;

  • 如果允許分鐘級別的數據遺失,可以只使用 RDB;

  • 如果只用 AOF,優先使用 everysec 的配置選項,因為它在可靠性和效能之間取了一個平衡。

  • 熱門推薦