進階的Redis之雜湊分片原理與叢集實戰

公眾號_Zack說碼發表於2018-11-12

前面介紹了《進階的Redis之資料持久化RDB與AOF》《進階的Redis之Sentinel原理及實戰》,這次來了解下Redis的叢集功能,以及其中雜湊分片原理。

叢集分片模式

如果Redis只用複製功能做主從,那麼當資料量巨大的情況下,單機情況下可能已經承受不下一份資料,更不用說是主從都要各自儲存一份完整的資料。在這種情況下,資料分片是一個非常好的解決辦法。

Redis的Cluster正是用於解決該問題。它主要提供兩個功能:

  1. 自動對資料分片,落到各個節點上
  2. 即使叢集部分節點失效或者連線不上,依然可以繼續處理命令

對於第二點,它的功能有點類似於Sentienl的故障轉移(可以瞭解下之前Sentinel的文章),在這裡不細說。下面詳細瞭解下Redis的槽位分片原理,在此之前,先了解下分散式簡單雜湊演算法和一致性雜湊演算法,以幫助理解槽位的作用。

簡單雜湊演算法

假設有三臺機,資料落在哪臺機的演算法為

  c = Hash(key) % 3
複製程式碼

例如key A的雜湊值為4,4%3=1,則落在第二臺機。Key ABC雜湊值為11,11%3=2,則落在第三臺機上。

利用這樣的演算法,假設現在資料量太大了,需要增加一臺機器。A原本落在第二臺上,現在根據演算法4%4=0,落到了第一臺機器上了,但是第一臺機器上根本沒有A的值。這樣的演算法會導致增加機器或減少機器的時候,引起大量的快取穿透,造成雪崩。

一致性雜湊演算法

在1997年,麻省理工學院的Karger等人提出了一致性雜湊演算法,為的就是解決分散式快取的問題。

一致性雜湊演算法中,整個雜湊空間是一個虛擬圓環

進階的Redis之雜湊分片原理與叢集實戰

假設有四個節點Node A、B、C、D,經過ip地址的雜湊計算,它們的位置如下

進階的Redis之雜湊分片原理與叢集實戰

有4個儲存物件Object A、B、C、D,經過對Key的雜湊計算後,它們的位置如下

進階的Redis之雜湊分片原理與叢集實戰
對於各個Object,它所真正的儲存位置是按順時針找到的第一個儲存節點。例如Object A順時針找到的第一個節點是Node A,所以Node A負責儲存Object A,Object B儲存在Node B。

一致性雜湊演算法大概如此,那麼它的容錯性擴充套件性如何呢?

假設Node C節點掛掉了,Object C的儲存丟失,那麼它順時針找到的最新節點是Node D。也就是說Node C掛掉了,受影響僅僅包括Node B到Node C區間的資料,並且這些資料會轉移到Node D進行儲存。

進階的Redis之雜湊分片原理與叢集實戰

同理,假設現在資料量大了,需要增加一臺節點Node X。Node X的位置在Node B到Node C直接,那麼受到影響的僅僅是Node B到Node X間的資料,它們要重新落到Node X上。

所以一致性雜湊演算法對於容錯性和擴充套件性有非常好的支援。但一致性雜湊演算法也有一個嚴重的問題,就是資料傾斜

如果在分片的叢集中,節點太少,並且分佈不均,一致性雜湊演算法就會出現部分節點資料太多,部分節點資料太少。也就是說無法控制節點儲存資料的分配。如下圖,大部分資料都在A上了,B的資料比較少。

進階的Redis之雜湊分片原理與叢集實戰

雜湊槽

Redis叢集(Cluster)並沒有選用上面一致性雜湊,而是採用了雜湊槽(SLOT)的這種概念。主要的原因就是上面所說的,一致性雜湊演算法對於資料分佈、節點位置的控制並不是很友好。

首先雜湊槽其實是兩個概念,第一個是雜湊演算法。Redis Cluster的hash演算法不是簡單的hash(),而是crc16演算法,一種校驗演算法。

另外一個就是槽位的概念,空間分配的規則。其實雜湊槽的本質和一致性雜湊演算法非常相似,不同點就是對於雜湊空間的定義。一致性雜湊的空間是一個圓環,節點分佈是基於圓環的,無法很好的控制資料分佈。而Redis Cluster的槽位空間是自定義分配的,類似於Windows盤分割槽的概念。這種分割槽是可以自定義大小,自定義位置的。

Redis Cluster包含了16384個雜湊槽,每個Key通過計算後都會落在具體一個槽位上,而這個槽位是屬於哪個儲存節點的,則由使用者自己定義分配。例如機器硬碟小的,可以分配少一點槽位,硬碟大的可以分配多一點。如果節點硬碟都差不多則可以平均分配。所以雜湊槽這種概念很好地解決了一致性雜湊的弊端。

