1. 定義:
由于數據量過大,單個Master復制集難以承擔,因此需要對多個復制集進行集群,形成水平擴展每個復制集只負責存儲整個數據集的一部分,這就是Redis的集群,其作用是提供在多個Redis節點間共享數據的程序集。官網介紹地址
2.能干嘛
-
Redis集群支持多個Master,每個Master又可以掛載多個Slave,實現讀寫分離,支持數據的高可用,支持海里數據的讀寫存儲操作
-
由于Cluster自帶Sentinel的故障轉移機制,內置了高可用的支持,無需再去使用哨兵功能
-
客戶端與Redis的節點連接,不再需要連接集群中所有的節點,只需要任意連接集群中的一個可用節點即可
-
槽位slot負責分配到各個物理服務節點,由對應的集群來負責維護節點、插槽和數據之間的關系
3. 集群算法-分片-槽位slot
3.1 官網介紹:
翻譯后:
3.2 redis集群的槽位slot
3.3 redis集群的分片
3.4 他兩的優勢
3.5 slot槽位映射,一般業界有3種解決方案
3.5.1 哈希取余分區
3.5.2 一致性哈希算法分區
(1) 是什么
一致性Hash算法背景
一致性哈希算法在1997年由麻省理工學院中提出的,設計目標是為了解決分布式緩存數據變動和映射問題,某個機器宕機了,分母數量改變了,自然取余數不OK了。
(2)能干嘛
提出一致性Hash解決方案。目的是當服務器個數發生變動時,盡量減少影響客戶端到服務器的映射關系
(3)3大步驟
- 算法構建一致性哈希環
一致性哈希環
一致性哈希算法必然有個hash函數并按照算法產生hash值,這個算法的所有可能哈希值會構成一個全量集,這個集合可以成為一個hash空間[0,2^32-1],這個是一個線性空間,但是在算法中,我們通過適當的邏輯控制將它首尾相連(0 = 2^32),這樣讓它邏輯上形成了一個環形空間。
它也是按照使用取模的方法,前面筆記介紹的節點取模法是對節點(服務器)的數量進行取模。而一致性Hash算法是對232取模,簡單來說,一致性Hash算法將整個哈希值空間組織成一個虛擬的圓環,如假設某哈希函數H的值空間為0-232-1(即哈希值是一個32位無符號整形),整個哈希環如下圖:整個空間按順時針方向組織,圓環的正上方的點代表0,0點右側的第一個點代表1,以此類推,2、3、4、……直到232-1,也就是說0點左側的第一個點代表232-1, 0和232-1在零點中方向重合,我們把這個由232個點組成的圓環稱為Hash環。
- redis服務器ip節點映射
節點映射
將集群中各個IP節點映射到環上的某一個位置。
將各個服務器使用Hash進行一個哈希,具體可以選擇服務器的IP或主機名作為關鍵字進行哈希,這樣每臺機器就能確定其在哈希環上的位置。假如4個節點NodeA、B、C、D,經過IP地址的哈希函數計算(hash(ip)),使用IP地址哈希后在環空間的位置如下:
- key落到服務器的落鍵規則
當我們需要存儲一個kv鍵值對時,首先計算key的hash值,hash(key),將這個key使用相同的函數Hash計算出哈希值并確定此數據在環上的位置,從此位置沿環順時針“行走”,第一臺遇到的服務器就是其應該定位到的服務器,并將該鍵值對存儲在該節點上。
如我們有Object A、Object B、Object C、Object D四個數據對象,經過哈希計算后,在環空間上的位置如下:根據一致性Hash算法,數據A會被定為到Node A上,B被定為到Node B上,C被定為到Node C上,D被定為到Node D上。
(4)優點
容錯性
假設Node C宕機,可以看到此時對象A、B、D不會受到影響。一般的,在一致性Hash算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即沿著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。簡單說,就是C掛了,受到影響的只是B、C之間的數據且這些數據會轉移到D進行存儲。
擴展性
數據量增加了,需要增加一臺節點NodeX,X的位置在A和B之間,那收到影響的也就是A到X之間的數據,重新把A到X的數據錄入到X上即可,
不會導致hash取余全部數據重新洗牌。
(5)缺點
Hash環的數據傾斜問題
一致性Hash算法在服務節點太少時,容易因為節點分布不均勻而造成數據傾斜(被緩存的對象大部分集中緩存在某一臺服務器上)問題,
例如系統中只有兩臺服務器:
(6)小總結
為了在節點數目發生改變時盡可能少的遷移數據
將所有的存儲節點排列在收尾相接的Hash環上,每個key在計算Hash后會順時針找到臨近的存儲節點存放。
而當有節點加入或退出時僅影響該節點在Hash環上順時針相鄰的后續節點。
優點
加入和刪除節點只影響哈希環中順時針方向的相鄰的節點,對其他節點無影響。
缺點
數據的分布和節點的位置有關,因為這些節點不是均勻的分布在哈希環上的,所以數據在進行存儲時達不到均勻分布的效果。
3.5.3 哈希槽分區
(1) 為什么出現
哈希槽實質就是一個數組,數組[0,2^14 -1]形成hash slot空間。
(2) 能干什么
解決均勻分配的問題,在數據和節點之間又加入了一層,把這層稱為哈希槽(slot),用于管理數據和節點之間的關系,現在就相當于節點上放的是槽,槽里放的是數據。
槽解決的是粒度問題,相當于把粒度變大了,這樣便于數據移動。哈希解決的是映射問題,使用key的哈希值來計算所在的槽,便于數據分配
(3) 多少個hash槽
一個集群只能有16384個槽,編號0-16383(0-2^14-1)。這些槽會分配給集群中的所有主節點,分配策略沒有要求。
集群會記錄節點和槽的對應關系,解決了節點和槽的關系后,接下來就需要對key求哈希值,然后對16384取模,余數是幾key就落入對應的槽里。HASH_SLOT = CRC16(key) mod 16384。以槽為單位移動數據,因為槽的數目是固定的,處理起來比較容易,這樣數據移動問題就解決了。
(4)hash槽計算
Redis 集群中內置了 16384 個哈希槽,redis 會根據節點數量大致均等的將哈希槽映射到不同的節點。當需要在 Redis 集群中放置一個 key-value時,redis先對key使用crc16算法算出一個結果然后用結果對16384求余數[ CRC16(key) % 16384],這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,也就是映射到某個節點上。如下代碼,key之A 、B在Node2, key之C落在Node3上
3.6 經典面試題 為什么redis集群的最大槽數是16384個
3.6.1 為什么redis集群的最大槽數是16384個?
Redis集群并沒有使用一致性hash而是引入了哈希槽的概念。Redis 集群有16384個哈希槽,每個key通過CRC16校驗后對16384取模來決定放置哪個槽,集群的每個節點負責一部分hash槽。但為什么哈希槽的數量是16384(2^14)個呢?
CRC16算法產生的hash值有16bit,該算法可以產生2^16=65536個值。
換句話說值是分布在0~65535之間,有更大的65536不用為什么只用16384就夠?
作者在做mod運算的時候,為什么不mod65536,而選擇mod16384?
HASH_SLOT = CRC16(key) mod 65536為什么沒啟用
https://github.com/redis/redis/issues/2576
3.6.2 說明1
正常的心跳數據包帶有節點的完整配置,可以用冪等方式用舊的節點替換舊節點,以便更新舊的配置。這意味著它們包含原始節點的插槽配置,該節點使用2k的空間和16k的插槽,但是會使用8k的空間(使用65k的插槽)。同時,由于其他設計折衷,Redis集群不太可能擴展到1000個以上的主節點。因此16k處于正確的范圍內,以確保每個主機具有足夠的插槽,最多可容納1000個矩陣,但數量足夠少,可以輕松地將插槽配置作為原始位圖傳播。請注意,在小型群集中,位圖將難以壓縮,因為當N較小時,位圖將設置的slot / N位占設置位的很大百分比。
3.6.3 說明2
(1) 如果槽位為65536,發送心跳信息的消息頭達8k,發送的心跳包過于龐大。
在消息頭中最占空間的是myslots[CLUSTER_SLOTS/8]。 當槽位為65536時,這塊的大小是: 65536÷8÷1024=8kb
在消息頭中最占空間的是myslots[CLUSTER_SLOTS/8]。 當槽位為16384時,這塊的大小是: 16384÷8÷1024=2kb
因為每秒鐘,redis節點需要發送一定數量的ping消息作為心跳包,如果槽位為65536,這個ping消息的消息頭太大了,浪費帶寬。
(2) redis的集群主節點數量基本不可能超過1000個。
集群節點越多,心跳包的消息體內攜帶的數據越多。如果節點過1000個,也會導致網絡擁堵。因此redis作者不建議redis cluster節點數量超過1000個。 那么,對于節點數在1000以內的redis cluster集群,16384個槽位夠用了。沒有必要拓展到65536個。
(3) 槽位越小,節點少的情況下,壓縮比高,容易傳輸
Redis主節點的配置信息中它所負責的哈希槽是通過一張bitmap的形式來保存的,在傳輸過程中會對bitmap進行壓縮,但是如果bitmap的填充率slots / N很高的話(N表示節點數),bitmap的壓縮率就很低。 如果節點數很少,而哈希槽數量很多的話,bitmap的壓縮率就很低。