什么是 Cluster 集群
Redis 集群是一種分布式數(shù)據(jù)庫方案塑悼,集群通過分片(sharding)來進(jìn)行數(shù)據(jù)管理(「分治思想」的一種實(shí)踐),并提供復(fù)制和故障轉(zhuǎn)移功能楷掉。
將數(shù)據(jù)劃分為 16384 的 slots厢蒜,每個節(jié)點(diǎn)負(fù)責(zé)一部分槽位。槽位的信息存儲于每個節(jié)點(diǎn)中烹植。
它是去中心化的斑鸦,如圖所示,該集群有三個 Redis 節(jié)點(diǎn)組成草雕,每個節(jié)點(diǎn)負(fù)責(zé)整個集群的一部分?jǐn)?shù)據(jù)巷屿,每個節(jié)點(diǎn)負(fù)責(zé)的數(shù)據(jù)多少可能不一樣。
三個節(jié)點(diǎn)相互連接組成一個對等的集群墩虹,它們之間通過 Gossip協(xié)議相互交互集群信息嘱巾,最后每個節(jié)點(diǎn)都保存著其他節(jié)點(diǎn)的 slots 分配情況。
集群安裝
一個 Redis 集群通常由多個節(jié)點(diǎn)(node)組成诫钓,在剛開始的時候旬昭,每個節(jié)點(diǎn)都是相互獨(dú)立的,它們都處于一個只包含自己的集群當(dāng)中菌湃,要組建一個真正可工作的集群问拘,我們必須將各個獨(dú)立的節(jié)點(diǎn)連接起來,構(gòu)成一個包含多個節(jié)點(diǎn)的集群。
連接各個節(jié)點(diǎn)的工作可以通過 CLUSTER MEET 命令完成:CLUSTER MEET <ip> <port> 骤坐。
向一個節(jié)點(diǎn) node 發(fā)送 CLUSTER MEET 命令绪杏,可以讓 node 節(jié)點(diǎn)與 ip 和 port 所指定的節(jié)點(diǎn)進(jìn)行握手(handshake),當(dāng)握手成功時纽绍,node 節(jié)點(diǎn)就會將 ip 和 port 所指定的節(jié)點(diǎn)添加到 node 節(jié)點(diǎn)當(dāng)前所在的集群中蕾久。
Cluster 實(shí)現(xiàn)原理
Redis 3.0 開始,官方提供了 Redis Cluster 方案實(shí)現(xiàn)了切片集群拌夏,該方案就實(shí)現(xiàn)了數(shù)據(jù)和實(shí)例的規(guī)則腔彰。Redis Cluster 方案采用哈希槽(Hash Slot,接下來我會直接稱之為 Slot)辖佣,來處理數(shù)據(jù)和實(shí)例之間的映射關(guān)系。
將數(shù)據(jù)分成多份存在不同實(shí)例上
集群的整個數(shù)據(jù)庫被分為 16384 個槽(slot)搓逾,數(shù)據(jù)庫中的每個鍵都屬于這 16384 個槽的其中一個卷谈,集群中的每個節(jié)點(diǎn)可以處理 0 個或最多 16384 個槽。
Key 與哈希槽映射過程可以分為兩大步驟:
1霞篡、根據(jù)鍵值對的 key世蔗,使用 CRC16 算法,計算出一個 16 bit 的值朗兵;
2污淋、將 16 bit 的值對 16384 執(zhí)行取模,得到 0 ~ 16383 的數(shù)表示 key 對應(yīng)的哈希槽余掖。
哈希槽與 Redis 實(shí)例映射
通過 cluster create
創(chuàng)建寸爆,Redis 會自動將 16384 個 哈希槽平均分布在集群實(shí)例上,比如 N 個節(jié)點(diǎn)盐欺,每個節(jié)點(diǎn)上的哈希槽數(shù) = 16384 / N 個赁豆。
可以使用 cluster addslots 命令,手動指定每個實(shí)例上的哈希槽個數(shù)冗美。
故障檢測
Redis 集群節(jié)點(diǎn)采用 Gossip 協(xié)議來廣播自己的狀態(tài)以及自己對整個集群認(rèn)知的改變魔种。比如一個節(jié)點(diǎn)發(fā)現(xiàn)某個節(jié)點(diǎn)失聯(lián)了 (PFail),它會將這條信息向整個集群廣播粉洼,其它節(jié)點(diǎn)也就可以收到這點(diǎn)失聯(lián)信息节预。
如果一個節(jié)點(diǎn)收到了某個節(jié)點(diǎn)失聯(lián)的數(shù)量 (PFail Count) 已經(jīng)達(dá)到了集群的大多數(shù),就可以標(biāo)記該節(jié)點(diǎn)為確定下線狀態(tài) (Fail)属韧,然后向整個集群廣播安拟,強(qiáng)迫其它節(jié)點(diǎn)也接收該節(jié)點(diǎn)已經(jīng)下線的事實(shí),并立即對該失聯(lián)節(jié)點(diǎn)進(jìn)行主從切換挫剑。
故障轉(zhuǎn)移
當(dāng)一個 Slave 發(fā)現(xiàn)自己的主節(jié)點(diǎn)進(jìn)入已下線狀態(tài)后去扣,從節(jié)點(diǎn)將開始對下線的主節(jié)點(diǎn)進(jìn)行故障轉(zhuǎn)移。
1、從下線的 Master 及節(jié)點(diǎn)的 Slave 節(jié)點(diǎn)列表選擇一個節(jié)點(diǎn)成為新主節(jié)點(diǎn)愉棱。
2唆铐、新主節(jié)點(diǎn)會撤銷所有對已下線主節(jié)點(diǎn)的 slot 指派,并將這些 slots 指派給自己奔滑。
3艾岂、新的主節(jié)點(diǎn)向集群廣播一條 PONG 消息,這條 PONG 消息可以讓集群中的其他節(jié)點(diǎn)立即知道這個節(jié)點(diǎn)已經(jīng)由從節(jié)點(diǎn)變成了主節(jié)點(diǎn)朋其,并且這個主節(jié)點(diǎn)已經(jīng)接管了原本由已下線節(jié)點(diǎn)負(fù)責(zé)處理的槽王浴。
4、新的主節(jié)點(diǎn)開始接收處理槽有關(guān)的命令請求梅猿,故障轉(zhuǎn)移完成氓辣。
跟哨兵類似,兩者都是基于 Raft 算法來實(shí)現(xiàn)的袱蚓,流程如圖所示:
客戶端如何定位數(shù)據(jù)所在實(shí)例
Redis 實(shí)例會將自己的哈希槽信息通過 Gossip 協(xié)議發(fā)送給集群中其他的實(shí)例钞啸,實(shí)現(xiàn)了哈希槽分配信息的擴(kuò)散。
這樣喇潘,集群中的每個實(shí)例都有所有哈希槽與實(shí)例之間的映射關(guān)系信息体斩。
在切片數(shù)據(jù)的時候是將 key 通過 CRC16 計算出一個值再對 16384 取模得到對應(yīng)的 Slot,這個計算任務(wù)可以在客戶端上執(zhí)行發(fā)送請求的時候執(zhí)行颖低。
但是絮吵,定位到槽以后還需要進(jìn)一步定位到該 Slot 所在 Redis 實(shí)例。
當(dāng)客戶端連接任何一個實(shí)例忱屑,實(shí)例就將哈希槽與實(shí)例的映射關(guān)系響應(yīng)給客戶端蹬敲,客戶端就會將哈希槽與實(shí)例映射信息緩存在本地。
當(dāng)客戶端請求時想幻,會計算出鍵所對應(yīng)的哈希槽粱栖,在通過本地緩存的哈希槽實(shí)例映射信息定位到數(shù)據(jù)所在實(shí)例上,再將請求發(fā)送給對應(yīng)的實(shí)例脏毯。
重新分配哈希槽
哈希槽與實(shí)例之間的映射關(guān)系由于新增實(shí)例或者負(fù)載均衡重新分配導(dǎo)致改變了,集群中的實(shí)例通過 Gossip 協(xié)議互相傳遞消息獲取最新的哈希槽分配信息闹究,但是,客戶端無法感知食店。
Redis Cluster 提供了重定向機(jī)制:客戶端將請求發(fā)送到實(shí)例上渣淤,這個實(shí)例沒有相應(yīng)的數(shù)據(jù),該 Redis 實(shí)例會告訴客戶端將請求發(fā)送到其他的實(shí)例上吉嫩。
Redis 如何告知客戶端重定向訪問新實(shí)例呢价认?
分為兩種情況:MOVED 錯誤、ASK 錯誤自娩。
MOVED 錯誤
MOVED 錯誤(負(fù)載均衡用踩,數(shù)據(jù)已經(jīng)遷移到其他實(shí)例上):當(dāng)客戶端將一個鍵值對操作請求發(fā)送給某個實(shí)例,而這個鍵所在的槽并非由自己負(fù)責(zé)的時候,該實(shí)例會返回一個 MOVED 錯誤指引轉(zhuǎn)向正在負(fù)責(zé)該槽的節(jié)點(diǎn)脐彩。
同時碎乃,客戶端還會更新本地緩存,將該 slot 與 Redis 實(shí)例對應(yīng)關(guān)系更新正確惠奸。
ASK 錯誤
如果某個 slot 的數(shù)據(jù)比較多梅誓,部分遷移到新實(shí)例,還有一部分沒有遷移咋辦佛南?
如果請求的 key 在當(dāng)前節(jié)點(diǎn)找到就直接執(zhí)行命令梗掰,否則時候就需要 ASK 錯誤響應(yīng)了,槽部分遷移未完成的情況下嗅回,如果需要訪問的 key 所在 Slot 正在從從 實(shí)例 1 遷移到 實(shí)例 2及穗,實(shí)例 1 會返回客戶端一條 ASK 報錯信息:客戶端請求的 key 所在的哈希槽正在遷移到實(shí)例 2 上,你先給實(shí)例 2 發(fā)送一個 ASKING 命令绵载,接著發(fā)發(fā)送操作命令拥坛。
集群可以設(shè)置多大?
Redis 官方給的 Redis Cluster 的規(guī)模上線是 1000 個實(shí)例尘分。
到底是什么限制了集群規(guī)模呢?關(guān)鍵在于實(shí)例間的通信開銷丸氛,Cluster 集群中的每個實(shí)例都保存所有哈希槽與實(shí)例對應(yīng)關(guān)系信息(Slot 映射到節(jié)點(diǎn)的表)培愁,以及自身的狀態(tài)信息。
在集群之間每個實(shí)例通過 Gossip協(xié)議傳播節(jié)點(diǎn)的數(shù)據(jù)缓窜,Gossip 協(xié)議工作原理大概如下:
1定续、從集群中隨機(jī)選擇一些實(shí)例按照一定的頻率發(fā)送 PING 消息發(fā)送給挑選出來的實(shí)例,用于檢測實(shí)例狀態(tài)以及交換彼此的信息禾锤。PING 消息中封裝了發(fā)送者自身的狀態(tài)信息私股、部分其他實(shí)例的狀態(tài)信息、Slot 與實(shí)例映射表信息恩掷。
2倡鲸、實(shí)例接收到 PING 消息后,響應(yīng) PONG 消息黄娘,消息包含的信息跟 PING 消息一樣峭状。
集群之間通過 Gossip協(xié)議可以在一段時間之后每個實(shí)例都能獲取其他所有實(shí)例的狀態(tài)信息。
所以在有新節(jié)點(diǎn)加入逼争,節(jié)點(diǎn)故障优床,Slot 映射變更都可以通過 PING,PONG 的消息傳播完成集群狀態(tài)在每個實(shí)例的傳播同步誓焦。