redis學習手記(二)持久化方式

Edingbrugh.南空發表於2020-10-20

為什麼要將redis做持久化方式
一般 redis主要的運用場景為作為後端的快取層,redis中的資料都在記憶體,一旦伺服器當機,記憶體中的資料將全部丟失,所以有必要將資料做資料持久化。如果不做持久化,redis重新啟動的話,每次都要去資料庫讀取資料然後再次把資料回寫redis,容器造成資料壓力過大。另一個原因就是每次去讀取資料庫,這種是比較緩慢的一個過程,效能遠比不上從redis讀取出來的快,導致運用程式的卡頓。故實現持久化是有必要,它避免從後端資料庫中進行恢復。

redis持久化方式

  • AOF日誌
  • RDB快照

AOF日誌方式
aof日誌記錄的方式:是先執行命令,把資料寫入記憶體,然後才記錄日誌。這種就避免了執行命令有問題的時候也記錄進去日誌中不利於以後做日誌資料還原的(只記錄正確的執行命令)。

開啟AOF日誌功能
redis預設是不開啟aof功能,所以要使用aof功能,需要去配置檔案中把aof功能開啟方式如下:

# 預設是為no,所以需要的時候將appendonly 置為yes
appendonly yes

 # aof日誌檔案
appendfilename "appendonly.aof"

# 具體的寫入方式有以下三種方式,預設的為everysec(每秒寫回:每個寫命令執行完,
# 只是先把日誌寫到 AOF 檔案的記憶體緩衝區,每隔一秒把緩衝區中的內容寫入磁碟;)
# appendfsync always
appendfsync everysec
# appendfsync no

執行set keyTEST VALUE 在日誌的記錄表示為:*3”表示當前命令有三個部分,每部分都是由“$+數字”開頭,後面緊跟著具體的命令、鍵或值。這裡,“數字”表示這部分中的命令、鍵或值一共有多少位元組。

*3
$3
set
$7
keyTEST
$5
VALUE

AOF存在的問題

  • 如果剛執行完一個命令,還沒有來得及記日誌就當機了,那麼這個命令和相應的資料就有丟失的風險,還是會存在資料丟失的問題。
  • AOF 雖然避免了對當前命令的阻塞,但可能會給下一個操作帶來阻塞風險。(寫入磁碟的速度遠慢與記憶體對的操作)

AOF寫回三種策略

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

三種寫回策略都存在一定的問題,可以根據具體需求類選擇不同的寫回策略。
同步寫回”可以做到基本不丟資料,但是它在每一個寫命令後都有一個慢速的落盤操作,不可避免地會影響主執行緒效能。

作業系統控制的寫”在寫完緩衝區後,就可以繼續執行後續的命令,但是落盤的時機已經不在 Redis 手中了,只要 AOF 記錄沒有寫回磁碟,一旦當機對應的資料就丟失了;

“每秒寫回”採用一秒寫回一次的頻率,避免了“同步寫回”的效能開銷,雖然減少了對系統效能的影響,但是如果發生當機,上一秒內未落盤的命令操作仍然會丟失。所以,這隻能算是,在避免影響主執行緒效能和避免資料丟失兩者間取了個折中。

AOF日誌檔案過大怎麼辦?
檔案系統本身對檔案大小有限制,無法儲存過大的檔案;
如果檔案太大,之後再往裡面追加命令記錄的話,效率也會變低;
如果發生當機,AOF 中記錄的命令要一個個被重新執行,用於故障恢復,如果日誌檔案太大,整個恢復過程就會非常緩慢,這就會影響到 Redis 的正常使用。

解決aof檔案過大的問題
aof日誌檔案過大,採用重寫機制可以大大緩解這種問題,重寫的本質就是“多變一”功能。所謂的“多變一”,也就是說,舊日誌檔案中的多條命令,在重寫後的新日誌中變成了一條命令對於相同的執行命令只記錄最新的命令)。
重寫過程是由後臺執行緒 bgrewriteaof 來完成的,這也是為了避免阻塞主執行緒,導致資料庫效能下降。

重寫流程:
每次 AOF 重寫時,Redis 會先執行一個記憶體拷貝,用於重寫;然後,使用兩個日誌保證在重寫過程中,新寫入的資料不會丟失。而且,因為 Redis 採用額外的執行緒進行資料重寫,所以,這個過程並不會阻塞主執行緒。(主執行緒 fork 出後臺的 bgrewriteaof 子程式)

