ssdb get的设计问题
来源:互联网 发布:在线制作淘宝商品图片 编辑:程序博客网 时间:2024/06/08 01:09
背景
对ssdb进行性能测试,当跑以下测试用例时,惊奇发现set的qps跌到了百位数!
get-ssdb-bench没结束之前,切入set-ssdb-bench对get命令测试,并发连接数是200./get-ssdb-bench 127.0.0.1 8888 4000000 200qps: 41877对set命令测试,并发连接数是200./set-ssdb-bench 127.0.0.1 8888 4000000 200qps: 220.02
单独对set命令测试,set命令请求能达到40000+的qps。
分析
ssdb的代码优美,结构清晰。容易找到ssdb处理请求的核心逻辑(src/net/server.cpp)。
//只保留核心代码void NetworkServer::serve(){ //ssdb 根据命令的读写属性,将其放到对应的处理队列。 //命令与队列的对应关系可查serv.cpp的reg_procs函数 //REG_PROC(get, "r");get命令是在此线程处理 //REG_PROC(set, "wt"); set命令异步处理处理 writer = new ProcWorkerPool("writer"); writer->start(num_writers); reader = new ProcWorkerPool("reader"); reader->start(num_readers); ready_list_t ready_list; ready_list_t ready_list_2; ready_list_t::iterator it; const Fdevents::events_t *events; ... while(!quit){ ... ready_list.swap(ready_list_2); ready_list_2.clear(); if(!ready_list.empty()){ // ready_list not empty, so we should return immediately events = fdes->wait(0); }else{ events = fdes->wait(50); } if(events == NULL){ log_fatal("events.wait error: %s", strerror(errno)); break; } for(int i=0; i<(int)events->size(); i++){ const Fdevent *fde = events->at(i); ... }else if(fde->data.ptr == this->reader || fde->data.ptr == this->writer){ //此逻辑很关键。 //当发送队列有消息待发送时,就会触发这个逻辑 ProcWorkerPool *worker = (ProcWorkerPool *)fde->data.ptr; ProcJob job; //注意! //每个epoll loop从发送队列取出一条消息,发送响应。 if(worker->pop(&job) == 0){ log_fatal("reading result from workers error!"); exit(0); } if(proc_result(&job, &ready_list) == PROC_ERROR){ // } }else{ proc_client_event(fde, &ready_list); } } for(it = ready_list.begin(); it != ready_list.end(); it ++){ ... ProcJob job; job.link = link; this->proc(&job); if(job.result == PROC_THREAD){ fdes->del(link->fd()); continue; } if(job.result == PROC_BACKEND){ fdes->del(link->fd()); this->link_count --; continue; } if(proc_result(&job, &ready_list_2) == PROC_ERROR){ // } } // end foreach ready link }}//请求处理函数void NetworkServer::proc(ProcJob *job){ ... if(cmd->flags & Command::FLAG_THREAD){ //如果命令需要放到线程中执行 if(cmd->flags & Command::FLAG_WRITE){ //如果命令是写命令 job->result = PROC_THREAD; writer->push(*job); //set请求会放到这个处理队列 }else{ //如果命令是读命令。 job->result = PROC_THREAD; reader->push(*job);//get命令的处理逻辑不走这里 } return; } proc_t p = cmd->proc; job->time_wait = 1000 * (millitime() - job->stime); job->result = (*p)(this, job->link, *req, &resp);//get请求会就地处理 job->time_proc = 1000 * (millitime() - job->stime) - job->time_wait; ...}
从上述代码可以获得几个重要信息:
- get请求在epoll loop就地处理,set请求异步处理。
- 每次epoll loop 从set处理队列取出一条响应消息,发送。
那么当有400个get并发连接时,会出现什么情况?
一个set请求过来,放到异步队列,接下来整个线程要处理400个get请求。就算set请求处理非常快,也要等下一个epoll loop才有机会把消息发送出去。
因此,上面bench出现的低qps是因为,处理完set请求-> 发送消息,这之间有较大的延迟。
解决方法
最简单有效的方法就是让get不要在epoll线程处理。
方法很简单,在(src/serv.cpp) 把 REG_PROC(get, “r”) 改为 REG_PROC(get, “rt”)。
再跑bench
./get-ssdb-bench 127.0.0.1 8888 4000000 200qps: 41877./set-ssdb-bench 127.0.0.1 8888 4000000 200qps: 23454
PS,给ssdb的作者提issue,希望在项目代码中做这个修改。(Edit in 2015-05-19, 作者已采纳修改)
Q&A
- 增加set请求的并发连接数能改善吗?
不能。因为每次epoll loop 都只会从set处理队列取一条响应消息,并且ssdb默认的处理写请求的线程只有一条。因此无补于事,甚至会造成更大的延迟。
此问题在一般的bench里面暴露不出来,希望这篇文章能够帮助大家。
0 0
- ssdb get的设计问题
- ssdb 遇到的一个问题
- 关于ssdb编译的小问题-cannot stat `ssdb-server': No such file or directory
- SSDB分布式并发锁设计
- SSDB分布式并发锁设计
- SSDB的安装配置
- SSDB的介绍
- ssdb队列的使用
- ssdb
- ssdb
- SSDB
- SSDB与Redis的区别
- ssdb中TTL的使用
- 关于ssdb的一点想法
- 关于SSDB的网络模型
- 基于SSDB的分布式锁
- SSDB数据库的基本操作
- POST,GET的问题
- PHP笔记-自动加载函数库与扩展函数库定义及模板显示与模板替换地址
- 利用jquery.zclip在浏览器里复制内容到剪切板
- POJ 1051 P,MTHBGWB
- 开始刷leetcode day15:Valid Parentheses
- java.lang.Class类详解
- ssdb get的设计问题
- 第5章 第10题
- Learning C++ by Creating Games With UE4(15.05.18)-1(Chapter 9-1)Coding
- solr修改配置实现所需字段返回
- 模拟微信浏览器
- 陈怡暖:美元反弹昙花一现,金银反弹仍有空间(午评)
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- 在Linux下调试信息输出自定义颜色字体
- instanceof, isinstance,isAssignableFrom的区别