天天看點

深入淺出Redis系列:深入分析Cluster 叢集模式

作者:JAVA後端架構
深入淺出Redis系列:深入分析Cluster 叢集模式

1 背景

前面我們學習了Redis高可用的兩種架構模式:主從模式、哨兵模式。

解決了我們在Redis執行個體發生故障時,具備主從自動切換、故障轉移的能力,最終保證服務的高可用。

但是這些其實遠遠不夠,随着我們業務規模的不斷擴充,使用者量膨脹,并發量持續提升。原有的主從架構,已經遠遠達不到我們的需求了,這時候會有一些問題出現,比如:

  • 單機的CPU、記憶體、連接配接數、計算力都是有極限的,不能無限制的承載流量的擴增。
  • 超額的請求、大規模的資料計算,導緻必然的慢響應。

    這時候就需要适當的推進架構的演進,來滿足發展的需要。

2 Cluster 模式介紹

2.1 什麼是Cluster模式

Cluster 即 叢集模式,類似MySQL,Redis 叢集也是一種分布式資料庫方案,叢集通過分片(sharding)模式來對資料進行管理,并具備分片間資料複制、故障轉移和流量排程的能力。這種 分治模式很常見,我們在 微服務系列:拆分政策 和 MySQL系列:分庫分表 中實踐過很多次了。

Redis叢集的做法是 将資料劃分為 16384(2的14次方)個哈希槽(slots),如果你有多個執行個體節點,那麼每個執行個體節點将管理其中一部分的槽位,槽位的資訊會存儲在各自所歸屬的節點中。以下圖為例,該叢集有4個 Redis 節點,每個節點負責叢集中的一部分資料,資料量可以不均勻。比如性能好的執行個體節點可以多分擔一些壓力。

深入淺出Redis系列:深入分析Cluster 叢集模式

一個Redis叢集一共有16384個哈希槽,你可以有1 ~ n個節點來配置設定這些哈希槽,可以不均勻配置設定,每個節點可以處理0個 到至多 16384 個槽點。

當16384個哈希槽都有節點進行管理的時候,叢集處于online 狀态。同樣的,如果有一個哈希槽沒有被管理到,那麼叢集處于offline狀态。

上面圖中4個執行個體節點組成了一個叢集,叢集之間的資訊通過 Gossip協定 進行互動,這樣就可以在某一節點記錄其他節點的哈希槽(slots)的配置設定情況。

2.2 為什麼需要Cluster模式

單機的吞吐無法承受持續擴增的流量的時候,最好的辦法是從橫向(scale out) 和 縱向(scale up)兩方面進行擴充,這個我們在 MySQL系列 和 微服務系列 的時候已經讨論過了。

  • 縱向擴充(scale up):将單個執行個體的硬體資源做提升,比如 CPU核數量、記憶體容量、SSD容量。
  • 橫向擴充(scale out):橫向擴增 Redis 執行個體數,這樣每個節點隻負責一部分資料就可以,分擔一下壓力,典型的分治思維。
深入淺出Redis系列:深入分析Cluster 叢集模式

那橫向擴充和縱向擴充各有什麼優缺點呢?

  • scale up 雖然操作起來比較簡易。但是沒法解決Redis一些瓶頸問題,比如持久化(如輪式RDB快照還是AOF指令),遇到大資料量的時候,照樣效率會很低,響應慢。另外,單台服務機硬體擴容也是有限制的,不可能無限操作。
  • scale out 更容易擴充,分片的模式可以解決很多問題,包括單一執行個體節點的硬體擴容限制、成本限制,還可以分攤壓力,精細化治理,精細化維護。但是同時也要面臨分布式帶來的一些問題

    現實情況下,在面對千萬級甚至億級别的流量的時候,很多大廠都是在千百台的執行個體節點組成的叢集上進行流量排程、服務治理的。是以,使用Cluster模式,是業内廣泛采用的模式。

3 Cluster 實作原理

3.1 叢集的組群過程

叢集是由一個個互相獨立的節點(readis node)組成的, 是以剛開始的時候,他們都是隔離,毫無聯系的。我們需要通過一些操作,把他們聚集在一起,最終才能組成真正的可協調工作的叢集。

各個節點的聯通是通過 CLUSTER MEET 指令完成的:CLUSTER MEET <ip> <port> 。

具體的做法是其中一個node向另外一個 node(指定 ip 和 port) 發送 CLUSTER MEET 指令,這樣就可以讓兩個節點進行握手(handshake操作) ,握手成功之後,node 節點就會将握手另一側的節點添加到目前節點所在的叢集中。

這樣一步步的将需要聚集的節點都圈入同一個叢集中,如下圖:

深入淺出Redis系列:深入分析Cluster 叢集模式

3.2 叢集資料分片原理

