Redis技術(shù)專題之幫你完全搞定Cluster集群架構(gòu)(原理篇)

每日一句

自律是最強(qiáng)者的本能,當(dāng)你把自律作為習(xí)慣,那你已經(jīng)成為強(qiáng)者。

前提概要

為什么需要Redis集群?

  • 在講Redis集群架構(gòu)之前,我們先簡單講下Redis單實(shí)例的架構(gòu),從最開始的一主N從,到讀寫分離,再到Sentinel哨兵機(jī)制,單實(shí)例的Redis緩存足以應(yīng)對大多數(shù)的使用場景,也能實(shí)現(xiàn)主從故障遷移。

    image

但是,在某些場景下,單實(shí)例存Redis緩存會存在的幾個問題:

Redis主從架構(gòu)+Sentinel仍存在問題

寫并發(fā)的壓力仍在

  • Redis單實(shí)例讀寫分離可以解決讀操作的負(fù)載均衡,但對于寫操作,仍然是全部落在了master節(jié)點(diǎn)上面,在海量數(shù)據(jù)高并發(fā)場景,一個節(jié)點(diǎn)寫數(shù)據(jù)容易出現(xiàn)瓶頸,造成master節(jié)點(diǎn)的壓力上升

海量數(shù)據(jù)的存儲壓力

  • 內(nèi)存容量的限制Redis的最大缺點(diǎn)和局限性就在于內(nèi)存存儲數(shù)據(jù),這樣子對容量而言會有相當(dāng)大的限制

  • 持久化和硬盤的限制Redis單實(shí)例本質(zhì)上只有一臺Master作為存儲,如果面對海量數(shù)據(jù)的存儲,一臺Redis的服務(wù)器就應(yīng)付不過來了,而且數(shù)據(jù)量太大意味著持久化成本高,嚴(yán)重時可能會阻塞服務(wù)器,造成服務(wù)請求成功率下降,降低服務(wù)的穩(wěn)定性

針對以上的問題,Redis集群提供了較為完善的方案,解決了存儲能力受到單機(jī)限制,寫操作無法負(fù)載均衡的問題。


什么是Redis集群?

【Redis集群】是一種服務(wù)器Sharding(分片)技術(shù),3.0版本開始正式提供。

Redis的哨兵模式基本已經(jīng)可以實(shí)現(xiàn)高可用,讀寫分離 ,但是在這種模式下每臺Redis服務(wù)器都存儲相同的數(shù)據(jù),很浪費(fèi)內(nèi)存,所以在redis3.0上加入了Cluster集群模式,實(shí)現(xiàn)了Redis的分布式存儲,也就是說每臺 Redis 節(jié)點(diǎn)上存儲不同的內(nèi)容

  • (分片存儲)Redis3.0加入了 Redis 的集群模式,實(shí)現(xiàn)了數(shù)據(jù)的分布式存儲,對數(shù)據(jù)進(jìn)行分片,將不同的數(shù)據(jù)存儲在不同的master節(jié)點(diǎn)上面,從而解決了海量數(shù)據(jù)的存儲問題。

  • (指令轉(zhuǎn)換)Redis集群采用去中心化的思想,沒有中心節(jié)點(diǎn)的說法,對于客戶端來說,整個集群可以看成一個整體,可以連接任意一個節(jié)點(diǎn)進(jìn)行操作,就像操作單一Redis實(shí)例一樣,不需要任何代理中間件,當(dāng)客戶端操作的key沒有分配到該node上時,Redis會返回轉(zhuǎn)向指令,指向正確的Redis節(jié)點(diǎn)。

  • (主從和哨兵)Redis也內(nèi)置了高可用機(jī)制,支持N個master節(jié)點(diǎn),每個master節(jié)點(diǎn)都可以掛載多個slave節(jié)點(diǎn),當(dāng)master節(jié)點(diǎn)掛掉時,集群會提升它的某個slave節(jié)點(diǎn)作為新的master節(jié)點(diǎn)。

image

