Redis 分散式快取

📢 本文由 gemini-3-flash-preview 翻譯

Redis 基礎: https://blog.yexca.net/zh-tw/archives/157/
Redis 分散式快取: 本文

引言

所以這兩篇文章是同時寫的,但卻過了一年才發布是吧

其實是我當時有三個主題要寫,但每次想起來要看的時候都忘了要寫什麼,就過了快一年…

問題

單機 Redis 存在:

  • 資料遺失問題:實現 Redis 資料持久化
  • 並發能力問題:搭建主從叢集,實現讀寫分離
  • 儲存能力問題:搭建分片叢集,利用插槽機制實現動態擴容
  • 故障復原問題:利用 Redis 哨兵 (Sentinel),實現健康檢查和自動復原

Redis 持久化

Redis 持久化有兩種方案:RDB 與 AOF

RDB 持久化

RDB 全稱 Redis Database Backup file (Redis 資料備份檔案),也叫做 Redis 資料快照。簡單來說就是把記憶體中所有資料都記錄到硬碟中。當 Redis 執行個體故障重新啟動後,從硬碟讀取快照檔案,復原資料。快照檔案稱為 RDB 檔案,預設儲存在執行目錄。

RDB 會在以下四種情況下執行:

  • 執行 save 指令:立即執行,會導致主程序執行 RDB,其他所有指令被阻塞。僅在資料遷移時可能用到。
  • 執行 bgsave 指令:非同步執行,開啟獨立子程序完成 RDB,主程序可以持續處理用戶請求,不受影響。
  • Redis 關機時:關機時會執行一次 save 指令。
  • 觸發 RDB 條件時:於設定檔配置,如下:
1
2
3
4
# 900秒內,如果至少有1個key被修改,則執行bgsave , 如果是save "" 則表示停用RDB
save 900 1  
save 300 10  
save 60 10000 

其他設定:

1
2
3
4
5
6
7
8
# 是否壓縮,建議不開啟,壓縮也會消耗 CPU,硬碟空間相對便宜
rdbcompression yes

# RDB檔案名稱
dbfilename dump.rdb  

# 檔案儲存的路徑目錄
dir ./ 

RDB 原理

bgsave 開始時會 fork 主程序得到子程序,子程序共享主程序的記憶體資料。完成 fork 後讀取記憶體資料並寫入 RDB 檔案。

fork 採用的是 copy-on-write 技術:

  • 當主程序執行讀取操作時,存取共享記憶體。
  • 當主程序執行寫入操作時,則會複製一份資料,執行寫入操作。

image

RDB 的缺點:

  • 執行間隔時間長,兩次 RDB 之間寫入的資料有遺失的風險。
  • fork 子程序、壓縮、寫出 RDB 檔案都比較耗時。

AOF 持久化

AOF 全稱 Append Only File (追加檔案),Redis 處理的每一個寫入指令都會記錄在 AOF 檔案,可以看作指令日誌檔案。

AOF 預設是關閉的,修改設定檔開啟:

1
2
3
4
# 是否開啟AOF功能,預設是no
appendonly yes
# AOF檔案的名稱
appendfilename "appendonly.aof"

記錄頻率也可以透過 redis.conf 設定:

1
2
3
4
5
6
# 表示每執行一次寫入指令,立即記錄到AOF檔案
appendfsync always 
# 寫入指令執行完先放入AOF緩衝區,然後表示每隔1秒將緩衝區資料寫到AOF檔案,是預設方案
appendfsync everysec 
# 寫入指令執行完先放入AOF緩衝區,由作業系統決定何時將緩衝區內容寫回硬碟
appendfsync no

設定項比較:

設定項寫入硬碟時機優點缺點
always同步寫入可靠性高,幾乎不丟資料對效能影響大
everysec每秒寫入效能適中最多遺失 1 秒資料
no作業系統控制效能最好可靠性差,可能遺失大量資料

檔案重寫

