redis集群扩容和缩容_深入理解Redis Cluster集群
一、背景
前面的文章《深入理解Redis哨兵機制》一文中介紹了Redis哨兵集群的工作原理,哨兵集群雖然滿足了高可用的特性,但是依然存在這樣的問題:即數據只能往一個主節點上進行寫入。
只能往一個主節點上進行寫入會有什么問題呢?大家都知道,其實在很多大型分布式系統中,要緩存的數據往往是非常大的,可能會達到幾十GB,幾百GB,甚至上TB的數據需要緩存。那么這種情況下,單節點寫入的架構可能就無法滿足業務的要求了,即使幾十GB的,我們雖然可以通過縱容擴容的方式來提高單機的內存容量,但是通過《深入理解Redis持久化》一文,我們了解到,單實例的Redis的不宜內存過大,否則數據持久化將可能導致性能問題。
那么這個時候,既然縱容擴容已經遭遇了瓶頸,那么是否有其它橫向擴容的解決方案呢?其實Redis已經為我們提供了橫向擴容的解決方案,那就是Redis Cluster集群。在早期的Redis版本(3.0之前),Redis是不支持Cluster解決方案的。那個時候,如果我們需要使用Redis分布式存儲的方案,我們只能自己手工對數據進行分片。
本文章的內容,我們將研討Redis Cluster集群的工作原理及對Redis Cluster集群的一些常見問題進行剖析。
二、核心原理
比如說現在有這樣一個場景,我們在18GB的數據要進行存儲,我們手里有3臺8G的機器,我們分別編號為:節點1、節點2、節點3。這個時候,我們應該怎么做呢?
這里我們就需要解決這樣兩個問題:
(1)、這18G的數據,我們應該如何將它平均分攤到這3個機器上;
(2)、查詢數據的時候,我們去哪臺機器上查找我們要查詢的數據。
對于數據的分布式存儲,我們通常要先對要進行分布式存儲的數據,先選定一個用于數據分片的鍵,通常稱為片鍵(sharding key),業內目前比較常見的處理手段有這么幾種:
a、按片鍵的值固定取模映射到不同節點;
b、按片鍵的值分區間存儲映射到不同節點;
c、將片鍵的值用一致性哈希算法均攤到各個節點上。
我們先來說說按片鍵取模的方案a,比如說我們如下userid:1,2,3,4,5,6,7,8,9......N-2, N-1, N。這里我們假設N % == 0。
節點1:userid % 3 == 0 (存儲3, 6, 9...N)
節點2:userid % 3 == 1 (存儲1, 4, 7... N-2)
節點3:userid % 3 == 2 (存儲2, 5, 8...N-1)
這種方案有什么好處,映射算法非常簡單;聰明的你可能也看出來了這個算法的壞處,那就是如果增加節點的數量或減少節點的數量。就會涉及到大量數據的移動,幾乎每個節點上的數據都需要重新進行求模運算,這個過程叫rehash。然后根據rehash的結果,將數據重新分布在各個實例上。
那么接下來,你也可以考慮這樣的映射算法,也是方案b:
節點1:1, 2 ... N/3
節點2:N/3+1 ... (2*N/3)
節點3:(2*N/3)+1 ... N
這樣按片鍵的區間,比如說每臺機器存儲一個區間,這樣進行映射,是不是可以呢?理論上這樣映射也是可以的。而且這個映射算法的好處,就是增加新節點的時候,它雖然不需要移動數據,但是新增加的節點,剛開始可能存儲的數據比較少,也是會出現新增加的節點在存儲壓力的分擔上與其它已經存在的節點不均衡的現象,這種不均衡的現象要直到它的數據被填滿(即達到分配給他的存儲區間)。另外,如果要減少一個實例,因為其它的每一個節點都是存儲固定范圍的數據,所以一旦節點被減少,那么就需要重新劃定每一個節點新的范圍,這里就會涉及到大量的數據移動。
OK,上面我們分析了按片鍵的值固定取模、按片鍵的值分區間映射兩種方案,它們都存在著這樣的一些問題:
(1)、擴容(取模)或縮容(取模、分區間)的時候,容易導致大批量的數據移動;
(2)、擴容的時候,新增加節點往往壓力與舊節點不均衡。
為了解決上述的分片缺點,那么Redis是怎么做的呢?
在Redis Cluster集群方案中,它采用了方案c的一個變種,它引入了slot的概念,什么呢slot呢?slot就是哈希槽的意思,redis把整個集群劃分為16384個哈希槽,具體操作上有兩個大的步驟:
1、把數據根據片鍵的值映射到具體的某一個slot上;
2、然后再在內部維護一個slot到實例節點的映射關系。
接下來,我們先來看看第一個步驟,如下圖所示:
Redis會對sharding key,按照CRC16算法計算一個16bit 的值;然后,再用這個16bit值對16384取模,得到 0~16383范圍內的模數,每個模數代表一個相應編號的哈希槽。上圖中的N==16383。關于CRC16哈希算法,如果你有興趣了解,可以自行查閱相關的文檔。
那么接下來就是第2個問題這些slot是如何映射到機器上的呢?其實在redis的內部,它是維護了一個slot到實例的映射表,如下圖所示:
這是一個雙向映射關系表,根據slot可以計算出它對應的節點,根據節點可以知道它分配了哪些slot。
通過上述的兩個步驟,Redis Cluster就完成了數據的分布以及查詢請求的路由,不管是數據的分布還是路由都是這樣兩個步驟:
(1)、先根據sharding key計算出slot;
(2)、再根據節點與slot的映射表,查到slot對應的節點。
要了解它的優點,我們就需要了解一致性哈希算法(帶虛擬節點的)的優點。關于什么是帶虛擬節點的一致性哈希算法,這里我們先不展開,讀者有興趣,可以自行了解一下。
我們先給出這樣做在應對擴容或縮容的時候,它會表現出哪些特點,主要從數據遷移代價以及各實例壓力分擔情況兩個維度來進行分析,我們假設有N臺機器:
1、擴容(增加1個節點)
數據遷移代價:平均每臺機器移動1/N的數據到新節點上。
各實例壓力分擔:均衡分擔。
2、縮容(減少1個節點)
數據遷移代價:平均每臺機器增加存儲被減少實例的1/N的數據。
各實例壓力分擔:均衡分擔。
綜合來看,我們的方案c,也就是Redis Cluster集群雖然在擴容與縮容的時候,也會涉及到數據的移動,但是無論哪一種情況,它都可以保持整個集群的相對穩定,各實例都可以按預定的計劃進行均衡的數據存儲,以及讀寫壓力的均衡分擔。
另外,redis提供了一個參數cluster-enabled,將該參數設置為yes,那么節點將以Redis Cluster集群的模式運行。
三、常見問題
上面我們已經介紹完了Redis Cluster集群的核心工作原理,下面我們來看看該集群的一些問題:
問題1:如果各個實例要部署的機器內存容量不一樣怎么辦?
對于這個問題,其實Redis Cluster是提供了手動配置slot到實例的映射關系的,這樣性能高的機器可以多配置一些slot,性能低一點的機器可以配置少一些slot的,但是需要滿足這樣兩個條件:1、盡可能均衡分配(這樣才可以保證擴容與縮容時,數據遷移代價及各實例讀寫分擔相對于實例性能整體平衡);2、在手動配置映射關系表的時候,必須將16384個slot全部配置完,否則集群無法正常工作。
問題2:客戶端是怎么知道各實例與slot的映射關系的呢?
Redis在集群搭建好了之后,每一個客戶端都會緩存映射關系表,因為最終客戶端取數據是要向具體的某一個redis實例去請求數據。
問題3:比如說有這樣一種情況,由于集群的擴容與縮容,會導致數據的遷移,那么對于客戶端來說,它有一個鍵值對,我們比如說叫:hello(key) - world(value)。它之前是存儲在節點1中,由于數據的遷移,這個key-value對就遷移至節點2了。那么這個時候,客戶端是怎么知道去節點2拿數據的呢?
Redis Cluster集群中的實例之間,會保護通信,以相互之間同步映射關系表信息,那么針對這種有key被遷移走了的情況,其實redis提供了一種重定向的機制來解決問題,如下圖所示:
大概的步驟如上圖所示:
(1)、客戶端之前根據hello計算出的slot所映射的機器是節點1;
(2)、由于集群擴容增加了節點2,hello這個key的值被移動到了節點2;
(3)、由于遷移的過程的,客戶端并不知道,于是客戶端繼續向節點1請求hello的這個key;
(4)、節點1計算這個hello的slot,根據最新的映射表查詢到該slot對應的節點是節點2;
(5)、節點1向客戶端響應一條moved命令,響應內容包含了hello當前所在的節點2的ip和port;
(6)、客戶端收到moved命令后,更新本地的映射表(實例到slot的映射表);
(7)、客戶端再次向節點2發起請求;
(8)、節點2返回hello對應的value,也就是將world返回給客戶端。
問題4:在實際應用中,針對問題3中客戶端先向節點1去查詢,如果恰好,hello這個key正在遷移中,此時會出現一種情況,會出現什么情況?
對于這種部分遷移完成的情況,在客戶端向節點1發起請求,要讀取hello的value時,節點1發現這個key正在遷移中,那么節點1會返回一個ASK命令的錯誤響應給客戶端,如下:
(error) ASK 10238 192.168.1.102:6379
這個10238,比如說就是代表hello這個key的哈希槽編號,也就是slot的編號,相當于節點1告訴客戶端,hello這個key正在遷移中,你去問問節點2,看看節點2是否現在已經完成了這個key的存儲。
然后客戶端會先向節點發送ASKING命令,通知節點2允許自己在節點2中繼續查詢hello的值。
這里有一個問題需要注意的是:在接受到節點1的ASK命令時,雖然也返回了節點2的ip和port,但客戶端此時并不會去更新本地的映射表。只有最后從節點2在接受ASKING的命令時,返回給了客戶端肯定的響應之后,客戶端才會更新本地映射表。
總結
以上是生活随笔為你收集整理的redis集群扩容和缩容_深入理解Redis Cluster集群的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奔驰b200仪表灯怎么调节亮度?
- 下一篇: 字符变量赋值规则_C#的变量、运算符