1. 程式人生 > >Redis Cluster原理初步

Redis Cluster原理初步

目錄

目錄 1

1. 前言 1

1. 前言

截至2016/5/16最新版本的redis-3.2.0仍然非強一致性,基於效能考慮master和它的slaves間資料是非同步複製的。另外,一個確定的key總是隻會落到確定的master,除非使用redis-trib.rb等工具修改slotsmaster間的繫結關係,目前的redis cluste不支援自動從一個master遷移一個slot到另一個masterslavesslots來說,可以認為和對應的master相同)。

2. 槽(slots

Redis cluster將所有儲存在其上的key通過一個hash演算法劃分成若干slots

,當前為16384slots,值在cluster.h檔案中由巨集CLUSTER_SLOSTS指定。

3. 路由配置(node.conf

儲存的內容和redis命令“cluster nodes”的輸出相同,即儲存了masterslave資訊,以及各master儲存的slots,亦即slots的路由資訊儲存在node.conf

同一Redis cluster中的所有節點的node.conf檔案內容最終是一致的。

4. 總slots數(cluster.h:16384

#define CLUSTER_SLOTS 16384// 等於(0x3FFF + 1)

巨集CLUSTER_SLOTS定義了

redis clusterslots數,理論上這個值應當可以修改重編譯。其值越大,相對更容易均衡,可支撐更多節點數的叢集(實際受限於無中心節點,當然架構的redis cluster節點數不宜過大,否則可能引起網路風暴)。

5. key的路由

-> key轉成整數值

-> 計算key所在的slot

-> 找到slot所在的masterslavesredis cluster可配置允許slaves提供讀)

-> 轉成直接對masterslaves的請求。

由於任何一個redis cluster節點都儲存了相同內容的node.conf,所以client可以請求任一節點獲得

slots的路由資料。

而且由於node.conf中包含了masterslaves資訊,因此讀寫操作可以完美的路由到相應的節點。

6. 將key轉成整數值(crc16.c:crc16

Redis使用crc演算法將一個字串轉成整數,巨集CLUSTER_SLOTS的值是不能超過CRC返回的最大值。

uint16_t crc16(const char *buf, int len) {

    int counter;

    uint16_t crc = 0;

    for (counter = 0; counter < len; counter++)

            crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];

    return crc;

}

7. 計算key所在slotcluster.c:keyHashSlot

對於一個redis KEY它歸屬於哪一個slot,這個可以通過函式keyHashSlot()呼叫計算出來:

unsigned int keyHashSlot(char *key, int keylen) {

    int s, e; /* start-end indexes of { and } */

    for (s = 0; s < keylen; s++)

        if (key[s] == '{') break;

    /* No '{' ? Hash the whole key. This is the base case. */

    if (s == keylen) return crc16(key,keylen) & 0x3FFF;

    /* '{' found? Check if we have the corresponding '}'. */

    for (e = s+1; e < keylen; e++)

        if (key[e] == '}') break;

    /* No '}' or nothing betweeen {} ? Hash the whole key. */

    if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;

    /* If we are here there is both a { and a } on its right. Hash

     * what is in the middle between { and }. */

    return crc16(key+s+1,e-s-1) & 0x3FFF// 3FFF即為16383

}

8. Redis Cluster Client實現

通過上面的資訊,不然發現,Redis Cluster Client只是在原來單機版client基礎上多了一層薄的路由邏輯。因此可以基於現有的hiredis等實現支援redis clusterclient庫。大致過程如下:

class CRedisClusterClient

{

public:

    // nodes Redis叢集中的單個或多個節點,格式為:ip1:port1,ip2:port2,如:127.0.0.1:6379,127.0.0.1:6380,192.168.31.11:6379

    CRedisClusterClient(const std::string& nodes);

    void set(const std::string& key, const std::string& value) const;

    void get(const std::string& key, std::string* value);

private:

    redisContext* _redis_context; // hiredis

};

set()函式實現:

1) CRedisClusterClient從nodes取任一nodeA,如:127.0.0.1:6380

2) 建立與nodeA的連線

3) 從nodeA取得slots路由資料(實現時可快取這部分資料,以提升效能)

4) 構造slots路由資料表(由於slots總數有限,可以以slot為下標陣列方式組織路由表)

5) 計算key所在的slot

6) 找到slot所在的nodeB(對於寫操作,要求nodeBmaster,有可能碰巧就是nodeA

7) 使用hiredis訪問nodeB(從這步開始和原使用hiredis相同)

8) 取得hiredis返回的結果

如果使用hiredis發生網路異常,對於寫操作從第3步開始重執行,對於讀操作從第6步重選一個node重執行。