當機了,Redis資料丟了怎麼辦?

愛撒謊的男孩發表於2020-11-24

持續原創輸出,點選上方藍字關注我

目錄

  • 前言
  • 什麼是AOF?
    • 三種寫回策略
    • 日誌檔案太大怎麼辦?
    • AOF重寫會阻塞主執行緒嗎?
    • AOF的缺點
    • 總結
  • 什麼是RDB?
    • 給哪些資料做快照?
    • 快照時能夠修改資料嗎?
    • 多久做一次快照?
    • 增量快照
    • AOF和RDB混合使用
    • 總結
  • 總結

前言

Redis作為記憶體型的資料庫,雖然很快,依然有著很大的隱患,一旦伺服器當機重啟,記憶體中資料還會存在嗎?

很容易想到的一個方案是從後臺資料恢復這些資料,如果資料量很小,這倒是一個可行的方案。但是如果資料量過大,頻繁的從後臺資料庫訪問資料,壓力很大;另外一方面恢復資料的時間極慢。

對於Redis來說,實現資料的持久化和快速恢復是至關重要。

今天這篇文章就來介紹一下Redis持久化的兩種機制AOF日誌、RDB快照。

什麼是 AOF 日誌?

AOF(Append Only File)日誌稱之為寫後日志,即是命令先執行完成,把資料寫入記憶體,然後才會記錄日誌。

AOF日誌(文字形式)會將收到每一條的命令且執行成功的命令以一定的格式寫入到文字中(追加的方式)。

寫後日志有什麼好處呢? 如下:

  1. 對於寫前日誌無論命令是否執行成功都會被記錄,但是Redis的寫後日志則只有命令執行成功才會被寫入日誌,避免了日誌中存在錯誤命令;
  2. 同時由於是命令執行成功之後才會寫入日誌,因此不會阻塞當前命令的執行。

但是AOF日誌也有潛在的風險,分析如下:

  1. 由於是寫後日志,如果在命令執行成功之後,在日誌未寫入磁碟之前伺服器突然當機,那重啟恢復資料的時候,這部分的資料肯定在日誌檔案中不存在了,那麼將會丟失。(無法通過後臺資料庫恢復的情況下)
  2. 雖然不會阻塞當前命令的執行,由於記錄日誌也是在主執行緒中(Redis是單執行緒),如果日誌寫入磁碟的時候突然阻塞了,肯定會影響下一個命令的執行。

為了解決上面的風險,AOF日誌提供了三種回寫策略。

三種寫回策略

AOF機制提供了三種回寫策略,這些都在appendfsync配置,如下:

  1. Always(同步寫回):命令執行完成,立馬同步的將日誌寫入磁碟
  2. Everysec(每秒寫回):命令執行完成後,先將日誌寫入 AOF 檔案的記憶體緩衝區,每隔一秒把緩衝區中內容寫入磁碟。
  3. No(作業系統控制的寫回):每個寫命令執行完,只是先把日誌寫到AOF檔案的記憶體緩衝區,由作業系統決定何時將緩衝區內容寫回磁碟。

其實這三中寫回策略都無法解決主執行緒的阻塞和資料丟失的問題,分析如下:

  1. 同步寫回:基本不丟失資料,但是每步操作都會有一個慢速的落盤操作,不可避免的影響主執行緒效能。
  2. 每秒寫回:採用一秒寫一次到 AOF 日誌檔案中,但是一旦當機還是會丟失一秒的資料。
  3. 作業系統控制的寫回:在寫完緩衝區之後則會寫入磁碟,但是資料始終在緩衝區的時間內一旦當機,資料還是會丟失。

以上三種策略優缺點總結如下表:

策略 優點 缺點
Always 可靠性高,資料基本不丟失 每個寫命令都要落盤,效能影響較大
Everysec 效能適中 當機時丟失一秒資料
No 效能好 當機時丟失資料較多

日誌檔案太大怎麼辦?

隨著資料量的增大,AOF日誌檔案難免會很大,這樣將會導致寫入和恢復資料都將變得非常慢。此時AOF提供了一種重寫機制解決這一問題。

重寫機制理解起來很簡單,即是Redis會建立一個新的AOF日誌檔案,將每個鍵值對最終的值用一條命令寫入日誌檔案中。

比如讀取了鍵值對key1:value1,重寫機制會在新的AOF日誌檔案中記錄如下一條命令:

set key1 value1

其實即是記錄多次修改的最終的值記錄在新的AOF日誌檔案中,這樣當恢復資料時可直接執行該命令。