因為是記錄指令,AOF 檔案會比 RDB 大很多,而且 AOF 會記錄對同一個 key 的多次寫入操作,但只有最後一次寫入操作才有意義。透過執行 bgrewriteaof 指令,可以讓 AOF 檔案執行重寫功能,用最少的指令達到相同效果。

假設原先指令為:

1
2
3
set num 123
set name jack
set num 666

重寫後:

1
mset name jack num 666

Redis 也會在觸發閾值時自動重寫 AOF 檔案,在設定檔配置:

1
2
3
4
# AOF檔案比上次檔案 增長超過多少百分比則觸發重寫
auto-aof-rewrite-percentage 100
# AOF檔案體積最小多大以上才觸發重寫 
auto-aof-rewrite-min-size 64mb 

RDB 與 AOF 比較

RDB 與 AOF 各有優缺點,如果對資料安全性要求極高,在實際開發中往往會結合兩者來使用。

RDBAOF
持久化方式定時對整個記憶體做快照記錄每一次執行的指令
資料完整性不完整,兩次備份之間會遺失相對完整,取決於寫入硬碟策略
檔案大小會進行壓縮,檔案體積小記錄指令,檔案體積很大
當機復原速度很快
資料復原優先級低,因為資料完整性不如 AOF高,因為資料完整性更高
系統資源佔用高,大量 CPU 和記憶體消耗低,主要是硬碟 I/O 資源
但 AOF 重寫時會佔用大量 CPU 和記憶體資源
使用場景可以容忍數分鐘的資料遺失,追求更快的啟動速度對資料安全性要求較高

Redis 主從架構

單節點 Redis 的並發能力是有上限的,要進一步提高 Redis 的並發能力,就需要搭建主從叢集,實現讀寫分離。

image

安裝叢集

基於 CentOS7

參考上圖一共三個節點,部署在同一台機器,連接埠為 7001(master)、7002、7003。

首先建立目錄:

1
2
cd /tmp
mkdir 7001 7002 7003

如需更改設定,需恢復預設的 RDB 模式:

1
2
3
4
5
6
7
8
# 開啟RDB
# save ""
save 3600 1
save 300 100
save 60 10000

# 關閉AOF
appendonly no

複製設定檔到每個執行個體目錄:

1
2
3
4
5
6
7
# 方式一
cp redis-6.2.4/redis.conf 7001
cp redis-6.2.4/redis.conf 7002
cp redis-6.2.4/redis.conf 7003

# 方式二
echo 7001 7002 7003 | xargs -t -n 1 cp redis-6.2.4/redis.conf

修改每個執行個體的連接埠,工作目錄(連接埠修改,rdb 檔案儲存位置修改):

1
2
3
sed -i -e 's/6379/7001/g' -e 's/dir .\//dir \/tmp\/7001\//g' 7001/redis.conf
sed -i -e 's/6379/7002/g' -e 's/dir .\//dir \/tmp\/7002\//g' 7002/redis.conf
sed -i -e 's/6379/7003/g' -e 's/dir .\//dir \/tmp\/7003\//g' 7003/redis.conf

修改 IP,每個目錄都要改 (替換 ip_address):

1
2
3
4
5
6
7
# 逐一執行
sed -i '1a replica-announce-ip ip_address' 7001/redis.conf
sed -i '1a replica-announce-ip ip_address' 7002/redis.conf
sed -i '1a replica-announce-ip ip_address' 7003/redis.conf

# 或者一鍵修改
printf '%s\n' 7001 7002 7003 | xargs -I{} -t sed -i '1a replica-announce-ip ip_address' {}/redis.conf

啟動:

1
2
3
4
5
6
# 第1個
redis-server 7001/redis.conf
# 第2個
redis-server 7002/redis.conf
# 第3個
redis-server 7003/redis.conf

停止:

1
printf '%s\n' 7001 7002 7003 | xargs -I{} -t redis-cli -p {} shutdown