如上圖所示,Redis集群可以看成多個主從架構(gòu)組合起來的,每一個主從架構(gòu)可以看成一個節(jié)點(diǎn)(其中,只有master節(jié)點(diǎn)具有處理請求的能力,slave節(jié)點(diǎn)主要是用于節(jié)點(diǎn)的高可用


集群的數(shù)據(jù)分片

前面講到,Redis集群通過分布式存儲的方式解決了單節(jié)點(diǎn)的海量數(shù)據(jù)存儲的問題,對于分布式存儲,需要考慮的重點(diǎn)就是如何將數(shù)據(jù)進(jìn)行拆分到不同的Redis服務(wù)器上。常見的分區(qū)算法有hash算法、一致性hash算法,關(guān)于這些算法這里就不多介紹。

如果要實(shí)現(xiàn) Redis 數(shù)據(jù)的分片,我們有三種方案。

  • (客戶端負(fù)載)第一種是在客戶端實(shí)現(xiàn)相關(guān)的邏輯,例如用取?;蛘咭恢滦怨ey進(jìn)行分片,查詢和修改都先判斷key的路由。

    • Jedis客戶端提供了Redis Sharding的方案,并且支持連接池。
    • Sharded 分片的原理?怎么連接到某一個Redis服務(wù):提供了一致性hash和md5散列兩種hash算法,默認(rèn)使用一致性hash算法。
    • 并且為了使得請求能均勻的落在不同的節(jié)點(diǎn)上,Sharded Jedis會使用節(jié)點(diǎn)的名稱(如果節(jié)點(diǎn)沒有名稱使用默認(rèn)名稱)虛擬化出160個虛擬節(jié)點(diǎn)。也可以根據(jù)不同節(jié)點(diǎn)的weight,虛擬化出160,weight個節(jié)點(diǎn)。
    • 當(dāng)客戶端訪問redis時,首先根據(jù)key計(jì)算出其落在哪個節(jié)點(diǎn)上,然后找到節(jié)點(diǎn)的ip和端口進(jìn)行連接訪問。

  • (中間代理負(fù)載)第二種是把做分片處理的邏輯抽取出來,運(yùn)行一個獨(dú)立的代理服務(wù),客戶端連接到這個代理服務(wù),代理服務(wù)做請求的轉(zhuǎn)發(fā)
    • 典型的代理分區(qū)方案有 Twitter 開源的 Twemproxy 和國內(nèi)的豌豆莢開源的 Codis

  • (服務(wù)端負(fù)載)第三種就是基于服務(wù)端實(shí)現(xiàn)。

普通hash算法

  • 如果是希望數(shù)據(jù)分布相對均勻的話,可以考慮哈希后取模。

  • 將key使用hash算法計(jì)算之后:hash(key)%N,根據(jù)余數(shù),決定映射到那一個節(jié)點(diǎn)

  • 優(yōu)點(diǎn)就是比較簡單,屬于靜態(tài)的分片規(guī)則。但是一旦節(jié)點(diǎn)數(shù)量變化,新增或者減少,由于取模的 N 發(fā)生變化, 數(shù)據(jù)需要重新分布和遷移。

一致性hash算法

  • 把所有的哈希值空間組織成一個虛擬的圓環(huán)(哈希環(huán)),整個空間按順時針方向組織。因?yàn)槭黔h(huán)形空間,0 和 2^32-1 是重疊的(總共2^32個)。

    1. 先根據(jù)機(jī)器的名稱或者 IP計(jì)算哈希值。

    2. 然后分布到哈希環(huán)中。查找時先根據(jù)key計(jì)算哈希值,得到哈希環(huán)中的位置

    3. 最后順時針找到第一個大于等于該哈希值的第一個Node,就是數(shù)據(jù)存儲的節(jié)點(diǎn)

      • 優(yōu)點(diǎn)是在加入和刪除節(jié)點(diǎn)時只影響相鄰的兩個節(jié)點(diǎn)。
      • 缺點(diǎn)是加減節(jié)點(diǎn)會造成部分?jǐn)?shù)據(jù)無法命中
      • 此外,針對于hash節(jié)點(diǎn)分散不均勻或者傾倒?fàn)顟B(tài),采用一個節(jié)點(diǎn)分為多個虛擬節(jié)點(diǎn)做優(yōu)化
  • 所以一般用于緩存,而且用于節(jié)點(diǎn)量大的情況下,擴(kuò)容一般增加一倍節(jié)點(diǎn)保障數(shù)據(jù)負(fù)載均衡。

