Redis Cluster

本文深入探讨了Redis Cluster的工作原理,包括如何通过CRC16哈希算法将键映射到4096个槽中,以及如何根据这些槽来确定命令应该由哪个节点处理。此外还介绍了Redis Cluster在接收到客户端请求时的处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Redis trunk版本,提供Cluster功能,下面我们来分析一下这个Cluster实现方式


这里首先执行的是 processCommand 命令,代码如下

 

/* If this function gets called we already read a whole
 * command, argments are in the client argv/argc fields.
 * processCommand() execute the command or prepare the
 * server for a bulk read from the client.
 */
int processCommand(redisClient *c) {
   
    /* If cluster is enabled, redirect here */
    if (server.cluster_enabled &&
                !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0)) {
        int hashslot;

        if (server.cluster.state != REDIS_CLUSTER_OK) {
            addReplyError(c,"The cluster is down. Check with CLUSTER INFO for more information");
            return REDIS_OK;
        } else {
            int ask;
            clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&ask);
            if (n == NULL) {
                addReplyError(c,"Multi keys request invalid in cluster");
                return REDIS_OK;
            } else if (n != server.cluster.myself) {
                addReplySds(c,sdscatprintf(sdsempty(),
                    "-%s %d %s:%d\r\n", ask ? "ASK" : "MOVED",
                    hashslot,n->ip,n->port));
                return REDIS_OK;
            }
        }
    }
}

 

在这里我们看到,server.cluster_enabled = True 将直接跳转到,cluster处理逻辑

活动cluster node 的函数是下面这句

 

 clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&ask);

那么redis是如何获得cluster的哪,getNoteByQuery 代码如下

 

 /* This is the first key we see. Check what is the slot
                 * and node. */
firstkey = margv[keyindex[j]];

slot = keyHashSlot((char*)firstkey->ptr, sdslen(firstkey->ptr));
n = server.cluster.slots[slot];
return n
 

  现在我们知道了是通过 keyHashSlot 获得的,那么具体实现哪?

 

unsigned int keyHashSlot(char *key, int keylen) {
    return crc16(key,keylen) & 0x0FFF;
}
 

 哦,原来crc16 但是请注意 &x0FFF, 其实就是 2**12 = 4096 ,将集群的范围控制在了4096个,这个大家要注意啦

 

  那么继续,我们获得了server 节点,接下来就要执行命令了 我们看到的是 addReplySds

 

 

 } else if (n != server.cluster.myself) {
                addReplySds(c,sdscatprintf(sdsempty(),
                    "-%s %d %s:%d\r\n", ask ? "ASK" : "MOVED",
                    hashslot,n->ip,n->port));
                return REDIS_OK;
 }
 

 

下面是 addReplySds 的实现,这里大家对SDS可能有疑惑,SDSLib, A C dynamic strings library

 

void addReplySds(redisClient *c, sds s) {
    if (_installWriteEvent(c) != REDIS_OK) {
        /* The caller expects the sds to be free'd. */
        sdsfree(s);
        return;
    }
    if (_addReplyToBuffer(c,s,sdslen(s)) == REDIS_OK) {
        sdsfree(s);
    } else {
        /* This method free's the sds when it is no longer needed. */
        _addReplySdsToList(c,s);
    }
}
 

将执行的命令放到了buffer里面,也就是放到了远程服务器去执行

/* -----------------------------------------------------------------------------
 * Low level functions to add more data to output buffers.
 * -------------------------------------------------------------------------- */

int _addReplyToBuffer(redisClient *c, char *s, size_t len) {
    size_t available = sizeof(c->buf)-c->bufpos;

    if (c->flags & REDIS_CLOSE_AFTER_REPLY) return REDIS_OK;

    /* If there already are entries in the reply list, we cannot
     * add anything more to the static buffer. */
    if (listLength(c->reply) > 0) return REDIS_ERR;

    /* Check that the buffer has enough space available for this string. */
    if (len > available) return REDIS_ERR;

    memcpy(c->buf+c->bufpos,s,len);
    c->bufpos+=len;
    return REDIS_OK;
}

 

远程服务器执行命令

 

 

/* Exec the command */
    if (c->flags & REDIS_MULTI &&
        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
    {
        queueMultiCommand(c);
        addReply(c,shared.queued);
    } else {
        call(c);
    }
    return REDIS_OK;

 当然具体执行命令的是call 方法,如下

 

/* Call() is the core of Redis execution of a command */
void call(redisClient *c) {
    long long dirty, start = ustime(), duration;

    dirty = server.dirty;
    c->cmd->proc(c);
    dirty = server.dirty-dirty;
    duration = ustime()-start;
    c->cmd->microseconds += duration;
    slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
    c->cmd->calls++;

    if (server.appendonly && dirty)
        feedAppendOnlyFile(c->cmd,c->db->id,c->argv,c->argc);
    if ((dirty || c->cmd->flags & REDIS_CMD_FORCE_REPLICATION) &&
        listLength(server.slaves))
        replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);
    if (listLength(server.monitors))
        replicationFeedMonitors(server.monitors,c->db->id,c->argv,c->argc);
    server.stat_numcommands++;
}


那么到这里就查不多了,还有就是如何添加节点,和添加节点后的变化,大家请看


http://redis.io/presentation/Redis_Cluster.pdf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值