前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

1、客户端管理和异常分析

命令 优点 info clients
client list 能精准分析每个客户端来定位问题 执行速度较慢,尤其是在连接数较多的情况下,频繁执行可能阻塞Redis
info clients 执行速度比client list快,分析过程较为简单 不能精准定位到客户端,不能显示所有输入缓冲区的总量,只能显示最大量

1.1、client list

说明:这里我同一个redis开启了两个客户端

127.0.0.1:6379> client list
id=3808 addr=127.0.0.1:55369 fd=8 name= age=5050 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=3810 addr=127.0.0.1:56759 fd=7 name= age=27 idle=26 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=client

1.2、标识

解析 说明
标识  
id 客户端连接的唯一标识
addr 客户端链接的ip和标识
name 客户端的名字
age 客户点已经链接的时间 ,连接redis的时间为27秒
idle 最近一次的空闲时间 redis空闲了26秒(当Redis连接的时间等于空闲时间的时候,就说明连接一直处于空闲,这种情况就是不正常的)
flags 客户端 flag
db 该客户端正在使用的数据库 ID
sub 已订阅频道的数量
psub 已订阅模式的数量
multi 在事务中被执行的命令数量
qbuf 输入缓冲区已使用总容量
qbuf-free 输入缓冲区剩余容量
obl 固定缓冲区的长度 举例,obl=0,固定缓冲区长度为0
oll 动态缓冲区的长度,举例:oll=4869,动态缓冲区有4869个对象
omem 总的使用的字节数,举例:omem=133081288,两个部分共使用了133081288字节=126M内存
events 文件描述符事件
cmd 最近一次执行的命令

1.3、输入缓冲区

redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送的命令临时保存,同时Redis会从缓冲区拉取命令并执行,输入缓存区为客户端发送命令到Redis提供了缓存功能。

**输入缓冲区会根据输入内容的大小而动态调整,只是要求缓冲区的大小不超过1G,超过后客户端将关闭**
解析 说明
qbuf 输入缓冲区已使用总容量
qbuf-free 输入缓冲区剩余容量

1.3.1、那么造成输入缓冲区过大的原因

1、Redis的处理速度跟不上输入缓冲区的输入速度,并且每次进入缓冲区的命令包含了大量bigkey

2、Redis发生了阻塞,短期内不能处理命令,造成客户端输入的命令积压在缓冲区    

1.3.2、如何快速发现和监控

1、通过定期执行·client list命令,收集qbufqbuf-free找到异常的连接记录并分析,最终找到可能出问题的客户端

2、通过info命令的info clients模块,找到最大的输入缓冲区client_biggest_input_buf:0,例如,可以设置10M就开始报警

127.0.0.1:6379> info clients
# Clients
connected_clients:2
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
127.0.0.1:6379> 

1.4、输出缓冲区

redis为每个客户端分配了输出缓冲区,它的作用是保存命令执行的结果返回给客户端。

实际上输出缓冲区由两部分组成,固定缓冲区和动态缓冲区,其中固定缓冲区返回比较小的执行结果,而动态缓冲区返回比较大的结果,例如大的字符串,hgetallsmembers 命令的结果

固定缓冲区使用的是字节数组,动态缓冲区使用的是列表。当固定缓冲区存满后会将Redis新的返回结果存放到动态缓冲区的队列中。队列中每个对象就是每个返回结果

127.0.0.1:6379> client list
id=3808 addr=127.0.0.1:55369 fd=8 name= age=5050 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=3810 addr=127.0.0.1:56759 fd=7 name= age=27 idle=26 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=client
参数 说明
obl 固定缓冲区的长度 举例,obl=0,固定缓冲区长度为0
oll 动态缓冲区的长度,举例:oll=4869,动态缓冲区有4869个对象
omem 总的使用的字节数,举例:omem=133081288,两个部分共使用了133081288字节=126M内存

1.4.2、配置输出缓存区的容量

与输入缓冲区不同,它的容量是可以通过参数配置的,。并且按照客户端的不同分为三种,普通客户端,发布订阅客户端,slave客户端。

