开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 (加他微信) ,在新加的朋友会分到3群(共1000人左右 1 + 2 + 3)
本篇非本人撰写,在本人后面有一个强大的DBA TEAM,本篇来自于本TEAM 中的 REDIS DBA 小闫,如想和他讨论REDIS 数据库的使用和理解以及更深的使用,可以加群。
阿里云的redis集群架构压测时候,会有Proxy,压测的时候,我们需要带着proxy压测,好在redis-benchmark压测工具最新版本支持了—cluster参数,能够支持集群的压测。但是有个坏消息是由于带有了proxy,redis-benchmark不能获取槽位信息,这就导致了在进行压测的时候,所有的请求仍然都是发送到集群中的一个节点,就会出现压测的时候出现一个节点cpu报表,但是其他cpu仍然在0附近徘徊。
首先需要了解redis-benchmark加了—cluster之后做了什么。
加了—cluster参数以后,cluster模式被打开,然后写入和读取的时候key会被加上hashtag,也就是Key中的一部分被{}包裹,这内部的东西会被计算哈希值,然后根据这个哈希值确定槽位,然后确定这个请求发送到某个node。简单来说,具有相同hashtag的key会被分到同一个node上。
加上了—cluster以后,redis-benchmark通过获取槽位信息,确定有多少个节点,就会选择有几个不同的hashtag,然后将请求分发到这些节点上。
然而上面说了,加了阿里云加了proxy的redis集群无法获取节点和槽位信息,我们只能看到一个节点。
那么,需要对redis-benchmark的源码做一些小改动。
改动不多
首先,如果直接开启—cluster并运行redis-benchmark的话,会直接报错提示:
"Invalid cluster: 1 node(s).
也就是只获取到了一个节点,正常的cluster是不可能只有1个节点的。
找到这行代码
if (config.cluster_node_count <= 1) {
fprintf (stderr, "Invalid cluster: %d node(s).\n" ,
config.cluster_node_count);
exit (1);
}
|
这里直接将小于等于换成<即可,或者这几行直接注释掉。
然后我们如果有四个节点,则需要将再复制出三个节点的配置信息。
继续向下
printf ( "Cluster has %d master nodes:\n\n" , config.cluster_node_count);
int i = 0;
//这里开始是添加的代码
config.cluster_nodes[0]->myflag=0;
config.cluster_nodes[1] = malloc ( sizeof (clusterNode));
memcpy (config.cluster_nodes[1], config.cluster_nodes[0], sizeof (clusterNode));
config.cluster_nodes[2] = malloc ( sizeof (clusterNode));
memcpy (config.cluster_nodes[2], config.cluster_nodes[0], sizeof (clusterNode));
config.cluster_nodes[3] = malloc ( sizeof (clusterNode));
memcpy (config.cluster_nodes[3], config.cluster_nodes[0], sizeof (clusterNode));
config.cluster_nodes[1]->myflag=1;
config.cluster_nodes[2]->myflag=2;
config.cluster_nodes[3]->myflag=3;
config.cluster_node_count=4;
//到这里结束
for (; i < config.cluster_node_count; i++) {
clusterNode *node = config.cluster_nodes[i];
if (!node) {
fprintf (stderr, "Invalid cluster node #%d\n" , i);
exit (1);
}
printf ( "Master %d: " , i);
if (node->name) printf ( "%s " , node->name);
printf ( "%s:%d\n" , node->ip, node->port);
node->redis_config = getRedisConfig(node->ip, node->port, NULL);
if (node->redis_config == NULL) {
fprintf (stderr, "WARNING: Could not fetch node CONFIG %s:%d\n" ,
node->ip, node->port);
}
}
printf ( "\n" );
|
这里添加了注释段的几行代码,其实就是强行修改config.cluster_nodes数组,原本是就有1个,我们复制出3个,这时候就有4个节点了,虽然他们连接地址都一样,但是后面我们需要通过手动构造tag来将他们区分开来。而通过什么来区分,需要有个标记,这里我添加了一个myflag属性作为一个标识。
这个需要在
/* Cluster. */
typedef struct clusterNode {
char *ip;
int port;
sds name;
int flags;
sds replicate; /* Master ID if node is a slave */
int *slots;
int slots_count;
int current_slot_index;
int *updated_slots; /* Used by updateClusterSlotsConfiguration */
int updated_slots_count; /* Used by updateClusterSlotsConfiguration */
int replicas_count;
sds *migrating; /* An array of sds where even strings are slots and odd
* strings are the destination node IDs. */
sds *importing; /* An array of sds where even strings are slots and odd
* strings are the source node IDs. */
int migrating_count; /* Length of the migrating array (migrating slots*2) */
int importing_count; /* Length of the importing array (importing slots*2) */
struct redisConfig *redis_config;
int myflag;
} clusterNode;
|
这个结构体最后添加一个int类型的字段,myflag。
然后通过分析代码,我们可以找到一个函数,函数名字叫做setClusterKeyHashTag,通过名字我们也能知道,这个就是设置hashtag的地方。
修改这个函数为下面的函数即可
static void setClusterKeyHashTag(client c) {
assert (c->thread_id >= 0);
clusterNode *node = c->cluster_node;
assert (node);
assert (node->current_slot_index < node->slots_count);
int is_updating_slots = 0;
atomicGet(config.is_updating_slots, is_updating_slots);
/* If updateClusterSlotsConfiguration is updating the slots array,
* call updateClusterSlotsConfiguration is order to block the thread
* since the mutex is locked. When the slots will be updated by the
* thread that's actually performing the update, the execution of
* updateClusterSlotsConfiguration won't actually do anything, since
* the updated_slots_count array will be already NULL. */
if (is_updating_slots) updateClusterSlotsConfiguration();
int slot = node->slots[node->current_slot_index];
const char *tag;
int taglen;
if (node->myflag == 1) {
// 根据 myflag 的值设置特定的标签
tag = "g1" ;
taglen = strlen (tag);
} else if (node->myflag == 2) {
// 根据 myflag 的值设置特定的标签
tag = "a2" ;
taglen = strlen (tag);
} else if (node->myflag==3){
tag = "a3" ;
taglen = strlen (tag);
} else {
// 使用默认的标签
tag = "a5" ;
taglen = strlen (tag);
}
size_t i;
for (i = 0; i < c->staglen; i++) {
char *p = c->stagptr[i] + 1;
p[0] = tag[0];
p[1] = (taglen >= 2 ? tag[1] : '}' );
//p[2] = (taglen == 3 ? tag[2] : '}');
}
}
|
这里的g1,a2,a3,a5,其实都是我自己试出来的,因为我们也看不到具体某个节点有哪个槽,所以只能通过压测以后查看cpu上升情况或者阿里云监测上面的key个数来获取这个信息。
修改完成以后make&&make install即可。
运行命令的时候开启—cluster即可看到命令被平均发送到每个节点了,cpu的计算压力和内存的存储压力已经被分散到四个节点上面了。