Redis In Production (Persistence, Availability, Cluster)

2024-12-11

筆記關於 Redis 在正式開發環境上所要考量的事項,包含 Persistence 的規劃、Availability 高可用性的設計以及 Cluster 叢集部署的分散式架構等。

logo

Redis Persistence

Prewarm 預熱的概念,是在 Redis 伺服器啟動時,將資料從硬碟載入到記憶體中,這樣可以加快 Redis 的啟動速度,避免在啟動時,因為資料量過大,導致 Redis 啟動時間過長。

Redis 所提供的 Persistence 有兩種方式,分別是 RDB (快照) 與 AOF (異動日誌),這兩種方式可以同時使用,也可以只使用其中一種。實務上會同時使用 RDB 與 AOF,這樣可以在 Redis 啟動時,使用 RDB 來快速載入資料,而 AOF 則是用來保證資料的完整性。

總結而言透過結合 RDB 與 AOF,讓 Redis 的資料會進行備份,儘量減少資料的遺失程度,並且在重新啟動時快速載入。

RDB

優點

  • RDB 是將記憶體中的資料快照到硬碟中,所以在資料恢復時,速度會比 AOF 快
  • RDB 是單檔,所以可以複製到其他地方,進行備份
  • RDB 支援 Partial Resynchronization

缺點

  • RDB 是週期性的快照,所以在 Redis 發生當機時,可能會有資料遺失的情況
  • RDB 頻繁進行快照時,會影響使用者端的效能

Partial Resynchronization 是 Redis 複製功能中的一個過程。當一個從節點(replica,又稱 slave)與主節點(master)斷開連接後重新連接時,它只需要同步在斷開期間缺少的資料,而不需要重新載入主節點的所有資料。

如果要減少 RDB 在備份的影響,可以改使用 BGSAVE 指令,這樣可以在背景進行快照,減少影響使用者端的效能,可以將 BGSAVE 理解為非同步的 SAVE

執行 BGSAVE 時,子進程在調用 fork() 的瞬間會獲得資料的快照。子進程將這個快照寫入 RDB 檔案。因此,在 BGSAVE 過程中主進程所做的任何異動,都不會包含在該次生成的 RDB 檔案中。

運作邏輯與 CoW (Copy on Write) 的概念相關。

Copy-on-write(CoW) 是一種優化資料複製的策略。當需要複製資料時,系統並不立即複製整個資料,而是讓多個進程共享相同的記憶體頁面。只有當其中一個進程嘗試修改共享的資料時,操作系統才會實際進行複製,為該進程創建資料的副本。這種方法可以減少資源使用,提高效率,特別是在處理大型資料集時。CoW 的主要優點包括延遲資料複製直到修改發生,從而節省記憶體並提高效能。它常應用於檔案系統、虛擬記憶體、資料庫系統、網路通訊以及容器技術中。

在 Redis 的 BGSAVE 操作中,應用了 CoW 技術以提高效率。當 Redis 執行 BGSAVE 時,主進程會調用 fork() 系統呼叫,創建一個子進程。子進程負責將當前資料庫的快照(RDB 檔案)寫入磁碟,而主進程則繼續處理客戶端的請求。由於採用了 CoW 機制,父子進程最初共享相同的記憶體。當主進程處理寫操作並修改資料時,只有被修改的記憶體頁面才會被複製,這樣可以最大限度地節省記憶體資源,減少對系統效能的影響。因此,CoW 技術使得 Redis 能夠在不影響服務的情況下高效地執行資料備份操作,特別適合於資料量大且需要高可用性的實務專案。

  1. 主進程調用 fork():執行 BGSAVE 後 Redis 主進程調用 fork(),創建一個子進程。
  2. 父子進程共享記憶體:在 fork() 之後,父子進程共享相同的記憶體頁面。
  3. 子進程開始寫入 RDB 檔案:子進程將共享的記憶體資料讀取並寫入到 RDB 檔案中。
  4. 主進程繼續處理請求:主進程繼續處理客戶端的讀寫請求。
  5. CoW 機制介入(若有寫操作):
    • 寫操作發生: 如果主進程有新的寫操作,需要修改現有的記憶體頁面。
    • 頁面複製: 操作系統會將被修改的頁面複製一份給主進程,子進程保持對原始頁面的訪問 (因此新的寫操作不會包含到子進程的備份)。
    • 繼續操作: 主進程在新的頁面上進行寫操作,不影響子進程的備份。
  6. 子進程完成備份:子進程將所有資料寫入 RDB 檔案後,向主進程發送完成訊息,然後退出。