現在的Redis叢集分片的做法,主要是使用了官方提供的 Redis Cluster 方案。這種方案就是的核心就是叢集的執行個體節點與哈希槽(slots)之間的劃分、映射與管理。下面我們來看看他具體的步驟。

3.2.1 哈希槽(slots)的劃分

這個前面已經說過了,我們會将整個Redis資料庫劃分為16384個哈希槽,你的Redis叢集可能有n個執行個體節點,每個節點可以處理0個 到至多 16384 個槽點,這些節點把 16384個槽位瓜分完成。

而你實際存儲的Redis鍵值資訊也必然歸屬于這 16384 個槽的其中一個。slots 與 Redis Key 的映射是通過以下兩個步驟完成的:

  • 使用 CRC16 算法計算鍵值對資訊的Key,會得出一個 16 bit 的值。
  • 将 第1步中得到的 16 bit 的值對 16384 取模,得到的值會在 0 ~ 16383 之間,映射到對應到哈希槽中。

    當然,可能在一些特殊的情況下,你想把某些key固定到某個slot上面,也就是同一個執行個體節點上。這時候可以用hash tag能力,強制 key 所歸屬的槽位等于 tag 所在的槽位。

    其實作方式為在key中加個{},例如test_key{1}。使用hash tag後用戶端在計算key的crc16時,隻計算{}中資料。如果沒使用hash tag,用戶端會對整個key進行crc16計算。下面示範下hash tag使用:

127.0.0.1:6380> cluster keyslot user:case{1}(integer) 1024

127.0.0.1:6380> cluster keyslot user:favor(integer) 1023

127.0.0.1:6380> cluster keyslot user:info{1}(integer) 1024

如上,使用hash tag 後會對應到同一個hash slot:1024中。

3.2.2 哈希槽(slots)的映射

一種是初始化的時候均勻配置設定 ,使用 cluster create 建立,會将 16384 個slots 平均配置設定在我們的叢集執行個體上,比如你有n個節點,那每個節點的槽位就是 16384 / n 個了 。

另一種是通過 CLUSTER MEET 指令将 node1、node2、ndoe3、node4 4個節點聯通成一個叢集,剛聯通的時候因為還沒配置設定哈希槽,還是處于offline狀态。我們使用 cluster addslots 指令來指定。

指定的好處就是性能好的執行個體節點可以多分擔一些壓力。

可以通過 addslots 指令指定哈希槽範圍,比如下圖中,我們哈希槽是這麼配置設定的:執行個體 1 管理 0 ~ 7120 哈希槽,執行個體 2 管理 7121~9945 哈希槽,執行個體 3 管理 9946 ~ 13005 哈希槽,執行個體 4 管理 13006 ~ 16383 哈希槽。

redis-cli -h 192.168.0.1 –p 6379 cluster addslots 0,7120

redis-cli -h 192.168.0.2 –p 6379 cluster addslots 7121,9945

redis-cli -h 192.168.0.3 –p 6379 cluster addslots 9946,13005

redis-cli -h 192.168.0.4 –p 6379 cluster addslots 13006,16383

slots 和 Redis 執行個體之間的映射關系如下:

深入淺出Redis系列:深入分析Cluster 叢集模式

key testkey_1 和 testkey_2 經過 CRC16 計算後再對slots的總個數 16384 取模,結果分别比對到了 cache1 和 cache3 上。

3.3 資料複制過程和故障轉移

3.3.1 資料複制

Cluster 是具備Master 和 Slave模式,Redis 叢集中的每個執行個體節點都負責一些槽位,比如上圖中的四個節點分管了不同的槽位區間。而每個Master至少需要一個Slave節點,Slave 節點是通過《Redis系列3:高可用之主從架構》方式同步主節點資料。節點之間保持TCP通信,當Master發生了當機, Redis Cluster自動會将對應的Slave節點選為Master,來繼續提供服務。

與純主從模式不同的是,主從節點之間并沒有讀寫分離, Slave 隻用作 Master 當機的高可用備份,是以更合理來說應該是主備模式。

如果主節點沒有從節點,那麼一旦發生故障時,叢集将完全處于不可用狀态。 但也允許配置 cluster-require-full-coverage參數,及時部分節點不可用,其他節點正常提供服務,這是為了避免全盤當機。

主從切換之後,故障恢複的主節點,會轉化成新主節點的從節點。這種自愈模式對提高可用性非常有幫助。

3.3.2 故障檢測

一個節點認為某個節點當機不能說明這個節點真的挂起了,無法提供服務了。隻有占據多數的執行個體節點都認為某個節點挂起了,這時候cluster才進行下線和主從切換的工作。

Redis 叢集的節點采用 Gossip 協定來廣播資訊,每個節點都會定期向其他節點發送ping指令,如果接受ping消息的節點在指定時間内沒有回複pong,則會認為該節點失聯了(PFail),則發送ping的節點就把接受ping的節點标記為主觀下線。

