你是一個程序員,你知道 redis 本質上是個單機服務,一旦崩了,服務就不可用了,加幾個從節點當做備胎,時刻同步主節點數據,一旦主節點崩了。

某個覬覦主節點位置的從節點,就會成為新的主節點,繼續對外提供服務,這樣可以保證系統高可用。

可問題就來了,redis 是將數據放內存中的,從節點的數據跟主節點是一樣的,存的都是全量數據,但單機服務器內存總有上限。加再多從節點也無法突破單機瓶頸。如果想要存儲更多數據,該怎么辦呢?

這就需要聊聊 redis 集群模式了。

看之前,你點贊了嗎?關注了嗎?謝謝!
單機內存有限,而數據無限,拿有限的單機內存去支持無限的數據,明顯不合理,所以我們換個思路。
既然數據無限,那我們就對數據進行切分,變成多份,將多份切片數據放到多個 redis 節點上,數據量變大后,再根據需要增加 redis 節點,也就做到了所謂的高可擴展。

那怎么切分數據呢?
我們知道 redis 全稱 Remote Dictionary Server,遠程的字典服務。字典你熟啊,在 python 里叫 dict,在 java 里叫 map, 它們都有 Key 和 value。

所以很容易想到,我們可以對多個 redis 節點 進行編號,再將 key 的值跟 redis 的節點總數進行求余,就像這樣。計算得到的 redis_num 就是這個 key 對應的數據應該放到哪個 redis 節點上。

redis_num = (CRC16(key)) mod redis_all_num但它有個很大的問題,如果我們前期沒規劃好容量,后期想要調整 redis 節點個數,那上面公式里的 redis_all_num 就會改變,對應放數據的 redis 節點也會被改變,那大概率所有數據都需要重新遷移。
這對線上服務來說,成本和風險都太高。
有更好的方案嗎?有!
既然擔心公式發生變化會導致最終的分片結果改變,那我們就想辦法讓公式里用于求余的值固定下來。

怎么操作呢?
沒有什么是加一層中間層不能解決的,如果有,那就再加一層。
我們可以在數據的 key 和 redis 節點之間,加一層長度固定的數組層。
比如長度為16384的數組,數組里每一槽位就是所謂的哈希槽,slot。
將原來的數據分片公式改造成這樣:
slot_num = (CRC16(key)) mod 16384
再讓每個 redis 節點負責管理一部分哈希槽,比如節點 A 負責前面 1w 個槽,節點 B 負責后面的 6384 個。

數據的 key 經過公式分片后會落到某個 哈希槽 上,再由管理這個 槽 的 redis 節點來讀寫這個數據。
通過這個方式將key數據分散到多個哈希槽,也就是多個 redis 節點上。
并且每個 redis 節點 都維護了「其他 redis 管理了哪些槽」這一信息。有了這些信息,任意一個 redis 節點只要拿到 key,就能知道它該存到哪個 redis 節點上。
現在如果我們想要增加 redis 節點個數,由于哈希槽這一層的存在,16384 固定不變,那計算出的 slot_num 也不會變化。
我們只需要定義新增的 redis 負責哪部分 哈希槽,然后遷移那一小部分 哈希槽 里的數據就好了。

比起以前要全部數據重新遷移,現在的工作量大大減小。
看爽了,就來個一鍵三連吧。
引入哈希槽這一概念后,我們就能用多個 redis 節點來切分存儲數據了。
但這也引入了另外一個問題,哈希槽信息是維護在redis里的,客戶端沒有這個信息,讀寫數據時,怎么知道這些 key對應哪個哈希槽 和 redis 節點。
有解法嗎?
有!redis提供一個「CLUSTER SLOTS」命令,可以讓客戶端獲取到最新的哈希槽分配信息。
但如果redis節點數量有變化。客戶端沒有及時更新哈希槽信息,是不是就會出錯?
完全不用擔心,客戶端只管對任意一個redis節點讀寫就行。
接收到數據的 redis 節點會根據最新維護的哈希槽信息,判斷這個 key 屬于哪個 redis 節點,如果恰好屬于它自己,則直接完成讀寫。否則,redis 節點就返回一個錯誤,并告訴客戶端該去哪個 redis 節點讀寫數據就好,這個過程也叫重定向。

不過你也不需要自己寫代碼去實現這個重定向功能,其他大佬已經幫你實現好相關的客戶端代碼庫,用就完事了。
結合重定向,我們再來看下數據遷移時的一個問題。
如果我調整了 redis 個數,數據正在發生遷移行為,客戶端查某個 redis 節點沒數據,我怎么知道它是真沒有這個 key,還是沒來得及遷移過來呢?
好辦,redis 內部會對 redis 的哈希槽,維護一個是否正在遷移數據的狀態。

如果正在遷移中,redis 節點會返回一個重定向指令,告訴客戶端接下來該去哪個節點讀這個數據,客戶端根據重定向指令的提示,重新訪問另一個 redis 節點,如果這時候還是沒有數據,那就是真沒數據。
通過數據切分,現在每個 redis 都維護了一部分數據,為了防止崩潰導致的部分數據無法訪問,還可以為每個 redis 節點加幾個從節點。
這樣一個將數據分片到多個 redis 節點上,同時為每個節點加入從節點的架構,就是所謂的集群模式。

到這里,我們就已經讓原來的單機服務 redis ,變得具備高可用和高可擴展的特性了。
接下來,我們用一個具體的例子了解下它的工作原理。
假設 Redis 集群有 3 個主節點(A、B、C)

0~54605461~1092210923~16383假設客戶端要寫入的 key 是 user:1001,value 是"小白 debug"。
這時候客戶端會隨機訪問其中一個 redis 節點,比如 節點 A ,發送 set key value 的操作。
注意: 如果客戶端有slot信息,則不是「隨機連」
Redis 會計算這個 key 對應的哈希槽的值,比如 1234。A 節點發現 1234 在哈希槽 0~5460范圍內,歸它自己管, 所以這個Key會被寫入到 節點 A。
但如果得到的哈希槽值是 5462, 屬于 B 節點管,那就會返回一個重定向 命令,告訴客戶端,這個應該寫到 B,客戶端會自動重定向到節點 B,成功完成寫入。
假設客戶端要讀取的依然是 user:1001。
客戶端還是隨機連到某一節點,比如節點 A,發送 get key 的操作。節點A 計算 key 的值發現 user:1001 的哈希槽的值是 1234,歸節點 A 自己管,那么就會返回對應的值"小白 debug"。
但如果計算得到的哈希槽的值是 5462,屬于節點 B。那節點 A 會返回一個重定向指令,告訴客戶端,這個key在節點 B。
客戶端會自動重定向到節點 B,完成數據讀取。
現在大家通了嗎?
好啦,如果你覺得這期視頻對你有幫助,記得轉發給你那不成器的兄弟。記得關注!我們下期見!