AOF

AOF 是將 Redis 的操作記錄到一個日誌檔案中,這樣可以保證 Redis 在當機時,可以透過 AOF 檔案來進行資料恢復。AOF 使用 Append-Only 的方式來進行操作,透過循序寫入的方式,達成高效能的操作。

優點

  • AOF 是將 Redis 的操作記錄到一個日誌檔案中,所以在資料恢復時,可以保證資料的完整性
  • AOF 是可以進行 Append-Only 的操作,所以在寫入時,不會影響使用者端的效能

缺點

  • AOF 的日誌檔案會隨著操作的增加而增加,所以在資料恢復時,會比 RDB 慢 🐢
模式 說明
always 每次操作都會寫入 AOF 檔案,遺失資料的機率最低
everysec 每秒寫入一次 AOF 檔案,遺失資料的機率較低
no 不寫入 AOF 檔案,效能最好,但遺失資料的機率最高

使用 aof-use-rdb-preamble 設定可以在 AOF 檔案中加入 RDB 的資料,有可用 RDB 檔案時,搭配 AOF 來進行資料恢復,有效減少資料恢復的時間。

Redis Availability

Redis 的高可用性模式可以在單機的架構下建置部署。Redis 的高可用性模式分為 Master(Primary) 與 Slave(Replica) 兩個角色,主要的讀寫的動作作業在 Master 上進行,Slave 則是將 Master 的資料複製一份,並且在 Master 當機時,Slave 可以自動切換為 Master,以達到高可用性的目的。

資料抄寫的方式包含同步 (Sync) 與非同步 (Async),同步的方式是在 Master 寫入資料後,Slave 會進行資料的抄寫,這個方式可以保證資料的一致性,但是會影響效能。非同步的方式是在 Master 寫入資料後,Slave 會在一段時間後進行資料的抄寫,這個方式可以提升效能,但是可能會有資料的不一致性的問題。Sync 稱為 Strong Consistency,Async 稱為 Eventual Consistency。

Redis 預設上僅支援 Async 的方式,所以在 Redis 的高可用性模式下,可能會有資料的不一致性問題。

Redis 沒有仲裁者 (Arbiter) 的概念,而是透過 Sentinel (哨兵) 來判斷 Master 是否當機,進行 Failover 切換 Replica 為 Master。也可以使用手動 (Manual) 的方式來進行 Failover。

此外 Redis 的 Replica 也可以作為讀寫分離 (Read-Write Separation) 架構方式來使用,這樣可以分散 Master 的讀取壓力。

Lab

Replica 的設定檔案會有以下不同

port 6381
pidfile "/tmp/redis_6381.pid"
logfile "/home/redis/redis_6381.log"
dbfilename "dump_6381.rdb"
appendfilename "appendonly_6381.aof"

一次啟動多個 Redis Instance

redis-server redis_6379.conf
redis-server redis_6380.conf
redis-server redis_6381.conf

確認 Redis Instance 是否正常運作

ps aux | grep 'redis-server'

要設定 Replica 為 Slave 的方式,就是登入該 Redis Instance 中,使用 replicaof 指令

redis-cli -p 6380
replicaof 127.0.0.1 -p 6379
exit
redis-cli -p 6381
replicaof 127.0.0.1 -p 6380
exit

完成設定後,可以透過 info replication 或者是 role 指令來查看 Replica 的狀態

info replication
role

Redis 單機 HA 的方式算是容易理解的學習方式,但是在實務上,可能會使用 Redis Cluster 的方式來進行高可用性的架構,才能夠達到備援與水平擴展並且提升效能的目的。

Replica 相關設定

