Redis系列1:深刻理解高效能Redis的本質
Redis系列2:資料持久化提高可用性
Redis系列3:高可用之主從架構
Redis系列4:高可用之Sentinel(哨兵模式)
前面我們學習了Redis高可用的兩種架構模式:主從模式、哨兵模式。
解決了我們在Redis範例發生故障時,具備主從自動切換、故障轉移的能力,最終保證服務的高可用。
但是這些其實遠遠不夠,隨著我們業務規模的不斷擴充套件,使用者量膨脹,並行量持續提升。原有的主從架構,已經遠遠達不到我們的需求了,這時候會有一些問題出現,比如:
Cluster 即 叢集模式,類似MySQL,Redis 叢集也是一種分散式資料庫方案,叢集通過分片(sharding)模式來對資料進行管理,並具備分片間資料複製、故障轉移和流量排程的能力。這種 分治模式很常見,我們在 微服務系列:拆分策略 和 MySQL系列:分庫分表 中實踐過很多次了。
Redis叢集的做法是 將資料劃分為 16384(2的14次方)個雜湊槽(slots),如果你有多個範例節點,那麼每個範例節點將管理其中一部分的槽位,槽位的資訊會儲存在各自所歸屬的節點中。以下圖為例,該叢集有4個 Redis 節點,每個節點負責叢集中的一部分資料,資料量可以不均勻。比如效能好的範例節點可以多分擔一些壓力。
一個Redis叢集一共有16384個雜湊槽,你可以有1 ~ n個節點來分配這些雜湊槽,可以不均勻分配,每個節點可以處理0個 到至多 16384 個槽點。
當16384個雜湊槽都有節點進行管理的時候,叢集處於online 狀態。同樣的,如果有一個雜湊槽沒有被管理到,那麼叢集處於offline狀態。
上面圖中4個範例節點組成了一個叢集,叢集之間的資訊通過 Gossip協定 進行互動,這樣就可以在某一節點記錄其他節點的雜湊槽(slots)的分配情況。
單機的吞吐無法承受持續擴增的流量的時候,最好的辦法是從橫向(scale out) 和 縱向(scale up)兩方面進行擴充套件,這個我們在 MySQL系列 和 微服務系列 的時候已經討論過了。
叢集是由一個個互相獨立的節點(readis node)組成的, 所以剛開始的時候,他們都是隔離,毫無聯絡的。我們需要通過一些操作,把他們聚集在一起,最終才能組成真正的可協調工作的叢集。
各個節點的聯通是通過 CLUSTER MEET 命令完成的:CLUSTER MEET <ip> <port>
。
具體的做法是其中一個node向另外一個 node(指定 ip 和 port) 傳送 CLUSTER MEET 命令,這樣就可以讓兩個節點進行握手(handshake操作) ,握手成功之後,node 節點就會將握手另一側的節點新增到當前節點所在的叢集中。
這樣一步步的將需要聚集的節點都圈入同一個叢集中,如下圖:
現在的Redis叢集分片的做法,主要是使用了官方提供的 Redis Cluster 方案。這種方案就是的核心就是叢集的範例節點與雜湊槽(slots)之間的劃分、對映與管理。下面我們來看看他具體的步驟。
這個前面已經說過了,我們會將整個Redis資料庫劃分為16384個雜湊槽,你的Redis叢集可能有n個範例節點,每個節點可以處理0個 到至多 16384 個槽點,這些節點把 16384個槽位瓜分完成。
而你實際儲存的Redis鍵值資訊也必然歸屬於這 16384 個槽的其中一個。slots 與 Redis Key 的對映是通過以下兩個步驟完成的:
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中。
一種是初始化的時候均勻分配 ,使用 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 範例之間的對映關係如下:
key testkey_1
和 testkey_2
經過 CRC16 計算後再對slots的總個數 16384 取模,結果分別匹配到了 cache1 和 cache3 上。
Cluster 是具備Master 和 Slave模式,Redis 叢集中的每個範例節點都負責一些槽位,比如上圖中的四個節點分管了不同的槽位區間。而每個Master至少需要一個Slave節點,Slave 節點是通過《Redis系列3:高可用之主從架構》方式同步主節點資料。 節點之間保持TCP通訊,當Master發生了宕機, Redis Cluster自動會將對應的Slave節點選為Master,來繼續提供服務。與純主從模式不同的是,主從節點之間並沒有讀寫分離, Slave 只用作 Master 宕機的高可用備份,所以更合理來說應該是主備模式。
如果主節點沒有從節點,那麼一旦發生故障時,叢集將完全處於不可用狀態。 但也允許設定 cluster-require-full-coverage
引數,及時部分節點不可用,其他節點正常提供服務,這是為了避免全盤宕機。
主從切換之後,故障恢復的主節點,會轉化成新主節點的從節點。這種自愈模式對提高可用性非常有幫助。
一個節點認為某個節點宕機不能說明這個節點真的掛起了,無法提供服務了。只有佔據多數的範例節點都認為某個節點掛起了,這時候cluster才進行下線和主從切換的工作。
Redis 叢集的節點採用 Gossip 協定來廣播資訊,每個節點都會定期向其他節點傳送ping命令,如果接受ping訊息的節點在指定時間內沒有回覆pong,則會認為該節點失聯了(PFail),則傳送ping的節點就把接受ping的節點標記為主觀下線。
如果叢集半數以上的主節點都將主節點 xxx 標記為主觀下線,則節點 xxx 將被標記為客觀下線,然後向整個叢集廣播,讓其它節點也知道該節點已經下線,並立即對下線的節點進行主從切換。
當一個從節點發現自己正在複製的主節點進入了已下線,則開始對下線主節點進行故障轉移,故障轉移的步驟如下:
如果只有一個slave節點,則從節點會執行SLAVEOF no one命令,成為新的主節點。
如果是多個slave節點,則採用選舉模式進行,競選出新的Master
新的主節點會復原所有對已下線主節點的slots指派,並將這些slots全部指派給自己。
新的主節點向叢集廣播一條PONG訊息,這條PONG訊息可以讓叢集中的其他節點立即知道這個節點已經由從節點變成了主節點,並且這個主節點已經接管了原本由已下線節點負責處理的槽。
新的主節點開始接收和自己負責處理的槽有關的命令請求,故障轉移完成。
跟哨兵類似,兩者都是基於 Raft 演演算法來實現的,流程如圖所示:
我們前面說過了,Redis 中的每個範例節點會將自己負責的雜湊槽資訊 通過 Gossip 協定廣播給叢集中其他的範例,實現了slots分配資訊的擴散。這樣的話,每個範例都知道整個叢集的雜湊槽分配情況以及對映資訊。
所以使用者端想要快捷的連線到伺服器端,並對某個redis資料進行快捷存取,一般是經過以下步驟: