Redis高级实践之————Redis短连接性能优化
摘要: 对于Redis服务,通常我们推荐用户使用长连接来访问Redis,但是由于某些用户在连接池失效的时候还是会建立大量的短连接或者用户由于客户端限制还是只能使用短连接来访问Redis,而原生的Redis在频繁建立短连接的时候有一定性能损耗,本文从源码角度对Redis短连接的性能进行了优化。
1. 问题
通过历史监控我们可以发现用户在频繁使用短连接的时候Redis的cpu使用率有显著的上升
2. 排查
通过扁鹊查看但是Redis的cpu运行情况如下
从扁鹊我们可以看到Redis在freeClient的时候会频繁调用listSearchKey,并且该函数占用了百分30左右的调用量,如果我们可以优化降低该调用,短连接性能将得到具体提升。
3. 优化
通过以上分析我们可以知道Redis在释放链接的时候频繁调用了listSearchKey,通过查看Redis关闭客户端源码如下:
void freeClient(redisClient *c) {
listNode *ln; /* If this is marked as current client unset it */
if (server.current_client == c) server.current_client = NULL; /* If it is our master that's beging disconnected we should make sure
* to cache the state to try a partial resynchronization later.
*
* Note that before doing this we make sure that the client is not in
* some unexpected state, by checking its flags. */
if (server.master && c->flags & REDIS_MASTER) {
redisLog(REDIS_WARNING,"Connection with master lost.");
if (!(c->flags & (REDIS_CLOSE_AFTER_REPLY|
REDIS_CLOSE_ASAP|
REDIS_BLOCKED| REDIS_UNBLOCKED)))
{
replicationCacheMaster(c);
return;
}
} /* Log link disconnection with slave */
if ((c->flags & REDIS_SLAVE) && !(c->flags & REDIS_MONITOR)) {
redisLog(REDIS_WARNING,"Connection with slave %s lost.",
replicationGetSlaveName(c));
} /* Free the query buffer */
sdsfree(c->querybuf);
c->querybuf = NULL; /* Deallocate structures used to block on blocking ops. */
if (c->flags & REDIS_BLOCKED)
unblockClientWaitingData(c);
dictRelease(c->bpop.keys); freeClientArgv(c); /* Remove from the list of clients */
if (c->fd != -) {
ln = listSearchKey(server.clients,c);
redisAssert(ln != NULL);
listDelNode(server.clients,ln);
} /* When client was just unblocked because of a blocking operation,
* remove it from the list of unblocked clients. */
if (c->flags & REDIS_UNBLOCKED) {
ln = listSearchKey(server.unblocked_clients,c);
redisAssert(ln != NULL);
listDelNode(server.unblocked_clients,ln);
}
...
...
...
/* Release other dynamically allocated client structure fields,
* and finally release the client structure itself. */
if (c->name) decrRefCount(c->name);
zfree(c->argv);
freeClientMultiState(c);
sdsfree(c->peerid);
if (c->pause_event > ) aeDeleteTimeEvent(server.el, c->pause_event);
zfree(c);
}
从源码我们可以看到Redis在释放链接的时候遍历server.clients查找到对应的redisClient对象然后调用listDelNode把该redisClient对象从server.clients删除,代码如下:
/* Remove from the list of clients */
if (c->fd != -) {
ln = listSearchKey(server.clients,c);
redisAssert(ln != NULL);
listDelNode(server.clients,ln);
}
查看server.clients为List结构,而redis定义的List为双端链表,我们可以在createClient的时候将redisClient的指针地址保留再freeClient的时候直接删除对应的listNode即可,无需再次遍历server.clients,代码优化如下:
3.1 createClient修改
redisClient *createClient(int fd) {
redisClient *c = zmalloc(sizeof(redisClient)); /* passing -1 as fd it is possible to create a non connected client.
* This is useful since all the Redis commands needs to be executed
* in the context of a client. When commands are executed in other
* contexts (for instance a Lua script) we need a non connected client. */
if (fd != -) {
anetNonBlock(NULL,fd);
anetEnableTcpNoDelay(NULL,fd);
if (server.tcpkeepalive)
anetKeepAlive(NULL,fd,server.tcpkeepalive);
if (aeCreateFileEvent(server.el,fd,AE_READABLE,
readQueryFromClient, c) == AE_ERR)
{
close(fd);
zfree(c);
return NULL;
}
}
...
if (fd != -) {
c->client_list_node = listAddNodeTailReturnNode(server.clients,c);
}
return c;
}
3.2 freeClient修改
freeClient修改如下:
/* Remove from the list of clients */
if (c->fd != -) {
if (c->client_list_node != NULL) listDelNode(server.clients,c->client_list_node);
}
3.3 优化结果
在同一台物理机上启动优化前后的Redis,分别进行压测,压测命令如下:
redis-benchmark -h host -p port -k -t get -n -c
其中-k 代表使用短连接进行测试
原生Redis-server结果:
99.74% <= milliseconds
99.78% <= milliseconds
99.84% <= milliseconds
99.90% <= milliseconds
99.92% <= milliseconds
99.94% <= milliseconds
99.99% <= milliseconds
100.00% <= milliseconds
10065.42 requests per second
优化后Redis-server结果
99.69% <= milliseconds
99.72% <= milliseconds
99.80% <= milliseconds
99.82% <= milliseconds
99.86% <= milliseconds
99.89% <= milliseconds
99.94% <= milliseconds
99.96% <= milliseconds
99.97% <= milliseconds
100.00% <= milliseconds
13823.61 requests per second
我们可以看到优化之后的Redis-server性能在短连接多的场景下提升了百分30%以上。
Redis高级实践之————Redis短连接性能优化的更多相关文章
- Redis进阶实践之五Redis的高级特性(转载 5)
Redis进阶实践之五Redis的高级特性 一.引言 上一篇文章写了Redis的特征,使用场景,同时也介绍了Redis的基本数据类型,redis的数据类型是操作redis的基础,这个必须好好的掌握.今 ...
- Redis进阶实践之六Redis Desktop Manager连接Windows和Linux系统上的Redis服务(转载6)
Redis进阶实践之六Redis Desktop Manager连接Windows和Linux系统上的Redis服务 一.引言 今天本来没有打算写这篇文章,但是,今天测试Redis的时候发现了两个问题 ...
- Redis进阶实践之七Redis和Lua初步整合使用(转载 7)
Redis进阶实践之七Redis和Lua初步整合使用 一.引言 Redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运 ...
- Redis进阶实践之四Redis的基本数据类型(转载4)
Redis进阶实践之四Redis的基本数据类型 一.引言 今天正式开始了Redis的学习,如果要想学好Redis,必须先学好Redis的数据类型.Redis为什么会比以前的Memchaed等内存缓存软 ...
- Redis进阶实践之五Redis的高级特性
一.引言 上一篇文章写了Redis的特征,使用场景,同时也介绍了Redis的基本数据类型,redis的数据类型是操作redis的基础,这个必须好好的掌握.今天我们开始介绍一些Redis的高级特性 ...
- Redis进阶实践之六Redis Desktop Manager连接Windows和Linux系统上的Redis服务
一.引言 今天本来没有打算写这篇文章,当初我感觉使用这个工具应该很简单,下载的过程也不复杂,也没有打算记录下来.但是在使用的过程中还是出现了一些问题,为了给第一次使用Redis Desktop Man ...
- Redis进阶实践之七Redis和Lua初步整合使用
一.引言 Redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当 ...
- Redis进阶实践之四Redis的基本数据类型
一.引言 今天正式开始了Redis的学习,如果要想学好Redis,必须先学好Redis的数据类型.Redis为什么会比以前的Memchaed等内存缓存软件使用的更频繁,适用范围更广呢?就是因为R ...
- redis在实践中的一些常见问题以及优化思路
1.fork耗时导致高并发请求延时 RDB和AOF的时候,其实会有生成RDB快照,AOF rewrite,耗费磁盘IO的过程,主进程fork子进程 fork的时候,子进程是需要拷贝父进程的空间内存页表 ...
随机推荐
- Nagios监控远端的mysql
工作原理: 利用特定的用户定期访问指定的mysql数据库.当不能访问或连不通时则报警. 1.在生产库上安装nagios插件 安装略 备注:编译完显示一定要有mysql支持,不然没有chec ...
- centos7.1-64bit安装qtcreator
首先,启用 EPEL Repository: yum -y install epel-release 启用 EPEL Repository 後, 可以用 yum 直接安裝qtcreator: yum ...
- popupwindow显示的位置 布局的右上角,标题栏下
View popview = View.inflate(activity, R.layout.popwindow_layout, null); int width = Dp2pxUtils.Dp2Px ...
- 集合(Collection)使用笔记
Collections.unmodifiableCollection这个可以得到一个集合的镜像,它的返回结果不可直接被改变,否则会提示 java.lang.UnsupportedOperationEx ...
- B类地址
从 128.0.0.0 到 191.255.255.254 的单址广播 IP 地址.前两个八位字节指明网络,后两个八位字节指明网络上的主机.B类地址范围:128.0.0.1到191.255.255.2 ...
- oracle查看表占磁盘大小
select segment_name, bytes/1024/1024 from user_segments S where S.segment_type = 'TABLE' AND S.segme ...
- WAMP,BITNAMI上建立多个虚拟主机都访问到主站上去了怎么解决?
新建立了多个虚拟主机,访问的结果都是localhost,只要把localhost也建立成一个虚拟主机所有的虚拟主机访问就正常了.
- 文件映射mmap
磁盘与内存的映射就是文件映射,说这个问题之前我们先说下swap,因为 这个问题让我很容易想起swap,linux swap 是交换分区的意思,在内存不 够的情况下,操作系统先把内存与磁盘的sw ...
- Apache Common-pool2对象池分析和应用
Apache Common-pool2包提供了一个通用的对象池技术的实现.可以很方便的基于它来实现自己的对象池,比如DBCP和Jedis他们的内部对象池的实现就是依赖于Common-pool2. 对象 ...
- 利用JAVA反射机制将JSON数据转换成JAVA对象
net.sf.json.JSONObject为我们提供了toBean方法用来转换为JAVA对象, 功能更为强大, 这里借鉴采用JDK的反射机制, 作为简单的辅助工具使用, 有些数据类型需要进行转 ...