Redis集群


隨着大型網站數據量和對系統可用性要求的提升,單機版的Redis越來越難以滿足需要,因此我們需要使用Redis集群來提供服務。

目前主流的Redis集群解決方案有三類,它們都是通過將key分散到不同的redis實例上來提高整體能力, 這種方法稱為分片(sharding):

  • 服務端分片: 客戶端與集群中任意的節點通信,服務端計算key在哪一個節點上,若不再當前節點上則通知客戶端應訪問的節點。 典型代表為官方推出的Redis Cluster
  • 客戶端分片: 客戶端計算key應在集群中的哪一個節點上,並與該節點通信。典型代表為ShardedJedis
  • 代理分片: 客戶端與集群中的代理(proxy)通信,代理與節點通信進行操作。典型代表為Codis

單機版的Redis中單條指令的執行總是原子性的,在集群中則難以保證這一性質,某些指令可能無法在集群中使用或者受到限制。

若需要使用這些指令或需要它們保持原子性,可以采用單機版Redis和集群搭配使用的方法。將主要業務部署在集群上,將需要較多支持的服務部署在單機版Redis上。

三種集群實現方式各有有缺,本文分別進行簡單介紹它們的特性。本文不會詳細介紹三種方式的使用方式,只對其架構和特性進行對比,幫助讀者選擇合適的解決方案。

基本概念

哈希槽

哈希槽(hash slot)是來自Redis Cluster的概念, 但在各種集群方案都有使用。

哈希槽是一個key的集合,Redis集群共有16384個哈希槽,每個key通過CRC16散列然后對16384進行取模來決定該key應當被放到哪個槽中,集群中的每個節點負責一部分哈希槽。

以有三個節點的集群為例:

  • 節點A包含0到5500號哈希槽
  • 節點B包含5501到11000號哈希槽
  • 節點C包含11001到16384號哈希槽

這樣的設計有利於對集群進行橫向伸縮,若要添加或移除節點只需要將該節點上的槽轉移到其它節點即可。

在某些集群方案中,涉及多個key的操作會被限制在一個slot中,如Redis Cluster中的mget/mset操作。

HashTag

HashTag機制可以影響key被分配到的slot,從而可以使用那些被限制在slot中操作。

HashTag即是用{}包裹key的一個子串,如{user:}1, {user:}2

在設置了HashTag的情況下,集群會根據HashTag決定key分配到的slot, 兩個key擁有相同的HashTag:{user:}, 它們會被分配到同一個slot,允許我們使用MGET命令。

通常情況下,HashTag不支持嵌套,即將第一個{和第一個}中間的內容作為HashTag。若花括號中不包含任何內容則會對整個key進行散列,如{}user:

HashTag可能會使過多的key分配到同一個slot中,造成數據傾斜影響系統的吞吐量,務必謹慎使用。

主從模型

幾種流行的Redis集群解決方案都沒有將一個key寫到多個節點中,若某個節點故障則無法訪問訪問其上的key這顯然是不滿足集群的分區容錯性的。

Redis集群使用主從模型(master-slave)來提高可靠性。每個master節點上綁定若干個slave節點,當master節點故障時集群會推舉它的某個slave節點代替master節點。

體驗Redis集群

Redis Cluster

為了便於部署本文使用docker部署了一個簡單集群:

docker run -i -t -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 -p 7006:7006 -p 7007:7007 grokzen/redis-cluster

打開另一個終端窗口, 啟動redis客戶端:

redis-cli -c -p 7000

在redis客戶端中嘗試進行操作:

127.0.0.1:7000> set a 1
-> Redirected to slot [15495] located at 127.0.0.1:7002
OK
127.0.0.1:7002> get a
"1"
127.0.0.1:7002> mset ab 2 ac 3
(error) CROSSSLOT Keys in request don't hash to the same slot
127.0.0.1:7002> mset {a}b 2 {a}c 3
OK

上述示例中, 執行set a命令時客戶端被重定向到了其它節點。

mset ab 2 ac 3命令因為key被分配到不同的slot中導致CROSSSLOT錯誤,而使用HashTag機制mset {a}b 2 {a}c 3就可以解決這個問題。

更多的內容可以參考Redis Cluster中文文檔

ShardedJedis

Jedis是一個流行的Java語言Redis客戶端,在Redis官方提供Redis Cluster之前便實現了客戶端集群功能。

ShardedJedis使用一致性哈希算法進行數據分片,不支持涉及多個key的命令, 其不支持的命令可以參考MultiKeyCommands

JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(2);
poolConfig.setMaxIdle(1);
poolConfig.setMaxWaitMillis(2000);

final String HOST = "127.0.0.1";
JedisShardInfo shardInfo1 = new JedisShardInfo(HOST, 6379);
JedisShardInfo shardInfo2 = new JedisShardInfo(HOST, 6380);
JedisShardInfo shardInfo3 = new JedisShardInfo(HOST, 6381);

ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, Arrays.asList(shardInfo1, shardInfo2, shardInfo3));

try(ShardedJedis jedis = jedisPool.getResource()) {
    jedis.set("a", "1");
    jedis.set("b", "2");
    System.out.println(jedis.get("a"));
}

在初始化ShardedJedisPool時設置keyTagPattern,匹配keyTagPattern的key會被分配到同一個實例中。

Codis

Codis是豌豆莢開源的代理式Redis集群解決方案,因為Twemproxy缺乏對彈性伸縮的支持,很多企業選擇了經過生產環境檢驗的Codis。

Codis的安裝和使用方法可以參考官方文檔, 為了方便起見我們使用ReleaseBinary文件安裝Codis-Server和Codis-Proxy。

或者使用第三方開發者制作的Docker鏡像:

docker run -d --name="codis" -h "codis" -p 18087:18087 -p 11000:11000 -p 19000:19000 ruo91/codis
docker exec codis /bin/bash codis-start all start

使用redis客戶端連接19000端口,嘗試進行操作:

127.0.0.1:19000> set a  1
OK
127.0.0.1:19000> get a
"1"
127.0.0.1:19000> mset ab 2 ac 3
OK
127.0.0.1:19000> mset {a}b 2 {a}c 3
OK

Codis也支持HashTag, 不過Codis已經解決了大多數命令的slot限制。

集群方案對比

協議支持

命令 Redis Cluster ShardedJedis Codis
mget/mset 僅限同一個slot 不支持 失去原子性
keys 僅限同一個slot 不支持 不支持
scan 僅限同一個slot 不支持 僅限同一個slot(SLOTSSCAN命令)
rename 僅限同一個slot 不支持 不支持
pipeline 不支持 不支持 支持
事務 支持相同slot 不支持 不支持
發布/訂閱 支持 不支持 不支持
eval 僅限同一slot 不支持 支持


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM