這節介紹Redis的集群模式
主從模式提供了讀寫分離的支持,Sentinel提供了高可用的保障,滿足了讀模式下的橫向擴展,但主節點只有一個,集中式的寫模式無法應對不斷增長的寫需求。
Redis 3.x 版本提供了Redis cluster
功能,服務端sharding使用槽作為分布式的解決方案。對於 2.x版本,則通過客戶端API提供的客戶端sharding方式,使用一致性哈希來實現數據分片。此外,twiter開源的twemproxy
和豌豆莢的codis
,則采用代理模式來實現。
1 服務端模式
對於客戶端來說,整個集群被看做一個整體,客戶端可以連接任意一個節點進行操作,就像操作單一Redis數據庫一樣。當客戶端操作的key沒有分配到該節點上時,會返回轉向指令,指向正確的節點。
1.1 集群的建立
3.x 版本的Redis節點可以通過打開clustrer-enabled
選項來開啟服務器的集群模式,再通過
CLUSTER MEET <ip> <port>
命令連接其他節點。
新加入的節點B通過CLUSTER MEET
命令同節點A建立連接后,節點A會通過Gossip協議
將節點B的信息傳播給集群中的其他節點,當其他節點發現新加入的節點B后也會主動同它建立連接。處於集群狀態的節點同單機模式下的服務器實例沒什么區別,只是只能使用db 0
數據庫,同時除了執行單機模式下的任務外還會執行集群所需的任務,如Gossip的消息的傳播。
1.2 槽指派
Redis集群使用槽來存儲分片信息,集群中的每個節點負責處理16384個槽中的一部分。對於每個寫操作,通過計算crc(key)%16384
對key值進行hashing,分配到16384個槽中的一個,然后由對應的節點處理。
節點負責哪些槽信息可以通過向節點發送
CLUSTER ADDSLOTS <slot> [slot …]
來指派。分配完槽信息后,該節點會將自身的信息傳播給集群中的機器節點,使得集群中的所有節點都有全部的槽分配信息。當16384個槽都有節點在處理時,節點處於上線狀態;相反的,如果有任何一個槽沒有得到處理,那么集群處於下線狀態。
集群中的某個節點在收到一個請求時,會先判斷是否由自己來處理,如果不是,則會向客戶端返回
MOVE <slot> <ip>:<port>
錯誤命令,該命令給出了請求所屬的槽以及對應節點的ip和端口號。客戶端在收到MOVE
錯誤時,會轉到對應的節點重新發送之前的命令。注意,需要使用客戶端的集群模式才會自動跳轉,如下:
redis-cli -c -h xxx -p xxx
```
 當動態添加或者減少節點時,需要將16384個槽做重新分片,槽中的鍵值也要遷移。重新分片可以在線執行,使用自帶的```redis-trib```工具。
##### 1.3 高可用和故障轉移
 Redis集群,要保證```16384```個槽對應的節點都正常工作,如果某個節點發生故障,那它負責的槽也就失效,整個集群將不能工作。可以使
```
CLUSTER REPLICATE <node_id>
```
將節點配置成主從結構來增加節點的可用行。
 集群中的每個節點會定期的向其他節點發送PING消息來檢測對方的狀態,一個節點的狀態可以為```在線```,```疑似下線```和```下線```。當某個節點n發現半數以上負責處理槽的主節點都將某個主節點x標記為疑似下線狀態時,節點n將節點x標記為下線狀態,並向集群廣播消息。
 當節點x的從節點收到主節點下線的消息時,會對該節點進行故障轉移。同Sentinel選舉新的leader Sentinel節點一樣,節點x的從節點會向集群中負責處理槽的其余主節點獲取選票,以在節點x的從節點中選出新的主節點。新的主節點會撤銷已下線主節點的槽指派,並分配給自己,然后向集群中的其他節點廣播自己為新主節點的消息。
#### 2 客戶端模式
 客戶端模式下每個節點都是單一的實例,需要由客戶端應用自己管理key所在的分片,以及處理節點的故障轉移,Redis的客戶端Jedis提供了分片的支持。

 Jedis對Sharded的實現主要是在```ShardedJedis.java```和```ShardedJedisPool.java```中,可以見[這里](https://segmentfault.com/a/1190000002691429)。
 Jedis的Redis Sharding實現具有如下特點:
1. 采用[一致性哈希算法](http://www.cnblogs.com/lpfuture/p/5796398.html),將key和節點name同時hashing,然后進行映射匹配,采用的算法是```MURMUR_HASH```。采用一致性哈希而不是采用簡單類似哈希求模映射的主要原因是當增加或減少節點時,不會產生由於重新匹配造成的rehashing。一致性哈希只影響相鄰節點key分配,影響量小。
2. 為了避免一致性哈希只影響相鄰節點造成節點分配壓力,ShardedJedis會對每個Redis節點根據名字(沒有的話,Jedis會賦予缺省名字)會虛擬化出160個虛擬節點進行散列。根據權重也可虛擬化出160倍數的虛擬節點。用虛擬節點做映射匹配,可以在增加或減少Redis節點時,key在各 Redis節點移動再分配更均勻,而不是只有相鄰節點受影響。
3. Sharded Jedis支持```keyTagPattern```模式,即抽取key的一部分keyTag做sharding,這樣通過合理命名key,可以將一組相關聯的key放入同一個Redis節點,這在避免跨節點訪問相關數據時很重要。
#### 3 代理模式
 通過在客戶端和實際Redis服務中間增加代理層,代理模塊實現Redis協議,客戶端連接代理和連接原生的Redis實例沒有什么區別,上層應用可以像使用單機的Redis一樣使用,代理會處理請求的轉發,不停機的數據遷移等工作,所有后邊的一切事情,對於前面客戶端來說是透明的,可以簡單的認為后邊連接是一個內存無限大的Redis服務。

 Twitter的```twemproxy```和豌豆莢的```codis```就是該模式的實現。關於```codis```的介紹,可以看[這里]( http://www.cnblogs.com/wangdaijun/p/6156397.html)。
 同前面兩種模式相比,代理模式的好處在於,客戶端和服務端無需作出變更,主要維護工作在於代理模塊中;同后端實際實例的連接數落在了代理上,能夠有效的控制客戶端過多造成的連接數暴增;提供sharding功能,支持服務器集群水平擴展。同時,由於增加了代理,客戶端到真正的實例服務器需要走兩次網絡。

個人公眾號:啊駝