首页 > 编程知识 正文

redisbenchmark基准性能测试,redis缓存性能测试

时间:2023-05-05 08:45:49 阅读:221544 作者:4728

这篇文章总结的是Redis的benchmark源码修改中增加一些性能测试指标所应思考的问题,以及解决问题的方法,权当本渣渣一篇笔记,如果您发现有疏漏错误之处,烦请留言指出!

如不清楚吞吐率(RPS)、平均响应时间、99%响应时间的概念请参考:性能测试的几个指标(并发数、吞吐率、响应时间、平均响应时间、99%响应时间)

1、原生Redis的两种主要性能测试指标

在原生的Redis benchmark性能测试中,只提供了两种主要的测试指标:

一是RPS(requests per second),即每秒完成的请求数量,反映的是服务器处理请求的吞吐率,这是redis最主要的性能测试指标。

二是,洁净的御姐不使用quiet模式输出(不使用-q参数)时,redis会展示出响应时间低于1ms、2ms、3ms...等的请求数占总请求数的百分比。如下图所示。

下面我们分别说明以上两种测试指标的实现方式:

首先我们需要知道benchmark.c文件的一个重要的数据结构static struct config。

本文所讲的性能测试指标的最红显示和实现都是在redis-benchmark.c文件中的showLatencyReport函数中实现,所有修改也都在此函数中进行。

