Redis Cluster 提供了一種支持?jǐn)?shù)據(jù)在多個 Redis 節(jié)點上自動進(jìn)行分片的部署方式悬包。
Redis Cluster 主要是為了實現(xiàn)以下這些目標(biāo)(按在設(shè)計中的重要性排序):
- 在多達(dá) 1000 個節(jié)點的時候仍能保持高性能及線性的可擴(kuò)展性比搭。沒有代理,使用異步復(fù)制蔓钟,并且不對值執(zhí)行合并操作。
- 可接受的寫入安全:與多數(shù)派節(jié)點相連的客戶端所做的寫入操作,系統(tǒng)嘗試全部都保存下來(以最大努力的方式)丹诀。不過極小概率下容忍小部分寫入會丟失走孽。
- 可用性:分區(qū)情況下在多數(shù)派這邊惧辈,當(dāng)絕大多數(shù)的主節(jié)點是可達(dá)的,并且對于每一個不可達(dá)的主節(jié)點都至少有一個它的從節(jié)點可達(dá)的情況下磕瓷,Redis Cluster 仍能提供服務(wù)盒齿。
通信協(xié)議
在 Redis 集群中,節(jié)點負(fù)責(zé)存儲數(shù)據(jù)困食、記錄集群的狀態(tài)(包括 slots 信息)边翁。集群節(jié)點同樣能自動發(fā)現(xiàn)其他節(jié)點,檢測出異常節(jié)點硕盹, 并且在需要的時候在從節(jié)點中推選出主節(jié)點符匾。
為了執(zhí)行這些任務(wù),所有的集群節(jié)點都通過 TCP 連接和一個被稱作 “Redis Cluster Bus” 的二進(jìn)制協(xié)議進(jìn)行通信瘩例。 每一個節(jié)點都通過集群總線與集群上的其余每個節(jié)點連接起來啊胶。節(jié)點們使用 Gossip 協(xié)議來傳播集群的信息甸各,使用 Gossip 協(xié)議的最大的好處是:即使集群節(jié)點的數(shù)量增加,每個節(jié)點的負(fù)載也不會增加很多焰坪,幾乎是恒定的趣倾。這就允許 Redis Cluster 集群管理的節(jié)點規(guī)模能橫向擴(kuò)展到數(shù)千個。
Redis Cluster 中的每個節(jié)點都維護(hù)一份自己視角下的當(dāng)前整個集群的狀態(tài)琳彩,主要包括:
- 當(dāng)前集群狀態(tài)
- 集群中各節(jié)點所負(fù)責(zé)的 slots 信息誊酌,及其 migrate 狀態(tài)
- 集群中各節(jié)點的 master-slave 狀態(tài)
- 集群中各節(jié)點的存活狀態(tài)及懷疑 Fail 狀態(tài)
也就是說上面的信息,就是集群中Node相互八卦傳播流言蜚語的內(nèi)容主題露乏,而且比較全面碧浊,既有自己的更有別人的,這么一來大家都相互傳瘟仿,最終信息就全面而且一致了箱锐。
消息類型
Redis Cluster 的節(jié)點之間會相互發(fā)送多種消息,較為重要的如下所示:
- MEET:通過 CLUSTER MEET 命令劳较,已有集群的節(jié)點會向新的節(jié)點發(fā)送邀請驹止,加入現(xiàn)有集群,然后新節(jié)點就會開始與其他節(jié)點進(jìn)行通信观蜗。
- PING:節(jié)點按照配置的時間間隔向集群中其他節(jié)點發(fā)送 PING 消息臊恋,消息中帶有自己的狀態(tài),還有自己維護(hù)的集群元數(shù)據(jù)墓捻,和部分其他節(jié)點的元數(shù)據(jù)抖仅,用于檢測其他節(jié)點是否異常。
- PONG: 用于回應(yīng) PING 和 MEET 的消息砖第,結(jié)構(gòu)和 PING 消息類似撤卢。一個節(jié)點也可以通過向集群廣播自己的PONG 消息來讓集群中的其他節(jié)點立即刷新關(guān)于這個節(jié)點的認(rèn)識,例如當(dāng)一次故障轉(zhuǎn)移操作成功執(zhí)行之后梧兼,新的主節(jié)點會向集群廣播一條 PONG 消息放吩,以此來讓集群中的其他節(jié)點立即知道這個節(jié)點已經(jīng)變成了主節(jié)點。
- FAIL: 節(jié)點 PING 不通某節(jié)點后羽杰,會向集群所有節(jié)點廣播該節(jié)點掛掉的消息渡紫,其他節(jié)點收到消息后標(biāo)記已下線。
數(shù)據(jù)分片
哈希算法
關(guān)于數(shù)據(jù)分片方案最容易想到的的就是哈希算法考赛,即對 key 計算一個 hash 值惕澎,然后對 master 節(jié)點個數(shù)取模,可以讓 key 均勻分布到集群的各個節(jié)點上欲虚。
但這種模式其實存在較大的隱患集灌,如上圖所示,當(dāng)一個節(jié)點故障后,將會導(dǎo)致整個集群的緩存失效欣喧。例如開始的算法為 hash(key) % 3腌零,當(dāng)一個節(jié)點故障后變?yōu)?hash(key) % 2,導(dǎo)致整個集群的大多數(shù) key 都會因為取模變化而失效唆阿,從而導(dǎo)致緩存失效益涧。當(dāng)這種情況發(fā)生在緩存系統(tǒng)上時,整個系統(tǒng)的運行都有可能崩潰驯鳖。
一致性哈希算法
既然普通的哈希算法存在上述的問題闲询,于是就有人提出了一致性哈希算法,算法不再是對 master 節(jié)點數(shù)量來取模浅辙,而是固定對 2^32 取模扭弧,也就是值的范圍在 [0, 2^32 -1] 。一致性哈希將其范圍抽象成了一個圓環(huán)记舆,使用CRC16 算法計算出來的哈希值會落到圓環(huán)上的某個地方鸽捻。
同時 Redis 實例也分布在圓環(huán)上,計算出的值在圓環(huán)上按照順時針的順序找到第一個 Redis 實例即是服務(wù)實例泽腮,通過這樣完成對 key 的節(jié)點分配御蒲。
如圖所示,對于 key A 來說將找到 Node 3 節(jié)點诊赊,而對 key B 來說將找到 Node 2節(jié)點厚满。
即便是當(dāng)有節(jié)點發(fā)生故障,如圖當(dāng) Node 3 發(fā)生故障時候碧磅,key A 將順序找到 Node 2 進(jìn)行服務(wù)碘箍。Node 3 的故障只導(dǎo)致了 Node 1 ~ Node 3 中間的 key 發(fā)生了轉(zhuǎn)移,相較與普通哈希算法续崖,對整個集群的影響已經(jīng)大大減少了敲街。
一致性哈希算法团搞,也存在一些問題严望。如上圖所示,當(dāng)集群中節(jié)點較少時候逻恐,很容易產(chǎn)生節(jié)點在環(huán)上分布不均勻的情況像吻,這樣勢必導(dǎo)致每個節(jié)點的負(fù)載不一致。節(jié)點分布的不均衡复隆,非常容易導(dǎo)致系統(tǒng)的不穩(wěn)定性或者資源的浪費拨匆。
于是又引入了虛擬節(jié)點的概念,為系統(tǒng)內(nèi)的節(jié)點增加虛擬節(jié)點挽拂,虛擬節(jié)點與真實節(jié)點的功能一致惭每,只是為了變相增加集群內(nèi)節(jié)點的數(shù)量,從而使得節(jié)點能均勻的分步到 hash 環(huán)上。
Hash Slots
Redis Cluster 沒有使用一致性哈希算法, 而是引入了哈希槽的概念台腥。
Redis Cluster 中有 16384(2^14) 個哈希槽宏赘,每個 key 通過 CRC16 計算后對 16384 取模來決定放置哪個槽,然后決定服務(wù)節(jié)點黎侈。集群的每個節(jié)點負(fù)責(zé)一部分哈希槽察署,舉個例子,比如當(dāng)前集群有3個節(jié)點峻汉,那么可以這么分配哈希槽:
- 節(jié)點 A 包含 0 到 5500 號哈希槽
- 節(jié)點 B 包含 5501 到 11000 號哈希槽
- 節(jié)點 C 包含 11001 到 16383號哈希槽
這種結(jié)構(gòu)很容易添加或刪除節(jié)點贴汪。 比如我們想新添加個節(jié)點 D,我需要從節(jié)點 A休吠,B扳埂,C 中移動部分哈希槽到節(jié)點 D 上。如果我想移除節(jié)點 A瘤礁,需要將 A 中的哈希槽移到 B 和 C 節(jié)點上聂喇,然后將沒有任何哈希槽的 A 節(jié)點從集群中移除即可。由于從一個節(jié)點將哈希槽移動到另一個節(jié)點并不會停止服務(wù)蔚携,所以無論添加希太、刪除或者改變某個節(jié)點的哈希槽的數(shù)量都不會造成集群不可用。