redis 叢集常見問題 QA

ponponon發表於2023-04-21

資料分片

Q:redis 叢集是怎麼做資料分片的?

A:Redis叢集是透過資料分片(sharding)來實現資料在不同節點間的分配。Redis叢集中有16384個hash slot(槽位),每個節點可以處理其中的一部分槽位,不同節點之間可以共同處理這16384個槽位。當一個鍵值對需要被放到Redis叢集中時,Redis會先計算該鍵值對的key對應的hash值,然後將其放到對應的槽位中。Redis叢集的每個節點都知道自己負責處理哪些槽位,所以能夠快速定位資料在哪個節點上。

具體來說,Redis叢集使用雜湊槽分片(hash slot sharding)來將所有資料分成16384個雜湊槽。當客戶端連線Redis叢集時,會得到叢集中任意一個節點的IP和埠資訊,然後將客戶端的請求路由到正確的節點上。每個節點都負責處理一部分雜湊槽,這些雜湊槽的範圍透過分散式雜湊演演算法(比如CRC16)來計算,然後分配到不同的節點上。當一個客戶端請求需要操作某個key時,Redis會先根據key計算出該key對應的雜湊槽,然後將請求路由到負責該雜湊槽的節點上。這樣就實現了資料的分片儲存和訪問。

Q:一個槽位,可以被多個 redis 主節點共同持有嗎?

A:在 Redis 叢集中,一個槽位只能被一個 Redis 節點持有,即每個槽位只能被分配給 Redis 叢集中的一個主節點。在 Redis 叢集中,槽位會被均勻地分配給各個主節點,確保每個主節點都分配了一定數量的槽位,從而實現資料的分散式儲存。如果一個槽位被多個主節點持有,那麼可能會導致資料衝突和資料一致性問題。因此,Redis 叢集的設計保證了一個槽位只被一個主節點持有。

Q:一個 redis 主節點,怎麼知道自己持有哪些槽位?持有的槽位是離散的,還是連續的?

A:在 Redis 叢集中,一個 Redis 主節點會持有一部分槽位,具體是哪些槽位是由叢集的節點管理器指定的。一個槽位只會被分配給一個 Redis 主節點,而不會被多個主節點共同持有。每個 Redis 主節點都會記錄自己持有的槽位資訊,可以透過命令 CLUSTER SLOTS 來獲取當前節點所持有的槽位資訊,例如:

127.0.0.1:6379> CLUSTER SLOTS
1) 1) (integer) 0
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 6379
      3) "f2b5a96e2e79d08ce5d5a5b1d64b5a31f07a734f"
   4) 1) "127.0.0.1"
      2) (integer) 6380
      3) "92d24c0209384f4c4c9abef7adff0f129b73e7b6"
2) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 6380
      3) "92d24c0209384f4c4c9abef7adff0f129b73e7b6"
   4) 1) "127.0.0.1"
      2) (integer) 6379
      3) "f2b5a96e2e79d08ce5d5a5b1d64b5a31f07a734f"
...

上述命令返回了當前節點所持有的所有槽位資訊。其中,每個槽位由一個起始槽號和一個結束槽號組成,例如 (integer) 0 到 (integer) 5460 表示第一個槽位的起始編號為 0,結束編號為 5460。對於連續的槽位,它們的起始和結束編號是連續的,而對於離散的槽位,它們的編號是不連續的。

Q:客戶端怎麼知道應該把資料寫到 redis 叢集的哪個節點?

A:在 Redis 叢集中,客戶端可以使用雜湊槽位演演算法來確定應該將請求傳送到哪個節點。雜湊槽位演演算法將每個 key 對映到 16384 個槽位中的一個,並將每個槽位分配給叢集中的一個節點。當客戶端需要與叢集通訊時,它會將 key 對映到一個槽位,並向負責該槽位的節點傳送請求。如果請求需要訪問多個 key,則使用所有 key 的槽位來確定應該將請求傳送到哪個節點。

在 Redis 叢集中,每個節點都知道哪些槽位被分配給它。當一個節點收到一個請求,如果它不是負責該槽位的節點,則它會返回一個 MOVED 錯誤,告訴客戶端應該將請求傳送到負責該槽位的節點。

因此,客戶端透過雜湊槽位演演算法將 key 對映到節點,並使用 MOVED 錯誤來確定應該將請求傳送到哪個節點。

Q:redis 叢集的客戶端,怎麼知道哪個 redis server 節點負責哪個槽位?

A:Redis叢集的客戶端透過叢集中的後設資料(metadata)來獲取槽和節點之間的對映關係,這個後設資料儲存在所有的Redis節點上。當一個客戶端需要知道某個槽位的資訊時,它會向Redis叢集的其中一個節點傳送CLUSTER SLOTS命令,該節點會返回一個包含當前叢集中節點和槽位資訊的列表,客戶端透過解析這個列表就可以瞭解槽位和節點之間的對映關係。客戶端在獲取了這個對映關係後,就可以將請求傳送到正確的節點上。

Q:redis 叢集的後設資料一般會很大嗎?會佔用很多網路通訊資源嗎?

