背景:最近生产环境中出现了一次Redis客户端连接数被打爆的事故,打算简单记录一下Redis客户端连接数高的排查思路,并深度学习一下Redis客户端方面的知识。
1.Redis客户端建立连接的过程Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:
- 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
- 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
- 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送
所以Redis连接数过高会占用一定的网络带宽,多个客户端会带来很大的网络开销,影响Redis性能。
2.Redis客户端相关配置- maxclients:Redis的连接数可以通过配置参数maxclients来做限制,超过这个配置后,将无法建立新的连接。
- timeout:服务端根据hz频率定时检测所有 client 的空闲时间是否超过配置的 timeout 值,如果超过timeout参数就会关闭这个连接,空闲时长可以根据client list 查看服务端的连接信息,其中的idle值就表示空闲时长。
- tcp-keepalive:服务端会根据这个参数定时给客户端发送 TCP 心跳包,检测网络连通性,当网络异常时,可以尽快清理TCP僵尸连接。 当客户端与服务端网络发生问题时,服务端并不会立即释放这个 client ,如果 Redis 没有开启 tcp-keepalive 的话,服务端直到配置timeout 时间后,才会清理释放这个 client 。在没有清理之前,如果还有大量新连接进来,就有可能导致 Redis 服务端内部持有的 client 超过了 maxclients,这时新连接就会被拒绝。所以建议开启此配置。
- tcp-backlog:TCP三次握手后,会将接受的连接放入队列中,tcpbacklog就是队列的大小,它在Redis中的默认值是511。通常来讲这个参数不需要调整,但是这个参数会受到操作系统的影响,例如在Linux操作系统中,如果/proc/sys/net/core/somaxconn小于tcp-backlog,那么在Redis启动时会看到如下日志,并建议将/proc/sys/net/core/somaxconn设置更大。
WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/ sys/net/core/somaxconn is set to the lower value of 128.
tcp-backlog只能再配置文件中提前配置,下面这三个参数配置都可以动态的进行调整,:
192.168.79.136:6379> config set maxclients 1000 OK 192.168.79.136:6379> config set timeout 60 OK 192.168.79.136:6379> config set tcp-keepalive 60 OK
3.主动关闭Redis客户端的场景
- 如果客户端进程退出或者被杀死,那么客户端与服务器之间的网络连接将被关闭,从而造成客户端被关闭。
- 如果客户端向服务器发送了带有不符合协议格式的命令请求,那么这个客户端也会被服务器关闭。
- 如果客户端成为了CLIENT KILL命令的目标,那么它也会被关闭。
- 如果用户为服务器设置了timeout配置选项,那么当客户端的空转时间超过timeout选项设置的值时,客户端将被关闭。不过timeout选项有一些例外情况:如果客户端是主服务器(打开了REDIS_MASTER标志),从服务器(打开了REDIS_SLAVE标志),正在被BLPOP等命令阻塞(打开了REDIS_BLOCKED标志),或者正在执行SUBSCRIBE、PSUBSCRIBE等订阅命令,那么即使客户端的空转时间超过了timeout选项的值,客户端也不会被服务器关闭。
- 如果客户端发送的命令请求的大小超过了输入缓冲区的限制大小(默认为1 GB),那么这个客户端会被服务器关闭。
- 如果要发送给客户端的命令回复的大小超过了输出缓冲区的限制大小,那么这个客户端会被服务器关闭。
2.1 通过redis-cli client list 命令可以查看客户端的连接信息:
| 序号 | 参数 | 含义 |
|---|---|---|
| 1 | id | 客户端连接id |
| 2 | addr | 客户端连接IP和端口 |
| 3 | fd | socket的文件描述符 |
| 4 | name | 客户端连接名 |
| 5 | age | 客户端连接存活时间 |
| 6 | idle | 客户端连接空闲时间 |
| 7 | flags | 客户端类型标识 |
| 8 | db | 使用的数据库下标 |
| 9 | sub/psub | 当前客户端订阅的频道或模式数 |
| 10 | multi | 当前事务中已执行的命令个数 |
| 11 | qbuf | 输入缓存区总容量 |
| 12 | qbuf-free | 输入缓存区剩余容量 |
| 13 | obl | 固定缓冲区的长度 |
| 14 | oll | 动态缓冲区列表的长度 |
| 15 | omem | 固定缓冲区和动态缓冲区使用的容量 |
| 16 | events | 文件描述符事件:r和w代表客户端套接字的可读和可写 |
| 17 | cmd | 当前客户端最后一次执行的命令 |
2.2 当连接数高时,可以通过client list 来排查占用连接最多的IP,从而快速运维处理,可以参考一下命令,通过此命令可以快速找的占用redis连接最多的top5;
redis-cli -h 127.0.0.1 -p 6379 client list |awk -F " " '{print $2}'|awk -F "=" '{print $2}'|cut -d ":" -f1| sort | uniq -c | sort -n -r | head -n 5
2.3 当redis连接超过maxclients时,无法通过client list 来排除问题时,可以通过netstat 命令来排查占用redis端口最多的top5连接,可以参考以下命令:
netstat -tan|awk -F " " '{print $4,$5}'|grep :6379 |cut -d " " -f2 |grep -v :6379 |cut -d ":" -f1 | sort | uniq -c | sort -n -r | head -n 5
2.4 当出现连接数被打满的运维场景时,可以临时调大maxclients,亦或是进行一次故障转移,切换客户端连接的Master节点;
2.5 如果以上方法无法解决,可以考虑通过服务端强制断开与客户端的连接,使用redis-cli client kill 命令批量杀掉占用连接最多的客户端。可以参考以下命令:
redis-cli -h 127.0.0.1 -p 6379 client list | grep '占用连接最多的客户端IP' | awk '{print $2}' | awk -F '=' '{print $2}'| xargs -i /usr/local/bin/redis-cli -h 127.0.0.1 -p 6379 client kill {}
5.客户端信息统计概览
Redis本身提供了一些命令可以对客户端连接信息进行统计。
192.170.79.136:6379> info clients # Clients connected_clients:31 client_recent_max_input_buffer:8 client_recent_max_output_buffer:0 blocked_clients:0 tracking_clients:0 clients_in_timeout_table:0
下面解释一下上图各个指标的含义:
1)connected_clients:代表当前Redis节点的客户端连接数,需要重点监控,一旦超maxclients,新的客户端连接将被拒绝。
2)client_longest_output_list:当前所有输出缓冲区中队列对象个数的最大值。
3)client_biggest_input_buf:当前所有输入缓冲区中占用的最大容量。
4)blocked_clients:正在执行阻塞命令(例如blpop、brpop、brpoplpush)的客户端个数。
除此之外 info stats 中还包含了两个客户端相关的统计指标,如下:
192.170.79.136:6379> info stats # Stats total_connections_received:3719386 ....... ....... rejected_connections:0
参数说明:
total_connections_received:Redis自启动以来处理的客户端连接数总数。 rejected_connections:Redis自启动以来拒绝的客户端连接数,需要重点监控。



