Redis Cluster

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

 

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

### Redis Cluster 配置与使用 #### 1. Redis Cluster 的基本配置 为了成功部署并使用 Redis Cluster,需满足以下条件: - **时间同步**:所有节点的时间必须保持一致[^1]。 - **硬件一致性**:建议各节点具有相同的硬件配置以减少性能差异[^1]。 - **版本统一**:所有 Redis 节点应运行相同版本的软件。 以下是 Redis Cluster 启用的关键参数及其作用: - `cluster-enabled yes`:启用集群模式。当该选项被激活时,Redis 将切换到集群模式,并显示为 Cluster 模式下的进程。 - `cluster-config-file nodes-6380.conf`:指定用于存储集群元数据的文件名。此文件由 Redis 自动生成和维护,无需人工干预。 此外,在初始化之前,确保所有的 Redis 实例均为空白状态(即无任何现有数据),这是为了避免潜在的数据冲突或不一致问题[^1]。 #### 2. Redis Cluster 的网络拓扑结构 Redis Cluster 使用分片机制来分布键空间,其典型架构如下所示[^1]: ```plaintext +-------------------+ | Master Node 1 | | (Port: 6379) | +-------------------+ | v +-------------------+ | Slave Node of M1 | | (Port: 6380) | +-------------------+ ... Other Nodes ... ``` 这种设计允许主节点负责写入操作,而从节点可以承担读取请求负载平衡的任务。 #### 3. Kubernetes 外部访问支持 如果希望让 Redis Cluster 可供 Kubernetes 集群之外的服务调用,则可以通过引入中间代理层实现这一目标。例如,利用 `redis-cluster-proxy` 工具完成跨环境通信桥接功能[^2]: 下面是一个典型的 YAML 文件片段定义如何配置此类代理实例: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: redis-proxy namespace: demo data: proxy.conf: | cluster redis-cluster:6379 bind 0.0.0.0 port 7777 threads 8 daemonize no enable-cross-slot yes auth P@ssw0rd log-level error ``` 在此配置下,外部客户端可通过端口号 `7777` 来连接至整个 Redis Cluster 系统。 #### 4. 日志管理 对于生产环境中运行的 Redis Clusters,合理调整日志级别有助于及时发现异常状况并优化运维效率。默认情况下,Redis 不会自动保存详细的执行记录;然而,管理员可以根据实际需求修改相应的设置项以便更好地跟踪系统行为变化趋势[^3]。 #### 5. 扩展性和容量规划 随着业务增长可能需要动态增加新的成员加入现有的 Redis Cluster 中去。此时借助专门开发出来的脚本工具比如 `redis-trib.rb` ,能够简化迁移流程并且降低人为失误风险概率[^4]。 ```bash cp /opt/tools/redis-4.0.12/src/redis-trib.rb /usr/bin/ ./redis-trib.rb add-node --slave new_node_ip existing_master_ip ``` 以上命令演示了向已有的集群添加新副本的具体方法之一[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值