另外在容錯性擴充套件性上,表象與一致性雜湊一樣,都是對受影響的資料進行轉移。而雜湊槽本質上是對槽位的轉移,把故障節點負責的槽位轉移到其他正常的節點上。擴充套件節點也是一樣,把其他節點上的槽位轉移到新的節點上。

但一定要注意的是,對於槽位的轉移和分派,Redis叢集是不會自動進行的,而是需要人工配置的。所以Redis叢集的高可用是依賴於節點的主從複製與主從間的自動故障轉移。

叢集搭建

下面以最簡單的例子,拋開高可用主從複製級轉移的內容,來重點介紹下Redis叢集是如何搭建,槽位是如何分配的,以加深對Redis叢集原理及概念的理解。

redis.conf配置

先找到redis.conf,啟用cluster功能。

進階的Redis之雜湊分片原理與叢集實戰

cluster-enabled yes預設是關閉的,要啟用cluster,讓redis成為叢集的一部分,需要手動開啟才行。

然後配置cluster的配置檔案

進階的Redis之雜湊分片原理與叢集實戰
每一個cluster節點都有一個cluster的配置檔案,這個檔案主要用於記錄節點資訊,用程式自動生成和管理,不需要人工干預。唯一要注意的是,如果在同一臺機器上執行多個節點,需要修改這個配置為不同的名字。

本次為了方便搭建,所有Redis例項都在同一臺機器上,所以修改不同的cluster config名字後,複製三份redis.conf配置,以用於啟動三個叢集例項(cluster至少要三個主節點才能進行)。

叢集關聯

  > redis-server /usr/local/etc/redis/redis-6379.conf --port 6379 &
  > redis-server /usr/local/etc/redis/redis-6380.conf --port 6380 &
  > redis-server /usr/local/etc/redis/redis-6381.conf --port 6381 &
複製程式碼

&符號的作用是讓命令在後臺執行,但程式執行的log依然會列印在console中。也可以通過配置redis.conf中deamonize yes,讓Redis在後臺執行。

連上6379的Redis例項,然後通過cluster nodes檢視叢集範圍。

進階的Redis之雜湊分片原理與叢集實戰
連上其他例項也是一樣,目前6379、6380、6381在各自的叢集中,且叢集只有它們自己一個。

在6379上,通過cluster meet命令,與6380、6381建立連結。

  127.0.0.1:6379> cluster meet 127.0.0.1 6380
  127.0.0.1:6379> cluster meet 127.0.0.1 6381
複製程式碼

進階的Redis之雜湊分片原理與叢集實戰
可以看到叢集中已經包含了6379、6380、6381三個節點了。登入其他節點檢視也是一樣的結果。即使6380與6381之間沒有直接手動關聯,但在叢集中,節點一旦發現有未關聯的節點,會自動與之握手關聯。

槽位分配

通過cluster info命令檢視叢集的狀態

進階的Redis之雜湊分片原理與叢集實戰
state的狀態是fail的,還沒啟用。看下官方的說明
進階的Redis之雜湊分片原理與叢集實戰
只有state為ok,節點才能接受請求。如果只要有一個槽位(slot)沒有分配,那麼這個狀態就是fail。而一共需要分配16384槽位才能讓叢集正常工作。

接下來給6379分配0~5000的槽位,給6380分配5001~10000的槽位,給6381分配10001~16383的槽位。

  > redis-cli -c -p 6379 cluster addslots {0..5000}
  > redis-cli -c -p 6380 cluster addslots {5001..10000}
  > redis-cli -c -p 6381 cluster addslots {10001..16383}
複製程式碼

再看看cluster info

進階的Redis之雜湊分片原理與叢集實戰
state已經為ok,16384個槽位都已經分配好了。現在叢集已經可以正常工作了。

效果測試

隨便登上一個例項,記得加上引數-c,啟用叢集模式的客戶端,否則無法正常執行。

  redis-cli -c -p 6380
複製程式碼

嘗試下set、get操作

進階的Redis之雜湊分片原理與叢集實戰
可以看到,Redis叢集會計算key落在哪個卡槽,然後會把命令轉發到負責該卡槽的節點上執行。

利用cluster keyslot命令計算出key是在哪個槽位上,從而得出會跳轉到哪個節點上執行。


更多技術文章、精彩乾貨,請關注
部落格:zackku.com
微信公眾號:Zack說碼

進階的Redis之雜湊分片原理與叢集實戰

相關文章