暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

REDIS 阿里云压测(redis-benchmark源码解读与修改)

AustinDatabases 2023-07-14
538

开头还是介绍一下群,如果感兴趣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的计算压力和内存的存储压力已经被分散到四个节点上面了。

文章转载自AustinDatabases,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论