replicaof <masterip> <masterport> # 預先在設定檔,指定 Replica 的 Master
replica-priority 100 # 設定 Replica 的優先權,數字越小,優先權越高,但數值 0 表示不接受提升為 Master
replica-serve-stale-data yes # 是否在 Master 當機時,允許 Replica 提供舊資料
replica-read-only yes # 是否在 Master 當機時,允許 Replica 提供讀取資料
repl-diskless-sync no # 是否在 Replica 進行同步時,只透過網路傳輸(或記憶體)而不使用硬碟,速度較快,但無法重複使用在其他 Replica
min-replicas-to-write 1 # 如果 Replica 數量大於等於這個數字,Master 才能進行寫入操作

Replica priority 數字越小優先權越高,但數值 0 表示不接受提升為 Master。

Redis Sentinel

有獨立的 config 設定,包含以下資料

port 26379
daemonize yes
logfile "/home/redis/sentinel_26379.log"
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 2 # 代表 Master 當機時,需要 2 個 Sentinel 同意才能進行 Failover
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

啟動 Sentinel

redis-sentinel redis-sentinel_26379.conf
redis-sentinel redis-sentinel_26380.conf
redis-sentinel redis-sentinel_26381.conf

Redis Cluster

Redis Cluster 是 Redis 提供的一種分散式的架構,可以透過 Redis Cluster 來提供高可用性、水平擴展、負載平衡的功能。Redis Cluster 是透過分散式的方式來儲存資料,並且在節點數量增加時,可以自動進行資料的重新分配,達到負載平衡的效果。

Redis Cluster 的節點,不需要透過 Proxy 以及 Async Replication 以及進行 Merge,每個節點都是獨立的。

Cluster 最少需要 3 個 主節點 (Master)。

叢集之間的節點溝通,沒有透過 Paxos 或 Raft 這類的共識演算法,而是透過 Gossip Protocol 來進行節點間的通訊。是透過去中心化、分散式的方式來進行節點間的通訊,類似於 Cassandra 與 Akka 等系統。

Gossip Protocl 包含 Meet, Ping, Pong 與 Fail 四種訊息,透過這四種訊息來進行節點間的通訊。

Redis 的 Cluster 主節點數量建議小於 1000 個,否則在效能上會有影響。

Key Hash Model

Redis 的 key hash model 是 Redis Cluster 中用來決定每個 key 應該儲存在哪個節點的機制。這個模型使用一致性雜湊(Consistent Hashing)來分配 key 到不同的節點上,以達到負載平衡和高可用性。

在 Redis Cluster 中,整個 key 空間被劃分成 16384 個槽(slots),每個槽可以被分配給不同的節點。當一個 key 被插入到 Redis Cluster 時,Redis 會使用 CRC16 演算法來計算該 key 的雜湊值,然後將這個雜湊值對 16384 取模,得到一個槽號。這個槽號決定了該 key 應該儲存在哪個節點上。

Cluster Conf

Redis Cluster 的設定檔案,可以透過以下方式來設定

appendonly yes
appendfilename "appendonly-6000.aof"
bind 192.168.100.150
cluster-enabled yes
cluster-config-file node_6000.conf
cluster-node-timeout 500
daemonize yes
dbfilename dump_6000.rdb
logfile "/home/redis/redis-cluster_6000.log"
dir "/home/redis/redis-data"
pidfile "/tmp/redis-cluster-6000.pid"
port 6000
protected-mode no

Cluster 的操作

redis-server redis-cluster_6000.conf
redis-server redis-cluster_6001.conf
redis-server redis-cluster_6002.conf

透過 redis-cli 來進行 Cluster 的操作

redis-cli -p 6000
cluster meet 192.168.100.150 6001
cluster meet 192.168.100.150 6002

# 分配 slots
redis-cli -p 6000
cluster addslotsrange 0 5000
redis-cli -p 6001
cluster addslotsrange 5001 10000
redis-cli -p 6002
cluster addslotsrange 10001 16383

在建立叢及並且分配好 slots 之後,client 連線可以加上參數 -c 來進行 Cluster 的操作

redis-cli -p 6000 -c

好處在於當要操作 GETSET 時,可以不用指定節點,Redis 會自動幫你轉發到正確的節點上。