如果叢集半數以上的主節點都将主節點 xxx 标記為主觀下線,則節點 xxx 将被标記為客觀下線,然後向整個叢集廣播,讓其它節點也知道該節點已經下線,并立即對下線的節點進行主從切換。

3.3.3 主從故障轉移

當一個從節點發現自己正在複制的主節點進入了已下線,則開始對下線主節點進行故障轉移,故障轉移的步驟如下:

  • 如果隻有一個slave節點,則從節點會執行SLAVEOF no one指令,成為新的主節點。
  • 如果是多個slave節點,則采用選舉模式進行,競選出新的Master
    • 叢集中設立一個自增計數器,初始值為 0 ,每次執行故障轉移選舉,計數就會+1。
    • 檢測到主節點下線的從節點向叢集所有master廣播一條CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,所有收到消息、并具備投票權的主節點都向這個從節點投票。
    • 如果收到消息、并具備投票權的主節點未投票給其他從節點(隻能投一票哦,是以投過了不行),則傳回一條CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示支援。
    • 參與選舉的從節點都會接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,如果收集到的選票 大于等于 (n/2) + 1 支援,n代表所有具備選舉權的master,那麼這個從節點就被選舉為新主節點。
    • 如果這一輪從節點都沒能争取到足夠多的票數,則發起再一輪選舉(自增計數器+1),直至選出新的master。
  • 新的主節點會撤銷所有對已下線主節點的slots指派,并将這些slots全部指派給自己。
  • 新的主節點向叢集廣播一條PONG消息,這條PONG消息可以讓叢集中的其他節點立即知道這個節點已經由從節點變成了主節點,并且這個主節點已經接管了原本由已下線節點負責處理的槽。
  • 新的主節點開始接收和自己負責處理的槽有關的指令請求,故障轉移完成。

跟哨兵類似,兩者都是基于 Raft 算法來實作的,流程如圖所示:

深入淺出Redis系列:深入分析Cluster 叢集模式

3.4 client 通路 資料叢集的過程

3.4.1 定位資料所在節點

我們前面說過了,Redis 中的每個執行個體節點會将自己負責的哈希槽資訊 通過 Gossip 協定廣播給叢集中其他的執行個體,實作了slots配置設定資訊的擴散。這樣的話,每個執行個體都知道整個叢集的哈希槽配置設定情況以及映射資訊。

是以用戶端想要快捷的連接配接到服務端,并對某個redis資料進行快捷通路,一般是經過以下步驟:

  • 用戶端連接配接任一執行個體,擷取到slots與執行個體節點的映射關系,并将該映射關系的資訊緩存在本地。
  • 将需要通路的redis資訊的key,經過CRC16計算後,再對16384 取模得到對應的 Slot 索引。
  • 通過slot的位置進一步定位到具體所在的執行個體,再将請求發送到對應的執行個體上。

    下圖展示了 Redis 用戶端如何定位資料所在節點:

4 總結

  • 哨兵模式已經實作了故障自動轉移的能力,業務規模的不斷擴充,使用者量膨脹,并發量持續提升,會出現了 Redis 響應慢的表象。
  • 使用 Redis Cluster 叢集,主要解決了大資料量存儲導緻的各種慢問題,同時也便于橫向拓展。在面對千萬級甚至億級别的流量的時候,很多大廠也都是在千百台的執行個體節點組成的叢集上進行流量排程、服務治理的。
  • 整個Redis資料庫劃分為16384個哈希槽,Redis叢集可能有n個執行個體節點,每個節點可以處理0個 到至多 16384 個槽點,這些節點把 16384個槽位瓜分完成。
  • Cluster 是具備Master 和 Slave模式,Redis 叢集中的每個執行個體節點都負責一些槽位,節點之間保持TCP通信,當Master發生了當機, Redis Cluster自動會将對應的Slave節點選為Master,來繼續提供服務。
  • 用戶端能夠快捷的連接配接到服務端,主要是将slots與執行個體節點的映射關系存儲在本地,當需要通路的時候,對key進行CRC16計算後,再對16384 取模得到對應的 Slot 索引,再定位到相應的執行個體上。

為幫助開發者們提升面試技能、有機會入職BATJ等大廠公司,特别制作了這個專輯——這一次整體放出。

大緻内容包括了: Java 集合、JVM、多線程、并發程式設計、設計模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat等大廠面試題等、等技術棧!

深入淺出Redis系列:深入分析Cluster 叢集模式

歡迎大家關注公衆号【Java爛豬皮】,回複【666】,擷取以上最新Java後端架構VIP學習資料以及視訊學習教程,然後一起學習,一文在手,面試我有。

每一個專欄都是大家非常關心,和非常有價值的話題,如果我的文章對你有所幫助,還請幫忙點贊、好評、轉發一下,你的支援會激勵我輸出更高品質的文章,非常感謝!

深入淺出Redis系列:深入分析Cluster 叢集模式

繼續閱讀