redis client list实战记录
一、redis client list各参数的含义
连接一个测试的redis服务,执行client list命令,得到的输出如下:
[work@q]$ redis-cli -h 127.0.0.1 -p 6379 client list
id=618509 addr=xx:24514 fd=9 name= age=11 idle=11 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=info
id=618514 addr=xx:51426 fd=10 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
各参数的含义如下:
参数 | 含义 |
---|---|
id | 客户端连接id |
addr | 客户端连接IP和端口 |
fd | socket的文件描述符 |
name | 客户端连接名 |
age | 客户端连接存活时间 |
idle | 客户端连接空闲时间 |
flags | 客户端类型标识 |
db | 当前客户端正在使用的数据库索引下标 |
sub/psub | 当前客户端订阅的频道或者模式数 |
multi | 当前事务中执行的命令个数 |
qbuf | 输入缓冲区总容量 |
qbuf-free | 输入缓冲区剩余容量 |
ob1 | 固定缓冲区的长度 |
oll | 动态缓冲区列表的长度 |
omem | 固定缓冲区和动态缓冲区使用的容量 |
events | 文件描述符事件(r/w) |
cmd | 当前客户端最后一次执行的命令,不包含参数 |
二、实战记录
2.1 发现问题
监控突然报警,提示客户端连接太多,查看监控后,发现果然是这样。
登录服务器,使用client list
查看连接的客户端情况,发现很多如下的客户端连接
id=29018917 addr=?:0 fd=6693 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
这些连接并不是我们平时看到的比较正常的那种连接,比如addr=?:0
,这是什么鬼?还有age=0,idle=0,cmd=NULL
又是什么意思,age=xxx,idle=xxxx,cmd=NULL
还是见过的,那这种=0
表示几个意思?
2.2 确定问题
查资料后得知,只要是age
和idle
的数字一样就表示空闲连接,后面的数字是时间,也就是说,age=0,idle=0
,也表示空闲连接,而且是刚建立好的空闲连接。
现在知道age=0,idle=0
表示刚建立好的空闲连接,那addr=?:0
表示啥呢?
Google和百度都没找到这个是啥意思,去翻了下源码:
* On failure the function still populates 'peerid' with the "?:0" string
* in case you want to relax error checking or need to display something
* anyway (see anetPeerToString implementation for more info). */
int genClientPeerId(redisClient *client, char *peerid, size_t peerid_len) {
char ip[REDIS_IP_STR_LEN];
int port;
if (client->flags & REDIS_UNIX_SOCKET) {
/* Unix socket client. */
snprintf(peerid,peerid_len,"%s:0",server.unixsocket);
return REDIS_OK;
} else {
/* TCP client. */
int retval = anetPeerToString(client->fd,ip,sizeof(ip),&port);
formatPeerId(peerid,peerid_len,ip,port);
return (retval == -1) ? REDIS_ERR : REDIS_OK;
}
}
可以看到,这个表示的意思是:如果获取客户端的ip:port失败,该函数就使用“?:0”字符串填充“peerid”。
知道这两个是什么意思之后,就该想办法解决这个问题了。
2.3 解决问题
其实这个问题主要就是大量的空闲连接的问题,也就是age=xxxx=idle
的问题。
主要分为以下两种情况:
age=xxxx1,idle=xxxx2
:这种情况(xxxx1=xxxx2),表示建立了大量的空闲连接,且很长时间都还没有释放。
这种比较好解决,通过config get timeout xx(单位为s)
设置timeout,就可以将长时间的空闲连接释放掉。age=0,idle=0
:这种比较少遇到,表示刚建立的连接。如果执行client list
几次,发现都是这样的情况,那就得提高警惕了。
可能是客户端的代码有bug,疯狂建立连接后又释放,或者是遭到攻击了。得重启客户端程序或者其他操作,服务端好像没啥办法可以解决,,,(知道的可以留言赐教)
以上两种情况一般都会伴随着cmd=NULL
(也就是最后一次没有执行什么命令),比较容易鉴别出来。
三、总结
单节点内存使用率上涨,响应时间慢,同时连接数很高的情况,很可能是因为客户端连接数过多,redis服务端输入缓冲区过大,才导致内存使用量上涨的,应该着重解决客户端连接问题,而不是内存上涨问题。
这时候应该使用client list
查看连接的客户端都是些什么地址,存活时间和空闲时间,以及执行的命令,分析一下客户端是否是正常的。
四、扩展思考
4.1 为什么切库之后,主从不能正常建立连接,进行复制?
(新主从为send_bluk状态,新从库连接状态为down)
因为切库之后,新主库马上被连接打满,阻塞了,来不及和新从库做主从复制。
主库也还没来得及释放连接,age=xxxx idle=xxxx,需要
设置timeout 60释放连接。此时会剩下cmd=CLUSTER的连接,应该是获取集群信息的命令,可能是cluster solts或者其他的(当时忘记记录是什么地址了,所以不好推测)。
4.2 从库挂了之后,主库内存突然升高,之后又回落
很可能是因为从主从复制的输出缓冲区过满,主库本来要发给从库的数据一直没有被消费,所以感觉阻塞了。