vic
8 min readJul 24, 2021

初識 Redis Cluster Ep 1 : Redis Cluster 架構簡介 — 當 Redis 群聚在一起

前言:

Redis 是強大的快取資料庫,當某個流程的效能卡在 I/O 時你就會想起他,由於 Redis 將資料都暫存在記憶體,當資料量一大,記憶體不小心撐爆,會讓很多 Process Crush,若剛好 Redis 異步的備份流程還沒觸發時,寶貴的資料可能會遺失,因此 Redis 提供 Cluster 的機制,讓你可以將資料分散在多台Server。

Redis Cluster 系列文章:

Ep 1. Redis Cluster 架構

Ep 2. Key-Based Sharding

Ep 3. Cluster Message Protocol

Ep 4. Cluster Management

Ep 5. 手把手用 Docker-Compose 架一個 Redis Cluster

內文 - Redis Cluster 架構

根據官方文件以下的說明

So in practical terms, what do you get with Redis Cluster?

- The ability to automatically split your dataset among multiple nodes.

- The ability to continue operations when a subset of the nodes are experiencing failures or are unable to communicate with the rest of the cluster.

從上述我們可以知道 Redis Cluster 主要提供兩大功能:

  • Scalability : 幫你將資料分散在不同的機器上,即便資料量變大,你也可以透過橫向擴展來 Handle 大量資料。
  • Availability : 提供 Fail-Over 功能,即便某個機器掛掉了,不僅不會影響 Client 向 Cluster 讀寫資料,Client 還可在別台活著的機器上找到掛點機器的資料。

要完成上面兩大功能首先要思考以下問題:

  • 如何讓多台 Redis Server 知道彼此以及互相合作?
  • 將資料分散在不同 Server 的同時,Client 端如何不透過中心化的 Proxy Server 來讀寫 Redis 資料?
  • 當某台 Redis Server 掛點時,Cluster 如何讓 Client 端幾乎沒有任何等待也能讀寫到掛點 Server 的資料?

問題一:如何讓多台 Redis Server 知道彼此以及互相合作?

第一個問題之所以重要是因為,Redis Cluster 是採用 P2P 的網路溝通方式,沒有一個中央管理者,因此每個 Redis Server 必須知道 Cluster 內其他 Server 的狀態,並針對各種狀態作出反應 (e.g 當某個 Server 掛點時要做出處理)。

而說到 P2P 網路,就要提到大名鼎鼎的 Gossip 協定,Redis Cluster 之間的溝通就採用了 Gossip 協定,該協定的訊息傳輸方式就如同八卦,一傳十,十傳百,百傳千。

舉例來說,當 Server A 發現 Server B 有異樣的時候,Server A 不會馬上告訴 Cluster 中的所有人,而是先告訴自己附近的人:「欸欸,Server B 好像怪怪的欸,我一直按門鈴他都沒回應,」這時候收到這個訊息的其他人,會在把這個訊息推播給附近的人,達到最終一制性,雖然沒辦法立即一致,但好處是他不要求 Cluster 中每個 Node 都要彼此建立連線,當 Node 變多時,複雜性不會變高。

問題二:將資料分散在不同 Server 的同時,Client 端如何不透過中心化的 Proxy Server 來讀寫 Redis 資料?

既然 Cluster 之間是 P2P 的網路,那 Client 是否可以只透過 Cluster 中隨便一個 Server 就對整個 Cluster 的資料做讀寫,而不用額外弄一個中心化的 Proxy勒?沒問題,Redis 都幫你想好了!

首先介紹 Redis 將資料分散到不同 Server 的方式,Hash Slot,你可以把他想像成多個抽屜 (Slot),每個抽屜有一個編號 (Redis 提供 0~16383 ),而透過 Hash Function 可以把資料分配到不同的抽屜 (Slot)

由於 Redis 是 Key-Value 的儲存結構,每筆資料都有 Key 作為 Index,當我們把這個 Key 丟進一個 Hash Function (e.g CRC16) 就會出現一個 Hash Number,然後我們在把這個 Hash Number % 16384,就會得到該資料屬於的抽屜編號了,想不到 Hash 還可以這樣用吧 (我是想不到)。

