Redis數據分片

轉自:http://m.blog.csdn.net/blog/hjx_1000/38304081

      http://blog.csdn.net/lang_man_xing/article/details/38405269


Redis的分片(Sharding或者Partitioning)技術是指將數據分散到多個Redis實例中的方法,分片之後,每個redis擁有一部分原數據集的子集。在數據量非常大時,這種技術能夠將數據量分散到若干主機的redis實例上,進而減輕單臺redis實例的壓力。分片技術能夠以更易擴展的方式使用多臺計算機的存儲能力(這裏主要指內存的存儲能力)和計算能力:

(1)從存儲能力的角度,分片技術通過使用多臺計算機的內存來承擔更大量的數據,如果沒有分片技術,那麼redis的存儲能力將受限於單臺主機的內存大小。

(2) 從計算能力的角度,分片技術通過將計算任務分散到多核或者多臺主機中,能夠充分利用多核、多臺主機的計算能力。


下面將以舉例的方式說明分片技術及其存在的優勢:

示例1:未採用分片技術,有1000萬條用戶信息數據,以鍵值對:UsrID:UsrInfo的形式存儲在一個redis實例中,此時所有的用戶信息都會存儲在一個redis實例中,對這1000萬條數據的所有插、查、刪、該操作壓力都會集中在這個redis所在的主機上;此時所要考慮的問題不僅有存儲和操作對該主機的壓力,還有該主機失效時將導致所有操作都無法進行的問題。

示例2:採用分片技術;有1000萬條用戶信息數據,以鍵值對:UsrID:UsrInfo的形式存儲於redis中,此時有4臺主機,每臺主機運行一個Redis實例:主機A (Redis1)、主機B(Redis2)、主機C(Redis3)、主機D(Redis4),分片時算法爲:

redis_index = 用戶的ID % 4 + 1;

例如ID爲10000654則可得到到redis_index的值:10000654 % 4 + 1 = 1,即用戶10000654的信息將被放到Redis1上,所有對用戶1000654的操作也將被分片到Redis1上;假如用戶ID以順序方式出現,這1000萬條用戶信息將被平均分配到這四臺主機的各Redis實例上。

採用分片算法

採用分片之後,數據將被分散到4個redis實例中,對數據的操作也被分散到每個redis實例中,此時單臺主機的壓力將大大減輕。

分片的部署,即實例2中分片算法被放在哪裏?是在分片時需要首先考慮的問題,分片部署方式一般分爲以下三種:

(1)在客戶端做分片;這種方式在客戶端確定要連接的redis實例,然後直接訪問相應的redis實例;

(2)在代理中做分片;這種方式中,客戶端並不直接訪問redis實例,它也不知道自己要訪問的具體是哪個redis實例,而是由代理轉發請求和結果;其工作過程爲:客戶端先將請求發送給代理,代理通過分片算法確定要訪問的是哪個redis實例,然後將請求發送給相應的redis實例,redis實例將結果返回給代理,代理最後將結果返回給客戶端。

(3)在redis服務器端做分片;這種方式被稱爲“查詢路由”,在這種方式中客戶端隨機選擇一個redis實例發送請求,如果所請求的內容不再當前redis實例中它會負責將請求轉交給正確的redis實例,也有的實現中,redis實例不會轉發請求,而是將正確redis的信息發給客戶端,由客戶端再去向正確的redis實例發送請求。

上面主要描述了分片的優點,當然分片的存在也有缺陷,例如:

(1) 通常無法支持涉及多鍵的操作;在redis中有很多一次操作多個key的操作,例如求集合交集的SINTER操作,該操作將涉及到多個鍵,而這多個鍵有可能被分片到不同的redis實例中,此時就無法執行這種操作。

(2)Redis的事務操作中涉及多個鍵時也不能用;

(3)分片將導致數據處理更加複雜;例如在分片過程中,隨着redis實例的增加,數據備份等操作都將會變得更加複雜。

(4)Redis目前不支持動態分片操作,擴容和縮容操作都會比較複雜,尤其分片操作部署在客戶端時,需要重新配置和啓動客戶端。在使用過程中縮容用的不多,擴容可以採用後面介紹的預分片策略來緩解此問題。

預分片技術

因爲使用了一致性哈稀進行分片,那麼不同的key分佈到不同的Redis-Server上,當我們需要擴容時,需要增加機器到分片列表中,這時候會使得同樣的key算出來落到跟原來不同的機器上,這樣如果要取某一個值,會出現取不到的情況。而且在正常運營環境中,一般所存儲的數據會逐漸增加,可能今天只要10個redis實例就能應付,但是到了一年以後就需要50個redis實例才能支撐,因此,redis的擴容是經常用到的功能,在redis的分佈式部署中,有預分片技術是非常好用的方法之一;


預分片技術是指在開始時就啓動足夠多的redis實例(例如32或64個,估計一下夠以後擴展用就行了),等到後續需要擴容的時候,只需要將其中一部分的redis實例轉移到新增加的機子上即可,在redis實例遷移過程中使用redis的複製功能可以最大限度的降低redis的停工時間甚至可以做到沒有停工時間。由於redis實例是輕量級的進程,而且佔用內存較少,這裏指單純的空的redis實例,一個空的redis實例大約佔用1M的內存;因此,這種方式即不會佔用太多系統開銷,又便於實現;

Redis的預分片技術可以按照以下步驟進行實例遷移操作:

(1)在新機子上啓動新的redis實例;

(2)將新redis實例作爲slave將原redis實例作爲master,將數據從原redis實例遷移到新redis實例上;

(3)停止客戶端(分片操作在客戶端上時)或代理服務器(分片操作在代理上)

(4)更新客戶端或者代理服務器中的配置信息,去掉被遷移的原redis實例的ip和端口等信息,加上新啓動redis實例的IP地址和端口;

(5)向新啓動的redis發送SLAVEOF NOONE命令,終止新redis實例對原redis實例的從屬關係;

(6)重啓客戶端程序或者代理程序,此時它們將會使用新的redis實例;

(7)關掉被遷移走數據的原redis實例;


Jedis分片連接池

Jedis通過一致性哈希分片算法來實現數據分片,一致性哈希算法http://blog.csdn.net/cywosp/article/details/23397179/。簡單用Jedis分片連接池來實現多Redis的master節點進行數據分片,步驟如下:

1. 部署2個Redis master節點。

2. 用ShardedJedisPool連接Redis maser節點,並將數據根據一致性哈希算法存放到不同的master節點,從而達成數據分片。

客戶端代碼如下:

public class RedisShardPoolTest {
    static ShardedJedisPool pool;
    static{
        JedisPoolConfig config =new JedisPoolConfig();//Jedis池配置
        config.setMaxActive(500);//最大活動的對象個數
          config.setMaxIdle(1000 * 60);//對象最大空閒時間
          config.setMaxWait(1000 * 10);//獲取對象時最大等待時間
          config.setTestOnBorrow(true);
        String hostA = "10.7.12.52";
          int portA = 6379;
          String hostB = "10.7.112.52";
          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
     */
    public static void 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);
           }
        }
    }
 
    private static int index = 1;
    public static String generateKey(){
        return String.valueOf(Thread.currentThread().getId())+"_"+(index++);
    }
}
通過執行結果會發現這100條數據會被分開存放到10.7.12.52和10.7.112.52機器上的master節點中,查看源碼會發現jds.set(key,value)方法會調用jds.getShared(key)來根據key的hash值來獲取該key應該存放到那個節點上。



發佈了73 篇原創文章 · 獲贊 18 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章