目录
Redis是用单线程来处理多个客户端的访问,因此作为Redis的开发 和运维人员需要了解Redis服务端和客户端的通信协议,以及主流编程 语言的Redis客户端使用方法,同时还需要了解客户端管理的相应API以及开发运维中可能遇到的问题。
一、客户端通信协议
redis的客户端通信协议主要有两个特点:第一,客户端与服务端之间的通信协议是在TCP协议之上构建的。 第二,Redis制定了RESP(REdis Serialization Protocol,Redis序列化协 议)实现客户端与服务端的正常交互,这种协议简单高效,既能够被机器解析,又容易被人类识别。
客户端发送一条set hello world命令给服务端,按照RESP的标准客户端需要将其封装为如下格式(每行用 \r\n分隔):
*3
$3
SET
$5
hello
$5
world
Redis服务端能够按照RESP将其解析为set hello world命令,执行后回复的格式如下:
+OK
下面我们来看一下命令格式的详细说明
1.发送命令格式(CRLF代表"\r\n"):
*<参数数量
> CRLF
$<参数
1的字节数量
> CRLF
<参数
1> CRLF
...
$<参数
N的字节数量
> CRLF
<参数
N> CRLF
以set hell world这条命令为例子,所以发送命令为
*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n
格式化显示的结果如下:
$3
SET
$5
hello
$5
world
2.返回结果格式:
Redis的返回结果类型分为以下五种:
·状态回复:在RESP中第一个字节为"+"。
·错误回复:在RESP中第一个字节为"-"。
·整数回复:在RESP中第一个字节为":"。
·字符串回复:在RESP中第一个字节为"$"。
·多条字符串回复:在RESP中第一个字节为"*"
因为redis-cli本身就是按照RESP进行结果解析的,所以看不到中间结果,只能看到最终的执行结果;例如执行set hello world,返回结果是OK,并不能看到加号;
如果我们想看到Redis服务端返回的“真正”结果,可以使用nc命令、telnet 命令、甚至写一个socket程序进行模拟;
1.使用nc127.0.0.16379连接到Redis:
nc 127.0.0.1 6379
2.执行命令查看返回结果
set hello world
+OK
命令不存在时:
sethx
-ERR unknown command 'sethx'
命令的执行结果是整数时:
incr counter
:1
命令的执行结果是字符串时:
get hello
$5
world
命令的执行结果是多条字符串时:
mset java jedis python redis-py
+OK
mget java python
*2
$5
jedis
$8
redis-py
无论是字符串回复还是多条字符串回复,如果有 nil值,那么会返回$-1
get not_exist_key
$-1
如果批量操作中包含一条为nil值的结果:
mget hello not_exist_key java
*3
$5
world
$-1
$5
jedis
二、客户端管理
1.客户端API
1)client list
client list命令能列出与Redis服务端相连的所有客户端连接信息:
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
...
输出结果的每一行代表一个客户端的信息,可以看到每行包含了十 几个属性,它们是每个客户端的一些执行状态,下面将选择几个重要的属性进行说明:
(1)标识:id、addr、fd、name
这四个属性属于客户端的标识:
- ·id:客户端连接的唯一标识,这个id是随着Redis的连接自增的,重 启Redis后会重置为0。 ·addr:客户端连接的ip和端口。
- ·fd:socket的文件描述符,与lsof命令结果中的fd是同一个,如果 fd=-1代表当前客户端不是外部客户端,而是Redis内部的伪装客户端。
- ·name:客户端的名字,后面的client setName和client getName两个 命令会对其进行说明。
(2)输入缓冲区:qbuf、qbuf-free
Redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送 的命令临时保存,同时Redis从会输入缓冲区拉取命令并执行,输入缓 冲区为客户端发送命令到Redis执行命令提供了缓冲功能;
client list中qbuf和qbuf-free分别代表这个缓冲区的总容量和剩余容 量,Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区 会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大 小不能超过1G,超过后客户端将被关闭。
输入缓冲使用不当会产生两个问题:
- ·一旦某个客户端的输入缓冲区超过1G,客户端将会被关闭。
- ·输入缓冲区不受maxmemory控制,假设一个Redis实例设置了 maxmemory为4G,已经存储了2G数据,但是如果此时输入缓冲区使用 了3G,已经超过maxmemory限制,可能会产生数据丢失、键值淘汰、 OOM等情况
127.0.0.1:6390> info memory
# Memory
used_memory_human:5.00G
...
maxmemory_human:4.00G
....
输入缓冲区过大主要是因为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
对比client list和info clients监控输入缓冲区的优劣势:
(3)输出缓冲区:obl、oll、omem
Redis为每个客户端分配了输出缓冲区,它的作用是保存命令执行 的结果返回给客户端,为Redis和客户端交互返回结果提供缓冲;
与输入缓冲区不同的是,输出缓冲区的容量可以通过参数clientoutput-buffer-limit来进行设置,并且输出缓冲区做得更加细致,按照客 户端的不同分为三种:普通客户端、发布订阅客户端、slave客户端;
对应的配置规则是:client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
- <class> ·:客户端类型,分为三种。a)normal:普通客户端;b) slave:slave客户端,用于复制;c)pubsub:发布订阅客户端。
- <hard limit> ·:如果客户端使用的输出缓冲区大于,客 户端会被立即关闭。
- <soft limit>和<soft seconds> :如果客户端使用的输出缓冲区超过了 并且持续了秒,客户端会被立即关闭。
Redis的默认配置是:
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
输出缓冲区也不会受到maxmemory的限 制,如果使用不当会造成maxmemory用满产生的数据丢失、键值淘 汰、OOM等情况;输出缓冲区由两部分组成:固定缓冲区(16KB)和动态缓 冲区,其中固定缓冲区返回比较小的执行结果,而动态缓冲区返回比较 大的结果,例如大的字符串、hgetall、smembers命令的结果等;
固定缓冲区使用的是字节数组,动态缓冲区使用的是列表。当固定 缓冲区存满后会将Redis新的返回结果存放在动态缓冲区的队列中,队 列中的每个对象就是每个返回结果: