前面介紹了《進階的Redis之數據持久化RDB與AOF》和《進階的Redis之Sentinel原理及實戰》,此次來了解下Redis的集羣功能,以及其中哈希分片原理。node
若是Redis只用複製功能作主從,那麼當數據量巨大的狀況下,單機狀況下可能已經承受不下一份數據,更不用說是主從都要各自保存一份完整的數據。在這種狀況下,數據分片是一個很是好的解決辦法。redis
Redis的Cluster正是用於解決該問題。它主要提供兩個功能:算法
對於第二點,它的功能有點相似於Sentienl的故障轉移(能夠了解下以前Sentinel的文章),在這裏不細說。下面詳細瞭解下Redis的槽位分片原理,在此以前,先了解下分佈式簡單哈希算法和一致性哈希算法,以幫助理解槽位的做用。緩存
假設有三臺機,數據落在哪臺機的算法爲bash
c = Hash(key) % 3
複製代碼
例如key A的哈希值爲4,4%3=1,則落在第二臺機。Key ABC哈希值爲11,11%3=2,則落在第三臺機上。微信
利用這樣的算法,假設如今數據量太大了,須要增長一臺機器。A本來落在第二臺上,如今根據算法4%4=0,落到了第一臺機器上了,可是第一臺機器上根本沒有A的值。這樣的算法會致使增長機器或減小機器的時候,引發大量的緩存穿透,形成雪崩。分佈式
在1997年,麻省理工學院的Karger等人提出了一致性哈希算法,爲的就是解決分佈式緩存的問題。測試
在一致性哈希算法中,整個哈希空間是一個虛擬圓環 ui
假設有四個節點Node A、B、C、D,通過ip地址的哈希計算,它們的位置以下 spa
有4個存儲對象Object A、B、C、D,通過對Key的哈希計算後,它們的位置以下
一致性哈希算法大概如此,那麼它的容錯性和擴展性如何呢?
假設Node C節點掛掉了,Object C的存儲丟失,那麼它順時針找到的最新節點是Node D。也就是說Node C掛掉了,受影響僅僅包括Node B到Node C區間的數據,而且這些數據會轉移到Node D進行存儲。
同理,假設如今數據量大了,須要增長一臺節點Node X。Node X的位置在Node B到Node C直接,那麼受到影響的僅僅是Node B到Node X間的數據,它們要從新落到Node X上。
因此一致性哈希算法對於容錯性和擴展性有很是好的支持。但一致性哈希算法也有一個嚴重的問題,就是數據傾斜。
若是在分片的集羣中,節點太少,而且分佈不均,一致性哈希算法就會出現部分節點數據太多,部分節點數據太少。也就是說沒法控制節點存儲數據的分配。以下圖,大部分數據都在A上了,B的數據比較少。
Redis集羣(Cluster)並無選用上面一致性哈希,而是採用了哈希槽(SLOT)的這種概念。主要的緣由就是上面所說的,一致性哈希算法對於數據分佈、節點位置的控制並非很友好。
首先哈希槽實際上是兩個概念,第一個是哈希算法。Redis Cluster的hash算法不是簡單的hash(),而是crc16算法,一種校驗算法。
另一個就是槽位的概念,空間分配的規則。其實哈希槽的本質和一致性哈希算法很是類似,不一樣點就是對於哈希空間的定義。一致性哈希的空間是一個圓環,節點分佈是基於圓環的,沒法很好的控制數據分佈。而Redis Cluster的槽位空間是自定義分配的,相似於Windows盤分區的概念。這種分區是能夠自定義大小,自定義位置的。
Redis Cluster包含了16384個哈希槽,每一個Key經過計算後都會落在具體一個槽位上,而這個槽位是屬於哪一個存儲節點的,則由用戶本身定義分配。例如機器硬盤小的,能夠分配少一點槽位,硬盤大的能夠分配多一點。若是節點硬盤都差很少則能夠平均分配。因此哈希槽這種概念很好地解決了一致性哈希的弊端。
另外在容錯性和擴展性上,表象與一致性哈希同樣,都是對受影響的數據進行轉移。而哈希槽本質上是對槽位的轉移,把故障節點負責的槽位轉移到其餘正常的節點上。擴展節點也是同樣,把其餘節點上的槽位轉移到新的節點上。
但必定要注意的是,對於槽位的轉移和分派,Redis集羣是不會自動進行的,而是須要人工配置的。因此Redis集羣的高可用是依賴於節點的主從複製與主從間的自動故障轉移。
下面以最簡單的例子,拋開高可用主從複製級轉移的內容,來重點介紹下Redis集羣是如何搭建,槽位是如何分配的,以加深對Redis集羣原理及概念的理解。
先找到redis.conf,啓用cluster功能。
cluster-enabled yes
默認是關閉的,要啓用cluster,讓redis成爲集羣的一部分,須要手動打開才行。
而後配置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
查看集羣範圍。
在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
複製代碼
經過cluster info
命令查看集羣的狀態
接下來給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
隨便登上一個實例,記得加上參數-c
,啓用集羣模式的客戶端,不然沒法正常運行。
redis-cli -c -p 6380
複製代碼
嘗試下set、get操做
利用cluster keyslot
命令計算出key是在哪一個槽位上,從而得出會跳轉到哪一個節點上執行。
更多技術文章、精彩乾貨,請關注
博客:zackku.com
微信公衆號:Zack說碼