Redis之Redis Cluster原理
簡介
1)高效能和線性擴充套件,最大可以支撐到1000個節點;Cluster架構中無Proxy層,Master與slave之間使用非同步replication,且不存在操作的merge。(即操作不能跨多個nodes,不存在merge層)
2)一定程度上保證writes的安全性,需要客戶端容忍一定程度的資料丟失:叢集將會盡可能(best-effort)儲存客戶端write操作的資料;通常在failover期間,會有短暫時間內的資料丟失(因為非同步replication引起);當客戶端與少數派的節點處於網路分割槽時(network partition),丟失資料的可能性會更高。(因為節點有效性檢測、failover需要更長的時間)
3)可用性:只要叢集中大多數master可達、且失效的master至少有一個slave可達時,叢集都可以繼續提供服務;同時“replicas migration”可以將那些擁有多個slaves的master的某個slave,遷移到沒有slave的master下,即將slaves的分佈在整個叢集相對平衡,盡力確保每個master都有一定數量的slave備份。
和單點操作變化
Cluster不能進行跨Nodes操作,也沒有nodes提供merge層代理。
Cluster中實現了一個稱為“hash tags”的概念,每個key都可以包含一個自定義的“tags”,那麼在儲存時將根據tags計算此key應該分佈在哪個nodes上(而不是使用key計算,但是儲存層面仍然是key);此特性,可以強制某些keys被儲存在同一個節點上,以便於進行“multikey”操作,比如“foo”和“{foo}.student”將會被儲存在同一個node上。不過在人工對slots進行resharding期間,multikey操作可能不可用。
在Cluster環境下,將不支援SELECT命令,所有的key都將儲存在預設的database中。
客戶端與Server角色
叢集中nodes負責儲存資料,保持叢集的狀態,包括keys與nodes的對應關係(內部其實為slots與nodes對應關係)。nodes也能夠自動發現其他的nodes,檢測失效的節點,當某個master失效時還應該能將合適的slave提升為master。
為了達成這些行為,叢集中的每個節點都通過TCP與其他所有nodes建立連線Nodes之間使用gossip協議(參見下文備註)向其他nodes傳播叢集資訊,以達到自動發現的特性,通過傳送ping來確認其他nodes工作正常,也會在合適的時機發送叢集的資訊。當然在Failover時(包括人為failover)也會使用Bus來傳播訊息。
Nodes之間使用gossip協議(參見下文備註)向其他nodes傳播叢集資訊,以達到自動發現的特性,通過傳送ping來確認其他nodes工作正常,也會在合適的時機發送叢集的資訊。當然在Failover時(包括人為failover)也會使用Bus來傳播訊息。
理論上,Client可以將請求傳送給任意一個nodes,然後根據在根據錯誤資訊轉發給合適的node,客戶端可以不用儲存叢集的狀態資訊,當然這種情況下效能比較低效,因為Client可能需要2次TCP呼叫才能獲取key的結果,通常客戶端會快取叢集中nodes與slots的對映關係,並在遇到“Redirected”錯誤反饋時,才會更新本地的快取。上,Client可以將請求傳送給任意一個nodes,然後根據在根據錯誤資訊轉發給合適的node,客戶端可以不用儲存叢集的狀態資訊,當然這種情況下效能比較低效,因為Client可能需要2次TCP呼叫才能獲取key的結果,通常客戶端會快取叢集中nodes與slots的對映關係,並在遇到“Redirected”錯誤反饋時,才會更新本地的快取。
slave選舉與提升
當slave發現自己的master變為FAIL狀態時,便嘗試進行Failover,以期成為新的master。由於掛掉的master可能會有多個slave。Failover的過程需要經過類Raft協議的過程在整個叢集內達到一致, 其過程如下:
slave發現自己的master變為FAIL
將自己記錄的叢集currentEpoch加1,並廣播Failover Request資訊
其他節點收到該資訊,只有master響應,判斷請求者的合法性,併發送FAILOVER_AUTH_ACK,對每一個epoch只發送一次ack
嘗試failover的slave收集FAILOVER_AUTH_ACK
超過半數後變成新Master
廣播Pong通知其他叢集節點。
1資料分片
我們已經知道資料會按照key雜湊到不同的slot,而每個節點僅負責一部分的slot,客戶端根據slot將請求交給不同的節點。將slots劃分給不同節點的過程稱為資料分片,對應的還可以進行分片的重新分配。這部分功能依賴外部呼叫命令:
CLUSTER ADDSLOTS slot [slot ...]
遷移:
- 對target執行CLUSTER SETSLOT slot IMPORTING [node-id],target節點將對應slots記為importing狀態;
- 對source執行CLUSTER SETSLOT MIGRATING[node-id],source節點將對應slots記為migrating狀態,與importing狀態一同在之後的請求重定向中使用
- 獲取所有要遷移slot對應的keys,CLUSTER GETKEYSINSLOT slot count
- 對source 執行MIGRATE host port key db timeout REPLACE [KEYS key [key …]]
- MIGRATE命令會將所有的指定的key通過RESTORE key ttl serialized-value REPLACE遷移給target
- 對所有節點執行CLUSTER SETSLOT slot NODE [node-id],申明target對這些slots的負責,並退出importing或migrating
2.請求重定向
由於每個節點只負責部分slot,以及slot可能從一個節點遷移到另一節點,造成客戶端有可能會向錯誤的節點發起請求。因此需要有一種機制來對其進行發現和修正,這就是請求重定向。有兩種不同的重定向場景:
1.MOVE已經被移走了
返回CLUSTER_REDIR_MOVED錯誤,和正確的節點。
客戶端向該節點重新發起請求,注意這次依然又發生重定向的可能。
2.ASK
key當前在migraging狀態,返回CLUSTER_REDIR_ASK,和importing該key的節點。客戶端向新節點發送ASKING,之後再次發起請求
新節點對傳送過ASKING,且key已經migrate過來的請求進行響應
3.叢集的狀態檢查
Cluster中的每個節點都維護一份在自己看來當前整個叢集的狀態,主要包括:
當前叢集狀態
叢集中各節點所負責的slots資訊,及其migrate狀態
叢集中各節點的master-slave狀態
叢集中各節點的存活狀態及不可達投票
心跳
節點之間相互的心跳(PING,PONG,MEET)及其攜帶的資料是叢集狀態傳播最主要的途徑。
Redis節點會記錄其向每一個節點上一次發出ping和收到pong的時間。
心跳資料
Header,傳送者自己的資訊
所負責slots的資訊
主從資訊
ip port資訊
狀態資訊
Gossip,傳送者所瞭解的部分其他節點的資訊
ping_sent, pong_received
ip, port資訊
狀態資訊,比如傳送者認為該節點已經不可達,會在狀態資訊中標記其為PFAIL或FAIL