A:Redis 叢集的後設資料一般不會很大,因為它只包含了關於槽位與節點的對映資訊。在預設的情況下,Redis 叢集有 16384 個槽位,因此後設資料的大小與節點數目和槽位的數量成正比。當叢集中有成百上千個節點時,後設資料會變得比較大,但是它通常不會成為瓶頸。

關於網路通訊資源的佔用,後設資料的大小相對較小,因此它對網路通訊資源的佔用也較小。在 Redis 叢集中,每個節點都需要與其他節點交換後設資料資訊以保持整個叢集的狀態一致性,這可能會佔用一定的網路頻寬。但是,Redis 採用了一些最佳化措施,如增量同步和只同步變化的資訊等,以降低網路通訊資源的佔用。

Q:redis 叢集的客戶端,怎麼知道哪個 redis server 節點上線了或者下線了?怎麼更新元資訊?

A:Redis叢集的客戶端可以透過監聽cluster:slots、cluster:meet、cluster:forget等事件來感知Redis節點的上線、下線、新增、刪除等操作,並相應地更新叢集後設資料。

具體來說,客戶端可以使用Redis提供的SUBSCRIBE命令來訂閱cluster:slots、cluster:meet、cluster:forget等事件的通知,當Redis節點的狀態發生變化時,Redis會發布相應的事件通知給所有客戶端。客戶端接收到通知後,可以更新自己本地的叢集後設資料,以確保後續的操作可以正確地路由到相應的節點。

例如,Python客戶端redis-py-cluster提供了RedisCluster類來操作Redis叢集,可以使用該類的pubsub屬性來訂閱相應的事件通知,如下所示:

from rediscluster import RedisCluster

startup_nodes = [{"host": "127.0.0.1", "port": "7000"}]
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)

# 訂閱cluster:slots事件
pubsub = rc.pubsub(ignore_subscribe_messages=True)
pubsub.subscribe("__keyspace@0__:*")

# 監聽事件通知
for item in pubsub.listen():
    print(item)

上面的程式碼訂閱了__keyspace@0__:*頻道,即監聽所有key空間的事件通知。當有節點上線、下線、新增、刪除等操作時,Redis會發布相應的事件通知到該頻道,客戶端透過監聽該頻道,就可以更新自己本地的叢集後設資料。

Q:對於一個 redis 叢集,如果有一個 redis 主節點當機下線了,這個時候,客戶端往這個節點寫入資料怎麼辦?

A:如果一個 Redis 主節點當機下線了,客戶端往這個節點寫入資料的請求會失敗。為了避免這種情況,Redis 叢集提供了自動故障遷移(automatic failover)機制,使得當主節點當機下線後,叢集能夠自動將從節點升級為新的主節點,以保證叢集的高可用性。

當主節點當機下線後,叢集會檢測到該節點下線,然後從其它從節點中選出一個新的主節點。選舉新主節點的過程是透過叢集內的投票機制來實現的。首先,叢集會在所有從節點中選舉一個新主節點,然後將新主節點的資訊廣播給叢集中的所有節點。當客戶端向叢集傳送寫入請求時,叢集會自動將請求路由到新的主節點上,從而保證叢集的正常執行。

Q:Redis 叢集提供的"自動故障遷移"需要幾秒鐘?

A:Redis 叢集提供的自動故障轉移(Automatic Failover)可以在幾秒鐘內完成,具體時間取決於叢集的配置和網路延遲情況。在預設情況下,Redis 叢集會在 15 秒內嘗試執行故障轉移。這個時間可以透過 cluster-node-timeout 引數進行配置。如果某個主節點超過了設定的 cluster-node-timeout 時間沒有響應,那麼這個節點將被認為是當機了,Redis 叢集就會開始自動故障轉移過程。

在自動故障轉移過程中,Redis 叢集會選舉一個新的主節點來接管原來當機的節點的槽位,並將資料重新分佈到叢集中的其他節點。這個過程通常只需要幾秒鐘的時間。但是需要注意的是,在自動故障轉移期間,叢集中的某些操作可能會出現短暫的不可用性,直到轉移過程完成。

Q:那這 15 秒內,客戶端都無法往這些槽寫入資料了,對嗎?

A:是的,如果發生故障遷移,需要在遷移完成之前暫停向失效節點所持有的槽寫入資料,否則可能會造成資料的不一致性。在 Redis 叢集的故障遷移過程中,通常需要在數秒內完成。

資料副本

Q:redis 叢集的副本是怎麼分配的?

A:在 Redis 叢集中,每個主節點會被分配若干個從節點,每個從節點都會在執行時透過複製主節點的資料來保持與主節點的資料一致。在預設情況下,每個主節點會被分配一個從節點,可以透過增加主節點的數量來增加叢集的可用性和容錯性。Redis 叢集使用的是無中心架構,所以每個節點都會分配一個固定的槽位,對於任何給定的槽位,只會有一個主節點和一個或多個從節點負責處理。

當一個主節點下線時,其對應的從節點會被提升為新的主節點,從而確保叢集的高可用性和可靠性。Redis 叢集還支援對資料進行分片,從而將資料分佈在多個主節點之間,提高併發處理能力和可擴充套件性。

相關文章