Redis 穩定性之戰:AOF 日誌支撐數據持久化

1 介紹

AOF(Append Only File)持久化:以獨立日誌的方式存儲了 Redis 服務器的順序指令序列,並只記錄對內存進行修改的指令。
當 Redis 服務發生雪崩等故障時,可以重啓服務並重新執行 AOF 文件中的指令達到恢復數據的目的。也就是說,通過重放(replay),來重新建立 Redis 當前實例的內存數據結構。這種模式有沒有很熟悉,可以聯想到 MySQL 主從同步時的 relay log。
相對於咱們上一篇介紹的《RDB 內存快照提供持久化能力》定點快照的做法,AOF 的主要作用是解決了數據持久化的實時性,並且已經是 Redis 持久化的主流方式。

2 AOF 實現日誌記錄

2.1 開啓 AOF 日誌記錄

1、 開啓 AOF 日誌記錄:在 redis.conf 文件中,找到 APPEND ONLY MODE 設置

appendonly yes  # 默認不開啓, 爲 no

2、配置默認文件名:在 redis.conf 文件中設置

appendfilename “appendonly.aof”

2.2 執行流程


流程如上圖所示,我們解析如下:

2.2.1 將所有的寫命令(set、hset)Append 到 aof_buf 緩衝區中

Redis 接收到 set keyName someValue 命令的時候,會先將數據寫到內存,Redis 會按照如下格式寫入 AOF 文件。

  1. *3:表示當前指令分爲三個部分,每個部分都是 $ + 數字開頭,後面是 3 部分的具體內容:指令、鍵、值。

  2. 數字:表示這部分的命令、鍵、值多佔用的字節大小。比如 $3表示這部分包含 3 個字符,也就是 set 的長度。

我們看看一個典型的 aof 文件示例,爲了清晰表示,下面的註釋都是手動加的:

[root@localhost bin]#vim appendonly.aof
#  執行 set key value
*3
$3           # 這邊代表set命令,長度爲3
set
$9 
user_name      # 這邊代表keyName,長度爲9
$5 
brand      #  這邊代表keyValue,長度爲5
# 執行 mset key1 1 ,key2 2 ,key33 3
# aof日誌如下:
*7  # 本批命令需要往下讀7行非 $ 開始的命令
$4  #接着讀取4個字節寬度,‘mset’長度爲4,記爲 $4
mset
$4  #接着讀取4個字節寬度,‘key1’長度爲4,記爲 $4
key1
$1  #接着讀取1個字節寬度,‘1’長度爲1,記爲 $1
1
$4
key2
$1
2
$5  #接着讀取的字節寬度,‘$key33’長度爲5,記爲 $5
key33
$1
3

2.2.2 AOF 緩衝區根據策略向硬盤做 sync 同步

AOF 爲什麼把命令 append 到 aof_buf 中,然後再進行同步?
這是因爲 Redis 使用單進程響應命令(參考筆者這篇《深刻理解高性能 Redis 的本質》),如果每次寫 AOF 文件命令都直接持久化到硬盤,那麼操作會是不是被間斷,且性能完全取決於硬盤 I/O 負載。這個跟 MySQL 就沒啥區別了。
先寫入緩衝區 aof_buf 中,Redis 可以提供多種緩衝區同步硬盤的策略,在性能、安全、數據可靠性方面做出平衡。

同步策略需關注以下幾個配置:

1、 appendfsync 模式

appendfsync always  # 接受寫命令後立即寫入磁盤,強持久化但執行慢,不推薦
appendfsync everysec # 每秒寫入磁盤一次, 性能和持久化方面做了折中, 推薦
appendfsync no  #  依賴操作系統自身同步的配置和策略,性能較佳,但是沒法保證實時和完全持久化

2、no-appendfsync-on-rewrite
在 AOF 重寫期間是否禁用 fsync。這可以提高重寫性能,但可能會增加數據丟失的風險。

# 默認值:no
# 可選值:yes 或 no
no-appendfsync-on-rewrite yes

2.2.3 AOF 文件 Rewrite 實現壓縮

