• Redis Cluster原理初步


    目录

    目录 1

    1. 前言 1

    2. 槽(slots 1

    3. 路由配置(node.conf 1

    4. 总slots数(cluster.h:16384 2

    5. key的路由 2

    6. 将key转成整数值(crc16.c:crc16 2

    7. 计算key所在slotcluster.c:keyHashSlot 2

    8. Redis Cluster Client实现 3

    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重执行。

  • 相关阅读:
    Redis常见的应用场景解析
    技术知识和稳定的系统之间,可能还差这些?
    学会数据库读写分离、分表分库——用Mycat,这一篇就够了!
    程序员也是弱势群体?——从WePhone开发者事件说起
    系统日志管理那点事
    我的Markdown的利器——Markdown Here、有道云笔记、iPic
    推荐几本产品类的书
    华为云的API调用实践(python版本)
    阿里云 API调用实践(python语言)
    HA总结:AWS 网络连接
  • 原文地址:https://www.cnblogs.com/aquester/p/9891529.html
Copyright © 2020-2023  润新知