zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

Redis 客户端的管理 输入缓冲区

Redis客户端输入 管理 缓冲区
2023-09-14 09:01:47 时间

 

客户端API


Redis提供了客户端相关API对其状态进行监控和管理

 

client list


client list命令能列出与Redis服务端相连的所有客户端连接信息, 例如下面代码是在一个Redis实例上执行client list的结果:

127.0.0.1:6379> client list
id=254487 addr=10.2.xx.234:60240 fd=1311 name= age=8888581 idle=8888581 flags=N
db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
id=300210 addr=10.2.xx.215:61972 fd=3342 name= age=8054103 idle=8054103 flags=N
db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
id=5448879 addr=10.16.xx.105:51157 fd=233 name= age=411281 idle=331077 flags=N
db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ttl
id=2232080 addr=10.16.xx.55:32886 fd=946 name= age=603382 idle=331060 flags=N
db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
id=7125108 addr=10.10.xx.103:33403 fd=139 name= age=241 idle=1 flags=N db=0
sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=del
id=7125109 addr=10.10.xx.101:58658 fd=140 name= age=241 idle=1 flags=N db=0
sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=del
...

以下是域的含义:
addr : 客户端的地址和端口
fd : 套接字所使用的文件描述符
age : 以秒计算的已连接时长
idle : 以秒计算的空闲时长
flags : 客户端 flag (见下文)
db : 该客户端正在使用的数据库 ID
sub : 已订阅频道的数量
psub : 已订阅模式的数量
multi : 在事务中被执行的命令数量
qbuf : 查询缓冲区的长度(字节为单位, 0 表示没有分配查询缓冲区)
qbuf-free : 查询缓冲区剩余空间的长度(字节为单位, 0 表示没有剩余空间)
obl : 输出缓冲区的长度(字节为单位, 0 表示没有分配输出缓冲区)
oll : 输出列表包含的对象数量(当输出缓冲区没有剩余空间时,命令回复会以字符串对象的形式被入队到这个队列里)
omem : 输出缓冲区和输出列表占用的内存总量
events : 文件描述符事件(见下文)
cmd : 最近一次执行的命令


客户端 flag 可以由以下部分组成:
O : 客户端是 MONITOR 模式下的附属节点(slave)
S : 客户端是一般模式下(normal)的附属节点
M : 客户端是主节点(master)
x : 客户端正在执行事务
b : 客户端正在等待阻塞事件
i : 客户端正在等待 VM I/O 操作(已废弃)
d : 一个受监视(watched)的键已被修改, EXEC 命令将失败
c : 在将回复完整地写出之后,关闭链接
u : 客户端未被阻塞(unblocked)
A : 尽可能快地关闭连接
N : 未设置任何 flag


文件描述符事件可以是:
r : 客户端套接字(在事件 loop 中)是可读的(readable)
w : 客户端套接字(在事件 loop 中)是可写的(writeable)

输出结果的每一行代表一个客户端的信息, 可以看到每行包含了十几个属性, 它们是每个客户端的一些执行状态理解这些属性对于Redis的开发和运维人员非常有帮助。 下面将选择几个重要的属性进行说明, 其余通过表格的形式进行展示。
(1) 标识: id、 addr、 fd、 name 这四个属性属于客户端的标识:
id:客户端连接的唯一标识, 这个id是随着Redis的连接自增的, 重启Redis后会重置为0。
addr:客户端连接的ip和端口。
fd:socket的文件描述符, 与lsof命令结果中的fd是同一个, 如果fd=-1代表当前客户端不是外部客户端, 而是Redis内部的伪装客户端。
name:客户端的名字

( 2) 输入缓冲区: qbuf、 qbuf-free

[root@localhost ~]# /usr/local/redis/bin/redis-cli client list | awk '{print $12,$13 }'
qbuf=0 qbuf-free=0
qbuf=0 qbuf-free=0
qbuf=26 qbuf-free=32742

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

client list中qbuf和qbuf-free分别代表这个缓冲区的总容量和剩余容量,Redis没有提供相应的配置来规定每个缓冲区的大小, 输入缓冲区会根据输入内容大小的不同动态调整, 只是要求每个客户端缓冲区的大小不能超过1G, 超过后客户端将被关闭。 下面是Redis源码中对于输入缓冲区的硬编码:

/* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */

输入缓冲使用不当会产生两个问题:
·一旦某个客户端的输入缓冲区超过1G, 客户端将会被关闭。(针对单个客户端)
·输入缓冲区不受maxmemory控制, 假设一个Redis实例设置了maxmemory为4G, 已经存储了2G数据, 但是如果此时输入缓冲区使用了3G, 已经超过maxmemory限制, 可能会产生数据丢失、 键值淘汰、 OOM等情况(如图所示:针对所有客户端的缓冲区总和) 

上面已经看到, 输入缓冲区使用不当造成的危害非常大, 那么造成输入缓冲区过大的原因有哪些? 输入缓冲区过大主要是因为Redis的处理速度跟不上输入缓冲区的输入速度, 并且每次进入输入缓冲区的命令包含了大量bigkey, 从而造成了输入缓冲区过大的情况。

还有一种情况就是Redis发生了阻塞, 短期内不能处理命令, 造成客户端输入的命令积压在了输入缓冲区,造成了输入缓冲区过大。

那么如何快速发现和监控呢? 监控输入缓冲区异常的方法有两种:
·通过定期执行client list命令, 收集qbuf和qbuf-free找到异常的连接记录并分析, 最终找到可能出问题的客户端。
·通过info命令的info clients模块, 找到最大的输入缓冲区, 例如下面命令中的其中client_biggest_input_buf代表最大的输入缓冲区, 例如可以设置超过10M就进行报警:

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

 

这两种方法各有自己的优劣势, 表对两种方法进行了对比。

运维提示:
输入缓冲区问题出现概率比较低, 但是也要做好防范, 在开发中要减少bigkey、 减少Redis阻塞、 合理的监控报警。
 

 

CLIENT KILL ip:port


可用版本: >= 2.4.0

时间复杂度: O(N) , N 为已连接的客户端数量。

关闭地址为 ip:port 的客户端。

ip:port 应该和 CLIENT LIST 命令输出的其中一行匹配。

因为 Redis 使用单线程设计,所以当 Redis 正在执行命令的时候,不会有客户端被断开连接。

如果要被断开连接的客户端正在执行命令,那么当这个命令执行之后,在发送下一个命令的时候,它就会收到一个网络错误,告知它自身的连接已被关闭。

返回值

当指定的客户端存在,且被成功关闭时,返回 OK 。

代码示例

# 列出所有已连接客户端
redis 127.0.0.1:6379> CLIENT LIST
addr=127.0.0.1:43501 fd=5 age=10 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

# 杀死当前客户端的连接
redis 127.0.0.1:6379> CLIENT KILL 127.0.0.1:43501
OK


# 之前的连接已经被关闭,CLI 客户端又重新建立了连接
# 之前的端口是 43501 ,现在是 43504
redis 127.0.0.1:6379> CLIENT LIST
addr=127.0.0.1:43504 fd=5 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