在 Redis Cluster 初始化的時候,Redis 本人會問你說,有哪些 Server 要加進來,然後他會告訴你哪些 Server 負責哪些 Slot,並同步該資訊到每個 Server ,也就是說當 Client 拿著一個 Key 隨便問 Cluster 中的 Server 時,該 Server 只要執行

HASH_SLOT = CRC16(key) mod 16384

他就會知道該 Key 的 Slot ,最後在跟 Client 說,這個 Slot 的負責人是 XXX,你去問他 (有沒有很像政府單位在踢皮球)。

問題三:當某台 Redis Server 掛點時,Cluster 如何讓 Client 端幾乎沒有任何等待也能讀寫到掛點 Server 的資料?

樹大必有枯枝,Server 多必有 Crush ,為了保證某個 Server 掛點時,他裡面的資料不僅不會遺失,還能讓 Client 繼續讀寫,Redis 採用了~~~ (給你想三秒),沒錯 Master-Slave 的架構,這裡的 Master-Slave 不僅可以提供讀寫分離的功能,還自帶 Fail-Over,當 Master 掛點時,Slave 會自動補上。

Redis Cluster 中可以有多台 Master, Master負責 Hash Slot 的讀寫,而一個 Master 可以有一個到多個 Slave,Slave 主要負責備份以及讀取資料。

然而 Redis 的 Master-Slave 採用異步 Sync 的方式,所以有可能你剛寫進 Master 的資料還沒同步給 Slave 時,Master 就掛了,雖然說這是為了效能考量,畢竟如果你每次寫入都要等 Master 同步完 Slave 那就等於多了一層 Network 傳輸的時間,但 Redis 還是提供了一些方法讓你的寫入更加可靠。

方法一:使用 WAIT Command,在下完寫入指令後,可以連帶使用 WAIT 指令,參數帶 Slave 的數量以及 timeout 的時間,來等待 Master 確實收到 Slave 同步完資料的 ACK 。貼心提醒,在寫入後接 WAIT 指令時可以使用 MULTI EXEC 將兩個指令封裝在一個 transaction 內。

方法二:cluster-node-timeout & cluster-slave-validity-factor 設定,有時候 Client 一個衰,會把寫資料到一個已經脫離 Cluster 的 Server 上,cluster-node-timeout 參數設定一個 Server 無法被其他 Server Reach 多長時間後就要被 Slave 替代,也就說設定越短的 node-timeout,Client 衰的機率越低,此外,在選擇替代的 Slave 也要選靠譜一點的,可不能選到某個 Slave 跟 Master 的連線不穩定,導致很多資料都沒同步到,於是 cluster-slave-validity-factor 可用來診斷一個 Slave 是否有資格成為 Master,如果一個 Slave 與 Master 之間 timeout 的時間大於cluster-slave-validity-factor * cluster-node-timeout ,拍謝,該 Slave 就從 Master 的候選人名單排除。

總結

  • Redis Cluster 中 Server 透過 Gossip 協定來了解彼此的狀況,如果發生任何問題,要做對應的處理,例如當發現某台 Server 掛點,要選出他某個 Slave 來替代他。
  • Redis Cluster 透過 Hash Slot 方式將不同 Key 的資料經過 Hash Function 分配到不同的 Slot,而不同的 Slot 會在 Cluster 初始化時,分配給不同 Server,Client 可以透過 Cluster 中隨便一台 Server 來知道資料讀寫的位置。
  • Redis Cluster 提供 Master-Slave 架構,不僅可讀寫分離還可以 Fail-Over 提供資料可用性,但要小心資料還沒 Sync 到 Slave,Master 就掛掉的問題。

下集預告

這集簡單地介紹了一下 Redis Cluster 架構以及其功能,下集會針對 Hash Slot 的機制在做深入一點點的說明,例如 Hash Slot 跟 Consistent Hashing 有什麼差別,Hash Slot 的好處在哪,如果 Key 都分散在不同的 Server 上,那 Transaction 或 Lua Script 有對多個 Key 的操作該怎麼辦?

參考資料