客户端缓冲区超过<hard limit> ,客户端会立即关闭;

如何客户端缓冲区的输出缓冲区超过了<soft limit> 并且持续了 <soft secontds>秒,客户端会被立即关闭

默认
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 8mb 2mb 60

1.4.2、监控输出缓冲区的方法

1、定期执行client list 模块命令,收集oblollomem找到异常的连接记录并分析,最终找到可能出问题的客户端

2、通过info命令的info clients模块,找到输出缓冲区列表的最大对象数client_longest_output_list


127.0.0.1:6379> info clients
# Clients
connected_clients:2
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
127.0.0.1:6379> 

1.4.3、发现和预防输出缓冲区出现异常

1、进行上述监控,设置阀值及时处理

2、限制普通客户端输出缓冲区参数的<hard limit> <soft limit > ,<soft seconds>,把错误扼杀在摇篮中,例如可以进行如下设置

client-output-buffer-limit normal 20mb 10mb 120

3、适当增大slave的输出缓冲区的<hard limit> <soft limit > ,<soft seconds>,如果master节点写入较大,slave客户端的输出缓冲区可能也会比较大,一旦slave客户端链接因为输出缓冲区溢出而别杀死,会造成重复重连

2、最大客户端连接数和超时时间

2.1、查看maxclientstimeout属性值

127.0.0.1:6379> config get maxclients
1) "maxclients"
2) "10000" //默认10000


127.0.0.1:6379> config get timeout
1) "timeout" //默认是0
2) "0"
127.0.0.1:6379> 

2.2、限制

2.2.1、限制最大连接数

Redis提供了maxclients参数来限制最大客户端连接数,一旦超过maxclients,新的连接将会被拒绝。

maxclients默认值是10000,可以通过info clients来查看当前的连接数

127.0.0.1:6379> info clients
# Clients
connected_clients:2  //当前的连接数
client_longest_output_list:0 //客户端输出缓冲区列表最大对象数
client_biggest_input_buf:0 //客户端输入缓冲区最大值
blocked_clients:0
127.0.0.1:6379> 

2.2.2、timeout

一般情况下来说maxclients=10000在大部分场景已经绝对够用,但是某些情况由于业务方使用不当。

例如,没有主动关闭连接,可能存在大量的idle空闲连接,无论是从网络连接成本还是超过maxclients的后果来说都不是什么好事,因此Redis提供了一个timeou来限制最大空闲连接,一旦空闲连接超过了timeout,连接将会被关闭

可以将timeout设置为300秒,同时客户端加上空闲检测和验证等措施

参数名字 内容 默认值
minEvictableIdleTimeMillis 连接的最小空闲时间,达到这个值后空闲连接将被移除timeBetweenEvictionRunsMillis大于0时才有意义; 默认30分钟
timeBetweenEvictionRunsMillis 空闲检测周期(单位毫秒) -1,表示永不检测
testWhileIdle 向连接池借用连接时是否做连接空闲检测,空闲超时的连接将会被移除 false

3、客户端命名、杀死、暂停、监控

3.1、客户端命名命名

这样比较容易标识出客户端的来源

127.0.0.1:6379> client setName clent1
127.0.0.1:6379> client getName
"client1"
127.0.0.1:6379> 


127.0.0.1:6379> client list
id=3818 addr=127.0.0.1:62540 fd=7 name= age=4 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
127.0.0.1:6379> client setName clent1
OK
127.0.0.1:6379> client list
id=3818 addr=127.0.0.1:62540 fd=7 name=clent1 age=15 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
127.0.0.1:6379> 


3.2、杀死指定ip地址和端口的客户端

client kill 127.0.0.1:62569
127.0.0.1:6379> client list
id=3819 addr=127.0.0.1:62555 fd=7 name=client1 age=153 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=3820 addr=127.0.0.1:62569 fd=8 name= age=102 idle=102 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=client

127.0.0.1:6379> client kill 127.0.0.1:62569
OK
127.0.0.1:6379> 

3.3、客户端暂停阻塞

使用范围:只对普通发布者和订阅发布者有效,对于主从复制是无效的,生产环境中成本非常高

