Jedis分片鏈接池(分佈式)

一下內容來自網絡,可是不少細節沒有寫出來,因此我通過本身琢磨,終於找到緣由了。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位置來計算,我大概看了一下其源碼以下:

jedis源碼中ShardedJedis實現sharding

 

 

(若是親仍是不信的話,能夠將上面程序中的 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),這樣才能保證修復期間新增數據的一致性。

相關文章
相關標籤/搜索