目录
- 一、问题介绍
- 二、问题分析
- 2.1 redis-cli 登录
- 2.2 info clients 查看连接数情况
- 2.3 client list 查看具体连接情况
- 2.4 分析连接空闲时长
- 2.5 client list 根据客户端IP统计连接数
- 三、问题结论和解决
- 3.1 问题结论:
- 3.2 解决方案①:优化程序
- 3.3 解决方案②:扩大最大连接数
- 3.4 解决方案③:杀死指定连接
一、问题介绍
我们在日常的开发过程中,经常会遇到 Redis 连接数不足 的情况,报错如下所示:
Caused by: org.redisson.client.RedisException: ERR max number of clients reached. channel
Caused by: org.redisson.client.RedisException: ERR max number of clients reached. channel: [id: 0xf10fcc26, L:/192.168.30.13:64137 - R:192.168.1.163/192.168.1.163:6379] command: (SELECT), params: [1]
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:365)
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:209)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:147)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:117)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:505)
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:283)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511)
... 4 common frames omitted
二、问题分析
ERR max number of clients reached. channel
报错是因为 Redis 的连接数不足 导致的。
2.1 redis-cli 登录
我们可以先使用如下命令登录 Redis 的客户端:
-
redis-cli -h 127.0.0.1 -p 6379 -c --raw
-c
:(cluster)选项是连接 Redis Cluster 结点需要使用的。-c 选项可以防止moved
和ask
异常。--raw
:选项是反馈格式化后的结果,默认选项。相对应的是--no-raw
,即返回原始格式(二进制)。
2.2 info clients 查看连接数情况
查看 Redis 的当前连接数:
- info clients
##版本6.2.4,相对5版本新增了部分显示信息
##当前redis节点的客户端连接数
connected_clients:1
##集群的连接数
cluster_connections:0
##客户端最大连接数
maxclients:10000
##当前所有输入缓冲区中队列对象个数的最大值
client_recent_max_input_buffer:24
##当前所有输出缓冲区中占用的最大容量
client_recent_max_output_buffer:0
##正在执行阻塞命令的客户端个数
blocked_clients:0
tracking_clients:0
clients_in_timeout_table:0
2.3 client list 查看具体连接情况
查看 Redis 的具体连接情况:
- client list
id=17750 addr=192.168.1.254:35864 laddr=172.17.0.3:6379 fd=9615 name= age=266661 idle=266661 flags=N db=2 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=select user=default redir=-1
id=2575 addr=192.168.1.254:44133 laddr=172.17.0.3:6379 fd=1885 name= age=422195 idle=422195 flags=N db=2 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=select user=default redir=-1
id=18212 addr=192.168.1.254:61386 laddr=172.17.0.3:6379 fd=9929 name= age=266308 idle=266308 flags=N db=2 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=select user=default redir=-1
id=13437 addr=192.168.1.254:17625 laddr=172.17.0.3:6379 fd=6431 name= age=349287 idle=349287 flags=N db=2 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=select user=default redir=-1
id=12730 addr=192.168.1.254:15535 laddr=172.17.0.3:6379 fd=6226 name= age=350448 idle=350448 flags=N db=2 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=select user=default redir=-1
- 官方文档: https://redis.io/docs/latest/commands/client-list/
client list
属性说明如下:
属性 | 说明 |
---|---|
id | 客户端连接ID |
addr | 客户端连接IP和端口 |
laddr | redis数据库连接地址和端口 |
fd | socket的文件描述符 |
name | 客户端连接名 |
age | 客户端连接存活时间 |
idle | 客户端连接空闲时间,单位秒。可以用于判断连接是否有没有被及时释放 |
flags | 客户端类型标识 |
db | 当前客户正在使用的数据库索引下标 |
sub/psub | 当前客户端订阅的频道或模式数 |
multi | 当前事务中已执行命令个数 |
qbuf | 输入缓冲区总容量 |
qbuf-free | 输入缓冲区剩余容量 |
argv-mem | 下一个命令的不完整参数(已从查询缓冲区中提取) |
obl | 固定缓冲区的长度 |
oll | 动态缓冲区列表的长度 |
omem | 固定缓冲区和动态缓冲区使用的容量 |
tot-mem | 此客户端在其各个缓冲区中消耗的总内存 |
events | 文件描述符时间(r:可读;w:可写) |
cmd | 当前客户端最后一次执行的命令,不包含参数 |
user | 用户名称,默认default |
redir | 当前用户跟踪重定位的客户端ID |
2.4 分析连接空闲时长
了解了以上的属性,我们再反观刚才 client list
的执行结果,可以看到:
- 其中
idle
(客户端连接空闲时间)和age
(客户端连接存活时间)的值基本一致,说明大部分的连接是没有用到的。
2.5 client list 根据客户端IP统计连接数
命令如下:
redis-cli -h 127.0.0.1 -p 6379 -c --raw client list | awk -F ' ' '{print $2}' | awk -F : '{print $1}' | sort | uniq -c | sort -nr | head -n 10
docker 命令版:
docker exec -it redis redis-cli client list | awk -F ' ' '{print $2}' | awk -F : '{print $1}' | sort | uniq -c | sort -nr | head -n 10
执行结果:
三、问题结论和解决
3.1 问题结论:
从上面的问题分析可以看出,问题原因是大部分连接被某一个IP占用导致的,可能是Redis最小连接数配置错误,或者程序Bug导致的。
3.2 解决方案①:优化程序
可以根据客户端的IP和端口定位到客户端的具体应用进行优化,但是如果客户端应用不是在你们部门维护的就不太好推进了。
3.3 解决方案②:扩大最大连接数
- 方式一: 在
redis.conf
配置文件中进行修改,如下所示:
# 设置最大连接数为10w
maxclients 100000
- 方式二: 通过命令设置,如下所示:
config set maxclients 100000
- 方式三: 启动
redis.service
服务时,添加参数--maxclients 100000
,如下所示:
redis-server --maxclients 100000 -f /ext/redis.conf
- 验证: 使用命令查看最大连接数,如下所示:
config get maxclients
3.4 解决方案③:杀死指定连接
可以在 redis 中使用如下命令杀死指定连接:
# 方式一:通过ip:port杀死
client kill ip:port
# 方式二:通过连接ID杀死
client kill id 5299
可以使用如下命令针对指定 IP 的连接进行批量杀死,例如批量杀死192.168.1.123的 3 个连接:
redis-cli client list | grep '192.168.1.123' | awk -F ' ' '{print \$1}' | sed 's/id=//g' | head -n 3 | xargs -n 1 redis-cli client kill id
- 如果需要杀死所有连接,可以去掉
head -n 3
。
docker 命令版:
docker exec -it redis sh -c "redis-cli client list | grep '192.168.1.254' | awk -F ' ' '{print \$1}' | sed 's/id=//g' | head -n 3 | xargs -n 1 redis-cli client kill id"
- 注意1: 这里需要使用
sh -c
来包装整个命令,确保命令在一个子shell
中执行,否则会报错: the input device is not a TTY。 - 注意2: 这里需要使用
xargs -n 1
来将awk
筛选后的结果逐行执行,-n 1
用于确保逐行执行,否则会报错: (error) ERR syntax error。 - 如果需要杀死所有连接,可以去掉
head -n 3
。
整理完毕,完结撒花~🌻
参考地址:
1.Redis:13–常用功能之redis-cli redis-server等命令,https://cloud.tencent.com/developer/article/1784416
2.redis client list使用,https://blog.csdn.net/weixin_44375561/article/details/121955791
3.[问题分析]redis客户端连接数过多,大量空闲连接和readonly连接,https://segmentfault.com/a/1190000020256823