為什麼重寫機制能夠縮小檔案呢? 當一個鍵值被多次修改後,AOF日誌檔案中將會記錄多次修改鍵值的命令,重寫機制是根據這個鍵值最新狀態為它生成寫入命令,這樣舊檔案中的多條命令在重寫後的新日誌中變成了一條命令。

作者畫了一張重寫流程圖,僅供參考,如下:

重寫機制流程
重寫機制流程

AOF重寫會阻塞主執行緒嗎?

AOF重寫雖然能夠縮減日誌檔案的大小,達到減少日誌記錄和資料恢復的時間,但是在資料量非常的大情況下把整個資料庫重寫後的日誌寫入磁碟是一個非常耗時的過程,難道不會阻塞主執行緒嗎?

答案是:不會阻塞主執行緒; 因為AOF重寫過程是由後臺子程式bgrewriteaof來完成的,這也是為了避免阻塞主執行緒,導致資料庫效能下降。

其實重寫的過程分為兩個階段:一個拷貝,兩處日誌

一個拷貝:指每次執行重寫時,主執行緒都fork一個子執行緒bgrewriteaof,主執行緒會把記憶體資料拷貝一份到子執行緒,此時子執行緒中包含了資料庫的最新資料。然後子執行緒就能在不影響主執行緒的情況下進行AOF重寫了。

兩處日誌是什麼?如下:

  1. 第一處日誌:子執行緒重寫並未阻塞主執行緒,此時主執行緒仍然會處理請求,此時的AOF日誌仍然正在記錄著,這樣即使當機了,資料也是齊全的。第一處日誌即是值主執行緒正在使用的日誌。
  2. 第二處日誌:指新的AOF重寫日誌;重寫過程中的操作也會被寫到重寫日誌緩衝區,這樣重寫日誌也不會丟失最新的操作。等到拷貝資料的所有操作記錄重寫完成後,重寫日誌記錄的這些最新操作也會寫入新的 AOF 檔案,以保證資料庫最新狀態的記錄。此時,我們就可以用新的 AOF 檔案替代舊檔案了。

總結Redis在進行AOF重寫時,會fork一個子執行緒(不會阻塞主執行緒)並進行記憶體拷貝用於重寫,然後使用兩個日誌保證重寫過程中,新寫入的資料不會丟失。

AOF的缺點

雖說進行了日誌重寫後,AOF日誌檔案會縮減很多,但是在資料恢復過程中仍然是一條命令一條命令(由於單執行緒,只能順序執行)的執行恢復資料,這個恢復的過程非常緩慢。

總結

AOF這種通過逐一記錄操作命令的日誌方式,提供了三種寫回策略保證資料的可靠性,分別是AlwaysEverysecNo,這三種策略在可靠性上是從高到低,而在效能上則是從低到高。

為了避免日誌檔案過大,Redis提供了重寫的機制,每次重寫都fork一個子執行緒,拷貝記憶體資料進行重寫,將多條命令縮減成一條生成鍵值對的命令,最終重寫的日誌作為新的日誌。

什麼是RDB?

RDB(Redis DataBase)是另外一種持久化方式:記憶體快照。

RDB記錄的是某一個時刻的記憶體資料,並不是操作命令。

這種方式類似於拍照,只保留某一時刻的形象。記憶體快照是將某一時刻的狀態以檔案的形式寫入磁碟。這樣即使當機了,資料也不會丟失,這個快照檔案就稱為RDB檔案。

由於記錄的是某個時刻的記憶體資料,資料恢復非常快的,不需要像AOF日誌逐一執行記錄的命令。

給哪些資料做快照?

為了保證資料的可靠性,Redis執行的全量快照,也就是把記憶體中的所有資料都寫到磁碟中。

隨著資料量的增大,一次性把全部資料都寫到磁碟中勢必會造成執行緒阻塞,這就關係到Redis的效能了。

針對執行緒阻塞的問題Redis提供了兩個命令,如下:

  1. save:在主執行緒中執行,會導致主執行緒阻塞。
  2. bgsavefork一個子程式,專門用於寫入RDB檔案,避免了主執行緒的阻塞,這是Redis的預設配置。

這樣就可以使用bgsave命令執行全量快照,既可以保證資料的可靠性也避免了主執行緒的阻塞。

快照時能夠修改資料嗎?

子執行緒執行全量快照的同時,主執行緒仍然在接受著請求,讀資料肯定沒有問題,但是如果個修改了資料,如何能夠保證快照的完整性呢?