127.0.0.1:6379> client pause 10000
……

3.4、monitor 用于监控Redis正在执行的命令

另一个客户端B输入命令,则客户端A,monitor可以监听到正在执行的命令,并且记录了详细的时间戳

每个客户端都有自己的输出缓冲区,既然monitor能监听到所有的命令,则一旦并发量过大,则monitor的输出缓存就会暴涨,瞬间占用大量内存,禁止在生产环境中使用monitor命令

打开两个客户端,一个客户端A输入 monitor

127.0.0.1:6379> monitor
^

另一个客户端B输入命令,则客户端A可以监听到正在执行的命令,并且记录了详细的时间戳

客户端B 
127.0.0.1:6379> keys *
1) "name"
2) "count"
3) "age"
4) "hello"
5) "qq"
127.0.0.1:6379> set m girl
OK
127.0.0.1:6379> 


客户端A

127.0.0.1:6379> monitor
OK
1523876898.815455 [0 127.0.0.1:62628] "keys" "*"
1523876908.519187 [0 127.0.0.1:62628] "set" "m" "girl"

4、客户端常见异常分析,从源头上找

4.1、客户端链接超时

Connect time out

1、设置超时时间过短

2、网络不正常

3、Redis自身发生阻塞

4.2、无法从连接池池获取到连接

could not get a resourse

1、Jedis连接池连接个数是有限的,默认是8个,这里假设使用的默认配置,如果有8个对象被占用,并没有归还,此时还要借用,就需要等待(如果设置了maxWaitMIlls>0),如果超时,则会发生异常

2、连接池设置数量过小,并发量太大,8个也不够用

3、没有正确使用连接池,使用了8次,都没有释放

4、存在慢查询操作,这些慢查询持有的Jedis对象归还速度会比较慢,造成池子满了

4.3、客户端读写超时

Read time out

1、读写超时时间设置过段

2、命令本身比较慢

3、网络连接不正常

4、Redis自身发生阻塞

5.4、客户端缓冲区异常

unexpected end of stream

1、输出缓冲区满,例如将普通客户端的输出缓冲区设置成了 1M 1M 60

2、不正常的并发读写,Jedis对象呗多个线程并发操作

5.5、客户端连接数过大

如果客户端连接数超过了maxclients,就会抛出异常,遇到这个问题就麻烦了,因为肯定是不能再输入命令进行修复了

1、客户端方面,如果应用方是分布式结果的话,下线部分应用节点。让Redis连接数降下来,再通过查找程序bug或者调整maxclients进行修复

2、服务端,使用高可用模式主从复制和集群,将当前Redis做故障转移,这样别的就可以连接了

5.6、客户端案例分析

5.6.1、Redis内存陡增

服务端现象 :Redis主节点内存陡增,几乎用满maxmemory,而从节点没有变化(主从复制,内存使用主从节点基本相同)

客户端现象:客户端产生了OOM,也就是Redis主节点使用的内存已经超过了maxmemory,无法写入新的数据

1、原因和解决方案

1、确实有大量写入 ,通过查看主节点的dbseze和从节点的dbsize相同

2、输出缓冲区数量数量过大,造成内存陡增

3、查看到输出缓冲区数量过大

127.0.0.1:6379> info clients
# Clients
connected_clients:2
client_longest_output_list:225698
client_biggest_input_buf:0
blocked_clients:0
127.0.0.1:6379> 
发现输出缓冲区的队列已经超过了20万个对象

2、通过 client list 找到不正常的连接

一般来说大部分客户端的omem(输出缓存区占用字节为0,因为都已经执行完事了,只要找到不为omem=0的就代表找到了异常的链接),通过下面的查找发现最后一次执行的命令是monitor,很明显就知道是通过monitor造成的

redis-cli client list | grep -v “omem=0”

id=3822 addr=127.0.0.1:64244 fd=7 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=224551255844 events=r cmd=monitor

3、处理方法:只要使用 client kill就可以杀掉这个连接,但是我们以后如果及时发现并且避免

ContactAuthor