哈希槽Slot算法

Redis 集群既沒有用哈希取模,也沒有用一致性哈希,而是用Hash槽來實(shí)現(xiàn)的。Redis集群創(chuàng)建了 16384 個槽(slot),每個節(jié)點(diǎn)負(fù)責(zé)一定區(qū)間的slot

Redis集群的哈希槽的分區(qū)
  • Redis集群中有163842^14)個哈希槽(槽的范圍是 0 -16383,哈希槽),將不同的哈希槽分布在不同的Redis節(jié)點(diǎn)上面進(jìn)行管理,也就是說每個Redis節(jié)點(diǎn)只負(fù)責(zé)一部分區(qū)間的哈希槽。

  • 對數(shù)據(jù)進(jìn)行操作的時候:

    1. 集群會對使用CRC16算法對key進(jìn)行計(jì)算并對16384取模 (slot = CRC16(key)%16384) ?!?/li>
    2. 得到的結(jié)果就是Key-Value所放入的槽,通過這個槽值,去找對應(yīng)的槽所對應(yīng)的Redis節(jié)點(diǎn)。
    3. 然后直接到這個對應(yīng)的節(jié)點(diǎn)上進(jìn)行存取操作。
    • 好處:(自動更新hash槽的映射數(shù)據(jù)關(guān)系)使用哈希槽的好處就在于可以方便的添加或者移除節(jié)點(diǎn),并且無論是添加刪除或者修改某一個節(jié)點(diǎn),都不會造成集群不可用的狀態(tài)
      • 當(dāng)需要增加節(jié)點(diǎn)時,只需要把其他節(jié)點(diǎn)的某些哈希槽挪到新節(jié)點(diǎn)就可以了
      • 當(dāng)需要移除節(jié)點(diǎn)時,只需要把移除節(jié)點(diǎn)上的哈希槽挪到其他節(jié)點(diǎn)就行了
哈希槽分區(qū)的分析

集群的每個節(jié)點(diǎn)負(fù)責(zé)一部分hash槽,舉個例子,比如當(dāng)前集群有3個節(jié)點(diǎn),那么

  • 節(jié)點(diǎn) A 包含 0 到 5460 號哈希槽

  • 節(jié)點(diǎn) B 包含 5461 到 10922 號哈希槽

  • 節(jié)點(diǎn) C 包含 10923 到 16383 號哈希槽

這種結(jié)構(gòu)很容易添加或者刪除節(jié)點(diǎn)。

  • (1)如果我想新添加個節(jié)點(diǎn) D , 我需要從節(jié)點(diǎn) A, B, C 中得部分槽到 D 上。

  • (2)如果我想移除節(jié)點(diǎn) A ,需要將 A 中的槽移到 B 和 C 節(jié)點(diǎn)上,然后將沒有任何槽的 A 節(jié)點(diǎn)從集群中移除即可

由于從一個節(jié)點(diǎn)將哈希槽移動到另一個節(jié)點(diǎn)并不會停止服務(wù),所以無論添加刪除或者改變某個節(jié)點(diǎn)的哈希槽的數(shù)量都不會造成集群不可用的狀態(tài)

查看 key 屬于哪個 slot:
redis> cluster keyslot yan

注意:key 與 slot 的關(guān)系是永遠(yuǎn)不會變的,會變的只有 slot 和 Redis 節(jié)點(diǎn)的關(guān)系

(tips) 怎么讓相關(guān)的數(shù)據(jù)落到同一個節(jié)點(diǎn)上