舉個例子:我在T時刻進行全量快照,假設資料量有8G,寫入磁碟的過程至少需要20S,在這20S的時間內,一旦記憶體中的資料發生了修改,則快照的完整性就破壞了。

但是如果在快照時不能修改資料,則對Redis的效能有巨大的影響,對於這個問題,Redis是如何解決的呢?

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

其實很簡單,bgsave命令會fork一個子執行緒,這個子執行緒共享所有記憶體的資料,子執行緒會讀取主執行緒記憶體中的資料,將他們寫入RDB檔案。

寫時複製保證資料可修改
寫時複製保證資料可修改

如上圖,對於鍵值對A的讀取並不會影響子執行緒,但是如果主執行緒一旦修改記憶體中一塊資料(例如鍵值對D),這塊資料將會被複制一個副本,然後bgsave子執行緒會將其寫入RDB檔案。

多久做一次快照?

快照只是記錄某一時刻的資料,一旦時間隔離很久,則伺服器一旦當機,則會丟失那段時間的資料。

比如在T1時間做了一次快照,在T1+t時又做了一次快照,如果在t這個時間段內伺服器突然當機了,則快照中只儲存了T1時刻的快照,在t時間段內的資料修改未被記錄(丟失)。如下圖:

t時刻當機,未執行快照
t時刻當機,未執行快照

從上圖明顯可以看出,RDB並不是一個完美的日誌記錄方案,只有讓t時間逐漸縮小,才能保證丟失的資料縮小。

那麼問題來了,時間能夠縮短1秒嗎? 即是每秒執行一次快照。

全量快照是記錄某一個時刻的全部記憶體資料,每秒執行一次的對Redis效能影響巨大,於是增量快照就出來了。

增量快照

增量快照是指做了一次全量快照之後,後續的快照只對修改的資料進行快照記錄,這樣可以避免每次都全量快照的開銷。

增量快照的前提是Redis能夠記住修改的資料,這個功能其實開銷也是巨大的,需要儲存完整的鍵值對,這對記憶體的消耗是巨大的。

為了解決這個問題,Redis使用了AOFRDB混合使用的方式。

AOF和RDB混合使用

這個概念是在Redis4.0提出的,簡單的說就是記憶體快照以一定的頻率執行,比如1小時一次,在兩次快照之間,使用AOF日誌記錄這期間的所有命令操作。

混合使用的方式使得記憶體快照不必頻繁的執行,並且AOF記錄的也不是全部的操作命令,而是兩次快照之間的操作命令,不會出現AOF日誌檔案過大的情況了,避免了AOF重寫的開銷了。

這個方案既能夠用到的RDB的快速恢復的好處,又能享受都只記錄操作命令的簡單優勢,強烈建議使用。

總結

RDB記憶體快照記錄的是某一個時刻的記憶體資料,因此能夠快速恢復;AOFRDB混合使用能夠使得當機後資料快速恢復,又能夠避免AOF日誌檔案過大。

總結

本文介紹了兩種資料恢復和持久化的方案,分別是AOFRDB

AOF介紹了什麼?如下:

  1. AOF是寫後日志,通過記錄操作命令持久化資料。
  2. 由於AOF是在命令執行之後記錄日誌,如果在寫入磁碟之前伺服器當機,則會丟失資料;如果寫入磁碟的時候突然阻塞,則會阻塞主執行緒;為了解決以上問題,AOF機制提供了三種寫回的策略,每種策略都有不同的優缺點。
  3. AOF日誌檔案過大怎麼辦?AOF通過fork一個子執行緒重寫一個新的日誌檔案(共享主執行緒的記憶體,記錄最新資料的寫入命令),同時子執行緒重寫,避免阻塞主執行緒。

RDB介紹了什麼?如下:

  1. RDB是記憶體快照,記錄某一個時刻的記憶體資料,而不是操作命令。
  2. Redis提供了兩個命令,分別是savebgsave來執行全量快照,這兩個命令的區別則是save是在主執行緒執行,勢必會阻塞主執行緒,bgsave是在fork一個子執行緒,共享記憶體。
  3. RDB通過作業系統的寫時複製技術,能夠保證在執行快照的同時主執行緒能夠修改快照。
  4. 由於兩次快照之間是存在間隔的,一旦伺服器當機,則會丟失兩次間隔時刻的資料,Redis4.0開始使用AOF日誌記錄兩次快照之間執行的命令(AOFRDB混合使用)。

相關文章