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 文件。
-
*3
:表示當前指令分爲三個部分,每個部分都是$ +
數字開頭,後面是 3 部分的具體內容:指令、鍵、值。 -
數字:表示這部分的命令、鍵、值多佔用的字節大小。比如
$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 key1
、hdel key2
、srem keys
、set a111
等。重寫過程會識別並刪除這些無效命令,只保留最終數據的寫入命令,從而減小了文件大小。
3、合併多條寫命令:爲了進一步優化 AOF 文件的大小,重寫過程會將多條寫命令合併爲一個。例如,lpush list a
、lpush list b
、lpush 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 文件進行數據恢復。
流程如下:
-
當 AOF 和 RDB 文件同時存在時,優先加載 AOF
-
若關閉了 AOF(apendonly no),則加載 RDB 文件
-
加載 AOF/RDB 成功之後,redis 重啓成功。如果無相關的持久化,則直接啓動成功。
-
如果 AOF/RDB 數據恢復存在錯誤,則啓動失敗,並打印輸出錯誤信息
2.3 RDB 和 AOF 的比較和混合持久化
咱們上一篇介紹了《RDB 內存快照提供持久化能力》定點快照的用戶,那 RDB 跟 AOF 究竟孰優孰慮?
現實情況下,無論使用 RDB 或者 AOF 都差點意思。使用 rdb 來恢復內存狀態,勢必會丟失一部分數據。使用 AOF 日誌重放,重放對性能有一定的影響,而且在 Redis 實例很大的情況下,需要花費很長的時間。
Redis 4.0 解決了這個問題,才用了一個新的持久化模式——混合持久化,該 混合模式 默認是關閉狀態的。
將 RDB 文件的內容和 rdb 快照時間點之後的增量的 AOF 日誌文件存在一起。這時候 AOF 日誌不需要再是全量的日誌,而是最近一次快照時間點之後到當下發生的增量 AOF 日誌,通常這部分 AOF 日誌很小。
所以執行有如下順序:
-
查找 rdb 內容,如果存在先加載 rdb 內容再 重放剩餘的 aof。
-
沒有 rdb 內容,直接以 aof 格式重放整個文件。
這樣快照就不用頻繁的執行,同時由於 AOF 只需要記錄最近一次快照之後的數據,不需要記錄所有的操作,避免了出現單次重放文件過大的問題。
開啓混合持久化模式:
aof-use-rdb-preamble yes
這個設置告訴 Redis 在 AOF 重寫時使用混合持久化模式。當這個選項設置爲 yes 時,重寫後的 AOF 文件將包含 RDB 格式的數據前綴和 AOF 格式的增量修改操作。
總結
-
RDB 提供了快照模式,記錄某個時間的 Redis 內存狀態。RDB 設計了 bgsave 和寫時複製,儘可能避免執行快照期間對讀寫指令的影響,但是頻繁快照會給磁盤帶來壓力以及 fork 阻塞主線程。需把握頻率。
-
AOF 日誌存儲了 Redis 服務的順序指令序列,通過重放(replay)指令來寫入日誌文件,並通過寫回策略來避免高頻讀寫給 Redis 帶來壓力。
-
RDB 快照的照片時間間隔,必然會帶來數據缺失,如果允許分鐘級別的數據丟失,可以只使用 RDB。
-
如果只用 AOF,寫回策略優先使用 everysec 的配置選項,因爲它在可靠性和性能之間取了一個平衡。
-
數據不能丟失時,內存快照和 AOF 的混合使用是一個很好的選擇。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/sxIZO79lC2zkMajOHTivvA