在key里面加入{hash tag}即可。Redis在計(jì)算槽編號的時候只會獲取{}之間的字符串進(jìn)行槽編號計(jì)算,這樣由于上面兩個不同的鍵,{}里面的字符串是相同的,因此他們可以被計(jì)算出相同的槽

客戶端重定向

客戶端連接到哪一臺服務(wù)器?訪問的數(shù)據(jù)不在當(dāng)前節(jié)點(diǎn)上,怎么辦?

比如在 7291 端口的 Redis 的 redis-cli 客戶端操作:

127.0.0.1:7291> set qs 1

(error) MOVED 13724 127.0.0.1:7293
  1. 服務(wù)端返回 MOVED,也就是根據(jù) key 計(jì)算出來的 slot 不歸 7191 端口管理,而是 歸 7293 端口管理,服務(wù)端返回 MOVED 告訴客戶端去 7293 端口操作。

  2. 這個時候更換端口,用 redis-cli –p 7293 操作,才會返回 OK。這樣客戶端需要連接兩次?;蛘哂?/redis-cli -c -p port 的命令(c 代表 cluster)自動重定向。

新增或下線了 Master 節(jié)點(diǎn),數(shù)據(jù)怎么遷移(重新分配)?
  • 因?yàn)?key 和 slot 的關(guān)系是永遠(yuǎn)不會變的,當(dāng)新增了節(jié)點(diǎn)的時候,需要把原有的 slot 分配給新的節(jié)點(diǎn)負(fù)責(zé),并且把相關(guān)的數(shù)據(jù)遷移過來

  • 新增的節(jié)點(diǎn)沒有哈希槽,不能分布數(shù)據(jù),在原來的任意一個節(jié)點(diǎn)上執(zhí)行。

redis-cli --cluster reshard 127.0.0.1:7291
  • 槽的遷移與指派命令
CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000 

輸入需要分配的哈希槽的數(shù)量(比如 500),和哈希槽的來源節(jié)點(diǎn)(可以輸入 all 或 者 id)

集群的主從復(fù)制模型

  • **為了保證高可用,redis-cluster集群引入了主從復(fù)制模型,一個主節(jié)點(diǎn)對應(yīng)一個或者多個從節(jié)點(diǎn),當(dāng)主節(jié)點(diǎn)宕機(jī)的時候,就會啟用從節(jié)點(diǎn) **。

    • 當(dāng)其它主節(jié)點(diǎn) ping 一個主節(jié)點(diǎn) A 時,如果半數(shù)以上的主節(jié)點(diǎn)與 A 通信超時,那么認(rèn)為主節(jié)點(diǎn) A 宕機(jī)了。如果主節(jié)點(diǎn) A 和它的從節(jié)點(diǎn) A1 都宕機(jī)了,那么該集群就無法再提供服務(wù)了。
  • 默認(rèn)情況下,redis集群的讀和寫都是到master上去執(zhí)行的,不支持slave節(jié)點(diǎn)讀和寫,跟Redis主從復(fù)制下讀寫分離不一樣,因?yàn)閞edis集群的核心的理念,主要是使用slave做數(shù)據(jù)的熱備,以及master故障時的主備切換,實(shí)現(xiàn)高可用的。

  • Redis的讀寫分離,是為了橫向任意擴(kuò)展slave節(jié)點(diǎn)去支撐更大的讀吞吐量。而redis集群架構(gòu)下,本身master就是可以任意擴(kuò)展的,如果想要支撐更大的讀或?qū)懙耐掏铝浚伎梢灾苯訉aster進(jìn)行橫向擴(kuò)展。

集群的特點(diǎn)

