隨着大型網站數據量和對系統可用性要求的提高,單機版的Redis愈來愈難以知足須要,所以咱們須要使用Redis集羣來提供服務。html
目前主流的Redis集羣解決方案有三類,它們都是經過將key分散到不一樣的redis實例上來提升總體能力, 這種方法稱爲分片(sharding):java
單機版的Redis中單條指令的執行老是原子性的,在集羣中則難以保證這一性質,某些指令可能沒法在集羣中使用或者受到限制。git
若須要使用這些指令或須要它們保持原子性,能夠採用單機版Redis和集羣搭配使用的方法。將主要業務部署在集羣上,將須要較多支持的服務部署在單機版Redis上。github
三種集羣實現方式各有有缺,本文分別進行簡單介紹它們的特性。本文不會詳細介紹三種方式的使用方式,只對其架構和特性進行對比,幫助讀者選擇合適的解決方案。redis
哈希槽(hash slot)是來自Redis Cluster的概念, 但在各類集羣方案都有使用。算法
哈希槽是一個key的集合,Redis集羣共有16384個哈希槽,每一個key經過CRC16散列而後對16384進行取模來決定該key應當被放到哪一個槽中,集羣中的每一個節點負責一部分哈希槽。docker
以有三個節點的集羣爲例:bash
這樣的設計有利於對集羣進行橫向伸縮,若要添加或移除節點只須要將該節點上的槽轉移到其它節點便可。架構
在某些集羣方案中,涉及多個key的操做會被限制在一個slot中,如Redis Cluster中的mget/mset操做。網站
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節點。
爲了便於部署本文使用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中文文檔。
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是豌豆莢開源的代理式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 | 不支持 | 支持 |