static struct config { aeEventLoop *el; const char *hostip; int hostport; const char *hostsocket; int numclients; int liveclients; int requests;//总请求数 int requests_issued; int requests_finished;//已完成的请求数 int keysize; int datasize; int randomkeys;//1表示key值随机化 int randomkeys_keyspacelen;//key值取0~该值中的随机数 int keepalive; int pipeline; int showerrors; long long start;//性能测试前记录开始时间 long long totlatency;//记录性能测试所用的总延时 long long *latency;//算是一个数组指针,在堆空间开辟,存放每一笔请求的延时,一共有config.requests个 const char *title; list *clients; int quiet; int csv; int loop; int idlemode; int dbnum; sds dbnumstr; char *tests; char *auth;} config;

主要关注requests、 start、totlatency、*latency!

(1)RPS的实现:总请求数 / 性能测试的总延时, 即 

reqpersec = (float)config.requests_finished/((float)config.totlatency/1000);

(2)响应时间低于1ms、2ms、3ms...等的请求数占总请求数的百分比实现:先对latency数组中的所有请求的响应时间进行从小到大排序,然后遍历,当遍历到响应时间低于1ms时计算响应百分比即可,以此类推。

原生Redis实现的代码如下(以上两种指标的实现)

//定义比较函数static int compareLatency(const void *a, const void *b) { return (*(long long*)a)-(*(long long*)b);}static void showLatencyReport(void) { int i, curlat = 0; float perc, reqpersec; reqpersec = (float)config.requests_finished/((float)config.totlatency/1000); if (!config.quiet && !config.csv) { printf("====== %s ======n", config.title); printf(" %d requests completed in %.2f secondsn", config.requests_finished, (float)config.totlatency/1000); printf(" %d parallel clientsn", config.numclients); printf(" %d bytes payloadn", config.datasize); printf(" keep alive: %dn", config.keepalive); printf("n"); qsort(config.latency,config.requests,sizeof(long long),compareLatency);//对每条请求的响应时间进行升序排序 for (i = 0; i < config.requests; i++) {//打印出低于1ms、2ms...等的请求数与总请求数的百分比 if (config.latency[i]/1000 != curlat || i == (config.requests-1)) { curlat = config.latency[i]/1000; perc = ((float)(i+1)*100)/config.requests; printf("%.2f%% <= %d millisecondsn", perc, curlat); } } printf("%.2f requests per secondnn", reqpersec);//打印rps } else if (config.csv) { printf(""%s","%.2f"n", config.title, reqpersec); } else { printf("%s: %.2f requests per secondn", config.title, reqpersec);} 2、添加平均响应时间、99%响应时间 平均响应时间的实现

平均响应时间=总响应时间÷总请求数, 公式很简单,但是我们深入思考会发现一些问题:刚开始时我把总响应时间误以为就是上面的totlatency,后来发现不对,这里说清楚区别:

totlatency是在benchmark测试前后做的一个时间记录差值,即相当于服务器处理完requests条请求所花费的时间,这里面也包含了一部分客户端的函数执行的时间开销(我测试时使用单个客户端连接(即只有一个并发连接)、不使用管道pipelining的时候,测试出来的totlatency是大于【把latency数组中所有请求的响应时间相加所得sum】)。

所以把latency数组中所有请求的响应时间相加所得sum(我们姑且称之为响应时间之和),跟延迟差值totlatency是不一样的!

响应时间之和是所有请求的响应时间加到一块,而totlatency只是测试开始前后的一个时间差!但是你千万别以为响应时间之和就应该小于或者等于totlatency(因为你在测试过程中也有客户端的一些函数时间执行开销),我刚开始就是这样认为的!这种情况仅仅是当只有一个并发连接,并且不使用pipeline的时候,它确实是这样的!

当不止一个并发连接的时候,客户端怎么操作的呢? 假如说有10个并发连接客户端,那么这十个客户端就会逐个发送请求,哪个客户端收到请求回复了就再接着发请求,一直到请求数全部得到回复。这里模拟的是10个客户端并发连接请求的情况。所以这样你会发现:最后算的响应时间之和是远远大于totlatency的!

当使用pipelining的时候呢?假如-P的参数是5,即一条管道可以一次放5条请求。使用管道其实就是每次客户端把5个请求打包到一起然后一块发给服务器进行处理,服务器处理完之后把五个请求的结果再打包到一起返回给客户端。所以五条请求的响应时间是一样的!那这个时候五条请求的响应时间之和怎么算呢?是五个请求的响应时间全加起来,还是只算一个响应时间呢? 自习想一样,应该是把5个请求看做一个整体,他们的响应时间之和就是其中一个的响应时间。为什么这样算呢?很明显人家五个一起花了5ms,你不能说人家五个一共花了25ms吧! 比如说5个人团购一张券吃饭花了5块,那么每个人实质上是只用了1块,五个人一共用5块,而不是25块。所以当我们使用管道的时候,求平均响应时间和99%响应时间的时候需要整体除以管道大小pipeline。

99%响应时间的实现

先排序,然后找到99%所在的位置,把对应位置的响应时间记录即可。

修改后的实现代码

static void showLatencyReport(void) { int i, curlat = 0; float perc, reqpersec; reqpersec = (float)config.requests_finished/((float)config.totlatency/1000); if (!config.quiet && !config.csv) { printf("====== %s ======n", config.title); printf(" %d requests completed in %.2f secondsn", config.requests_finished, (float)config.totlatency/1000); printf(" %d parallel clientsn", config.numclients); printf(" %d bytes payloadn", config.datasize); printf(" keep alive: %dn", config.keepalive); printf("n"); qsort(config.latency,config.requests,sizeof(long long),compareLatency);//对每条请求的响应时间进行升序排序long long sumLatency = 0;//响应时间之和 float avg, percentile99 = (float)config.latency[config.requests - 1]/1000;//平均响应时间、99%响应时间(初始化为最后一个请求的延迟),单位msint flag = 0;//临时辅助标志位 for (i = 0; i < config.requests; i++) {sumLatency += config.latency[i];//requests条命令的响应时间总和if(flag == 0 && (float)(i + 1)/config.requests >= (float)0.99) { percentile99 = (float)config.latency[i]/1000;//99%响应时间flag = 1;} if (config.latency[i]/1000 != curlat || i == (config.requests-1)) {//打印出低于1ms、2ms...等的请求数与总请求数的百分比 curlat = config.latency[i]/1000; perc = ((float)(i+1)*100)/config.requests; printf("%.2f%% <= %d millisecondsn", perc, curlat); } }avg = (float)sumLatency/((float)config.requests*1000);//requests条请求的平均响应时间,单位是msif(config.pipeline > 1) {//考虑使用管道的特殊情况avg /= config.pipeline;percentile99 /= config.pipeline;}//printf("总延迟: %lldms 响应时间之和: %lldmsn", config.totlatency, sumLatency/1000); printf("%.2f requests per second | average response time: %.2fms | 99%%response time: %.2fms nn", reqpersec, avg, percentile99); } else if (config.csv) { printf(""%s","%.2f"n", config.title, reqpersec); } else { printf("%s: %.2f requests per secondn", config.title, reqpersec); }}

 

 

 

 

 

margin

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。