image
  • redis集群內(nèi)部的節(jié)點(diǎn)是相互通信的(PING-PONG機(jī)制),每個節(jié)點(diǎn)都是一個redis實(shí)例;內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬。

  • 為了實(shí)現(xiàn)集群的高可用,即判斷節(jié)點(diǎn)是否健康(能否正常使用),redis-cluster有這么一個投票容錯機(jī)制:如果集群中超過半數(shù)的節(jié)點(diǎn)投票認(rèn)為某個節(jié)點(diǎn)掛了,那么這個節(jié)點(diǎn)就掛了(fail)。這是判斷節(jié)點(diǎn)是否掛了的方法

  • Redis集群是沒有統(tǒng)一的入口的,客戶端與 Redis 節(jié)點(diǎn)直連,不需要中間代理層,也不需要連接集群所有節(jié)點(diǎn),即連接集群中任何一個可用節(jié)點(diǎn)即可。

  • 客戶端(client)連接集群的時候連接集群中的任意節(jié)點(diǎn)(node)即可。

  • 那么如何判斷集群是否掛了呢? -> 如果集群中任意一個節(jié)點(diǎn)掛了,而且該節(jié)點(diǎn)沒有從節(jié)點(diǎn)(備份節(jié)點(diǎn)),那么這個集群就掛了。這是判斷集群是否掛了的方法;

集群運(yùn)行的要求
  • Redis集群至少需要3個節(jié)點(diǎn),因?yàn)橥镀比蒎e機(jī)制要求超過半數(shù)節(jié)點(diǎn)認(rèn)為某個節(jié)點(diǎn)掛了該節(jié)點(diǎn)才是掛了,所以2個節(jié)點(diǎn)無法構(gòu)成集群。

  • 要保證集群的高可用,需要每個節(jié)點(diǎn)都有從節(jié)點(diǎn),也就是備份節(jié)點(diǎn),所以Redis集群至少需要6臺服務(wù)器。

集群的總結(jié)

優(yōu)勢

  1. 無中心架構(gòu)。

  2. 數(shù)據(jù)按照slot存儲分布在多個節(jié)點(diǎn),節(jié)點(diǎn)間數(shù)據(jù)共享,可動態(tài)調(diào)整數(shù)據(jù)分布。

    • 解耦數(shù)據(jù)和節(jié)點(diǎn)之間的關(guān)系,簡化了擴(kuò)容和收縮難度;
    • 節(jié)點(diǎn)自身維護(hù)槽的映射關(guān)系,不需要客戶端代理服務(wù)維護(hù)槽分區(qū)元數(shù)據(jù)
    • 支持節(jié)點(diǎn)、槽、鍵之間的映射查詢,用于數(shù)據(jù)路由,在線伸縮等場景.
  3. 可擴(kuò)展性,可線性擴(kuò)展到1000個節(jié)點(diǎn)(官方推薦不超過 1000 個)節(jié)點(diǎn)可動態(tài)添加或刪除。

  4. 高可用性,部分節(jié)點(diǎn)不可用時,集群仍可用。通過增加Slave做 standby 數(shù)據(jù)副本,能夠?qū)崿F(xiàn)故障自動failover,節(jié)點(diǎn)之間通過gossip協(xié)議交換狀態(tài)信息,用投票機(jī)制完成 Slave到Master的角色提升。

  5. 降低運(yùn)維成本,提高系統(tǒng)的擴(kuò)展性和可用性。

不足

  1. Client 實(shí)現(xiàn)復(fù)雜,驅(qū)動要求實(shí)現(xiàn) Smart Client,緩存 slots mapping 信息并及時更新,提高了開發(fā)難度,客戶端的不成熟影響業(yè)務(wù)的穩(wěn)定性。

  2. 節(jié)點(diǎn)會因?yàn)槟承┰虬l(fā)生阻塞(阻塞時間大于 clutser-node-timeout),被判斷下線,這種 failover是沒有必要的。

  3. 數(shù)據(jù)通過異步復(fù)制,不保證數(shù)據(jù)的強(qiáng)一致性

  4. 多個業(yè)務(wù)使用同一套集群時,無法根據(jù)統(tǒng)計(jì)區(qū)分冷熱數(shù)據(jù),資源隔離性較差,容 易出現(xiàn)相互影響的情況。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容