RDB快照方式
RDB是指:它實現類似照片記錄效果的方式,就是把某一時刻的狀態以檔案的形式寫到磁碟上,也就是快照。這樣一來,即使當機,快照檔案也不會丟失,資料的可靠性也就得到了保證。這個快照檔案就稱為 RDB 檔案,其中,RDB 就是 Redis DataBase 的縮寫。
和 AOF 相比,RDB 記錄的是某一時刻的資料,並不是操作,所以,在做資料恢復時,我們可以直接把 RDB 檔案讀入記憶體,很快地完成恢復

觸發快照的操作:
Redis 提供了兩個命令來生成 RDB 檔案,分別是 save 和 bgsave。save:在主執行緒中執行,會導致阻塞;bgsave:建立一個子程式,專門用於寫入 RDB 檔案,避免了主執行緒的阻塞,這也是 Redis RDB 檔案生成的預設配置。
預設配置如:

save 900 1
save 300 10
save 60 10000

需要注意的是使用bgsave 避免阻塞。其中避免阻塞和正常處理寫操作並不是一回事。此時,主執行緒的確沒有阻塞,可以正常接收請求,但是,為了保證快照完整性,它只能處理讀操作,因為不能修改正在執行快照的資料,所以修改操作的時候阻塞的。所以Redis 就會藉助作業系統提供的寫時複製技術(Copy-On-Write, COW),在執行快照的同時,正常處理寫操作(類似於java的CopyOnWriteArrayList一樣的思想)。bgsave 子程式是由主執行緒 fork 生成的,可以共享主執行緒的所有記憶體資料。bgsave 子程式執行後,開始讀取主執行緒的記憶體資料,並把它們寫入 RDB 檔案(如果主執行緒對這些資料也都是讀操作(例如圖中的鍵值對 A),那麼,主執行緒和 bgsave 子程式相互不影響。但是,如果主執行緒要修改一塊資料(例如圖中的鍵值對 C),那麼,這塊資料就會被複制一份,生成該資料的副本。然後,bgsave 子程式會把這個副本資料寫入 RDB 檔案,而在這個過程中,主執行緒仍然可以直接修改原來的資料。)

頻繁的執行快照方式導致什麼問題

  1. 頻繁將全量資料寫入磁碟,會給磁碟帶來很大壓力,多個快照競爭有限的磁碟頻寬,前一個快照還沒有做完,後一個又開始做了,容易造成惡性迴圈
  2. bgsave 子程式需要通過 fork 操作從主執行緒建立出來。雖然,子程式在建立後不會再阻塞主執行緒,但是,fork 這個建立過程本身會阻塞主執行緒,而且主執行緒的記憶體越大,阻塞時間越長。如果頻繁 fork 出 bgsave 子程式,這就會頻繁阻塞主執行緒了。

要解決頻繁的快照可以使用增量快照,所謂增量快照,就是指,做了一次全量快照後,後續的快照只對修改的資料進行快照記錄,這樣可以避免每次全量快照的開銷

資料不能丟失時,記憶體快照和 AOF 的混合使用使用兩種的混合模式

混合模式:
Redis 4.0 中提出了一個混合使用 AOF 日誌和記憶體快照的方法。簡單來說,記憶體快照以一定的頻率執行,在兩次快照之間,使用 AOF 日誌記錄這期間的所有命令操作。這樣一來,快照不用很頻繁地執行,這就避免了頻繁 fork 對主執行緒的影響。而且,AOF 日誌也只用記錄兩次快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現檔案過大的情況了,也可以避免重寫開銷。

實現混合模式的原理為:
fork出的子程式先將共享的記憶體副本全量的以RDB方式寫入aof檔案,然後在將重寫緩衝區的增量命令以AOF方式寫入到檔案,寫入完成後通知主程式更新統計資訊,並將新的含有RDB格式和AOF格式的AOF檔案替換舊的的AOF檔案。簡單的說:新的AOF檔案前半段是RDB格式的全量資料後半段是AOF格式的增量資料。

總結:

  • 資料不能丟失時,記憶體快照和 AOF 的混合使用是一個很好的選擇;
  • 如果允許分鐘級別的資料丟失,可以只使用 RDB;
  • 如果只用 AOF,優先使用 everysec 的配置選項,因為它在可靠性和效能之間取了一個平衡。

相關文章