一下內容來自網絡,可是不少細節沒有寫出來,因此我通過本身琢磨,終於找到緣由了。java
Redis-2.4.15目前沒有提供集羣的功能,Redis做者在博客中說將在3.0中實現集羣機制。目前Redis實現集羣的方法主要是採用一致性哈稀分片(Shard),將不一樣的key分配到不一樣的redis server上,達到橫向擴展的目的。下面來介紹一種比較經常使用的分佈式場景:redis
在讀寫操做比較均勻且實時性要求較高,能夠用下圖的分佈式模式:算法
在讀操做遠遠多於寫操做時,能夠用下圖的分佈式模式:服務器
對於一致性哈稀分片的算法,Jedis-2.0.0已經提供了,下面是使用示例代碼(以ShardedJedisPool爲例):網絡
package com.jd.redis.client;分佈式
import Java.util.ArrayList;spa import java.util.List;.net
import redis.clients.jedis.JedisPoolConfig;server import redis.clients.jedis.JedisShardInfo;對象 import redis.clients.jedis.ShardedJedis; import redis.clients.jedis.ShardedJedisPool; import redis.clients.util.Hashing; import redis.clients.util.Sharded;
publicclass RedisShardPoolTest { static ShardedJedisPoolpool; static{ JedisPoolConfig config =new JedisPoolConfig();//Jedis池配置 config.setMaxActive(500);//最大活動的對象個數 config.setMaxIdle(1000 * 60);//對象最大空閒時間 config.setMaxWait(1000 * 10);//獲取對象時最大等待時間 config.setTestOnBorrow(true); String hostA = "10.10.224.44"; int portA = 6379; String hostB = "10.10.224.48"; int portB = 6379; List<JedisShardInfo> jdsInfoList =new ArrayList<JedisShardInfo>(2); JedisShardInfo infoA = new JedisShardInfo(hostA, portA); infoA.setPassword("redis.360buy"); JedisShardInfo infoB = new JedisShardInfo(hostB, portB); infoB.setPassword("redis.360buy"); jdsInfoList.add(infoA); jdsInfoList.add(infoB);
pool =new ShardedJedisPool(config, jdsInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN); }
/** * @param args */ publicstaticvoid main(String[] args) { for(int i=0; i<100; i++){ String key =generateKey(); //key += "{aaa}"; ShardedJedis jds =null; try { jds =pool.getResource(); System.out.println(key+":"+jds.getShard(key).getClient().getHost()); System.out.println(jds.set(key,"1111111111111111111111111111111")); }catch (Exception e) { e.printStackTrace(); } finally{ pool.returnResource(jds); } } }
privatestaticintindex = 1; publicstatic String generateKey(){ return String.valueOf(Thread.currentThread().getId())+"_"+(index++); } } |
從運行結果中能夠看到,不一樣的key被分配到不一樣的Redis-Server上去了。
總結: 客戶端jedis的一致性哈稀進行分片原理:初始化ShardedJedisPool的時候,會將上面程序中的jdsInfoList數據進行一個算法技術,主要計算依據爲list中的index位置來計算,我大概看了一下其源碼以下:
(若是親仍是不信的話,能夠將上面程序中的 jdsInfoList在add的時候,先add第二個,在add第一個,絕對取不出數據,緣由很簡單,第一次set值的時候,是按list下標來hash計算出一個服務器的,因此取值的時候,list順序不能變更)
實際上,上面的集羣模式還存在兩個問題:
1. 擴容問題:
由於使用了一致性哈稀進行分片,那麼不一樣的key分佈到不一樣的Redis-Server上,當咱們須要擴容時,須要增長機器到分片列表中,這時候會使得一樣的key算出來落到跟原來不一樣的機器上,這樣若是要取某一個值,會出現取不到的狀況,對於這種狀況,Redis的做者提出了一種名爲Pre-Sharding的方式:
Pre-Sharding方法是將每個臺物理機上,運行多個不一樣斷口的Redis實例,假若有三個物理機,每一個物理機運行三個Redis實際,那麼咱們的分片列表中實際有9個Redis實例,當咱們須要擴容時,增長一臺物理機,步驟以下:
A. 在新的物理機上運行Redis-Server;
B. 該Redis-Server從屬於(slaveof)分片列表中的某一Redis-Server(假設叫RedisA);
C. 等主從複製(Replication)完成後,將客戶端分片列表中RedisA的IP和端口改成新物理機上Redis-Server的IP和端口;
D. 中止RedisA。
這樣至關於將某一Redis-Server轉移到了一臺新機器上。Prd-Sharding其實是一種在線擴容的辦法,但仍是很依賴Redis自己的複製功能的,若是主庫快照數據文件過大,這個複製的過程也會好久,同時會給主庫帶來壓力。因此作這個拆分的過程最好選擇爲業務訪問低峯時段進行。
再 總結一下這裏的擴容:其實這裏的擴容很簡單的思想:就是前期咱們可能只用到兩三個服務器,可是可是擔憂後期要擴容,因此前期就如今每個機器上面再裝兩個 redis,這樣就有9個redis嘛,後面若是確實服務器不夠,須要擴容,就從新找一臺新機來代替9箇中的一個redis,有人說,這樣不仍是9個麼, 是的,可是之前服務器上面有三個redis,壓力很大的,這樣作,至關於單獨分離出來而且將數據一塊兒copy給新的服務器。值得注意的是,還須要修改客戶 端被代替的redis的IP和端口爲如今新的服務器,只要順序不變,不會影響一致性哈希分片(剛纔上面剛說了哈)。
2. 單點故障問題:
仍是用到Redis主從複製的功能,兩臺物理主機上分別都運行有Redis-Server,其中一個Redis-Server是另外一個的從庫,採用雙機熱備技術,客戶端經過虛擬IP訪問主庫的物理IP,當主庫宕機時,切換到從庫的物理IP。只是過後修復主庫時,應該將以前的從庫改成主庫(使用命令slaveof no one),主庫變爲其從庫(使命令slaveof IP PORT),這樣才能保證修復期間新增數據的一致性。