隨着 AOF 文件越來越大,需要定期對 AOF 文件進行重寫,達到壓縮減負的目的,避免 AOF 文件過大導致性能和數據可靠性問題。
重寫後的 AOF 文件變小的原因主要有以下幾點:
1、進程內已超時的數據不再寫入:在重寫過程中,Redis 不會將已經超時的數據寫入新的 AOF 文件,這有助於減少不必要的數據記錄。
2、刪除無效命令:舊的 AOF 文件中可能包含無效的命令,如del key1hdel key2srem keysset a111等。重寫過程會識別並刪除這些無效命令,只保留最終數據的寫入命令,從而減小了文件大小。
3、合併多條寫命令:爲了進一步優化 AOF 文件的大小,重寫過程會將多條寫命令合併爲一個。例如,lpush list alpush list blpush list c可以合併爲lpush list a b c。這種合併減少了命令的數量,進而減小了 AOF 文件的大小。
4、防止單條命令過大:對於某些操作類型(如 list、set、hash、zset),爲了防止單條命令過大造成客戶端緩衝區溢出,重寫過程會以 64 個元素爲界拆分多條命令。雖然這在一定程度上可能增加了命令的數量,但它確保了每條命令的大小都在可控範圍內,有助於維持整體文件大小的合理性。
總之 AOF 重寫降低了文件佔用空間,同時提升加載性能,因爲更小的 AOF 文件可以更快地被 Redis 加載。

AOF 重寫關注以下配置:
1、auto-aof-rewrite-percentage
觸發 AOF 重寫的增長百分比。例如,如果當前 AOF 文件大小是 100MB,並且這個值設置爲 100,那麼當 AOF 文件增長到 200MB 時,說明增長了 100%,Redis 會嘗試重寫 AOF。

# 默認值:`100`
`auto-aof-rewrite-percentage 100`

2、auto-aof-rewrite-min-size

AOF 文件的最小大小,以便觸發重寫。即使 AOF 文件的增長百分比超過了 auto-aof-rewrite-percentage 設置的值,但如果文件大小小於這個值,Redis 也不會觸發重寫。

# 默認值:`64mb`
auto-aof-rewrite-min-size 64mb

2.2.4 故障重啓時的數據恢復

當 Redis 服務器重啓時,可以加載 AOF 文件進行數據恢復。

流程如下:

  1. 當 AOF 和 RDB 文件同時存在時,優先加載 AOF

  2. 若關閉了 AOF(apendonly no),則加載 RDB 文件

  3. 加載 AOF/RDB 成功之後,redis 重啓成功。如果無相關的持久化,則直接啓動成功。

  4. 如果 AOF/RDB 數據恢復存在錯誤,則啓動失敗,並打印輸出錯誤信息

2.3 RDB 和 AOF 的比較和混合持久化

咱們上一篇介紹了《RDB 內存快照提供持久化能力》定點快照的用戶,那 RDB 跟 AOF 究竟孰優孰慮?
現實情況下,無論使用 RDB 或者 AOF 都差點意思。使用 rdb 來恢復內存狀態,勢必會丟失一部分數據。使用 AOF 日誌重放,重放對性能有一定的影響,而且在 Redis 實例很大的情況下,需要花費很長的時間。
Redis 4.0 解決了這個問題,才用了一個新的持久化模式——混合持久化,該 混合模式 默認是關閉狀態的。
將 RDB 文件的內容和 rdb 快照時間點之後的增量的 AOF 日誌文件存在一起。這時候 AOF 日誌不需要再是全量的日誌,而是最近一次快照時間點之後到當下發生的增量 AOF 日誌,通常這部分 AOF 日誌很小。
所以執行有如下順序:

開啓混合持久化模式:

aof-use-rdb-preamble yes

這個設置告訴 Redis 在 AOF 重寫時使用混合持久化模式。當這個選項設置爲 yes 時,重寫後的 AOF 文件將包含 RDB 格式的數據前綴和 AOF 格式的增量修改操作。

總結

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/sxIZO79lC2zkMajOHTivvA