客户端通信协议

redis客户端和服务端通信协议建立在TCP基础上,并且制定了RESP(Redis Serialization Protocal)序列化协议。其格式大致如下:

*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n

RESP发送命令的格式如下,CRLF代表”\r\n",即换行符

*<参数数量> CRLF
$<参数1的字节数量> CRLF
<参数1> CRLF
$<参数N的字节数量> CRLF
<参数N> CRLF

RESP返回结果类型分为以下五种:

  • 状态回复:在RESP中第一个字节为”+”
  • 错误回复:在RESP中第一个字节为”-“
  • 整数回复:在RESP中第一个字节为”:”
  • 字符串回复:在RESP中第一个字节为”$”
  • 多条字符串回复:在RESP中第一个字节为”*”

Tips:如果有nil值则直接回复返回$-1

Jedis

连接Redis

Jedis connection = new Jedis(final String host,final int port,final int connectionTimeout,final int soTimeout)
  • host:Redis服务端IP
  • port:Redis服务端端口
  • connectionTimeout:客户端连接超时时间
  • soTimeout:客户端读写超时时间

连接池
Jedis每次连接会新建一个连接会话然后再断开连接,频繁访问不是非常高效。这时,可以采用Jedis连接池,所有Jedis对象预先放在Jedis pool中,每次要连接Redis,直接从连接池中分配然后再归还,能够有效的管理Redis会话。

GenericObjectPoolConfig pool = new GenericObjectPoolConfig();
JedisPool jedispool = new JedisPool(pool,"127.0.0.1",6379);
Jedis jedis = null;
try {
jedis = jedispool.getResource();
jedis.get("hello");
} catch (Exception e) {
logger.error(e.getMessage(),e);
} finally {
if (jedis != null) {
jedis.close();
}
}

下面是一些常用GenericObjectPoolConfig的属性,可以对连接池进行配置。

参数 说明 默认值
maxActive 连接池最大连接数 8
maxIdle 连接池最大空闲连接数 8
minIdle 连接池最小空闲连接数 0
maxWaitMillis 当连接池用尽,最大等待时间(毫秒) -1(永不超时)
jmxEnabled 是否开启jmx,如果开启了jmx端口并且启用该配置就可以通过jconsole看到连接池统计信息 true
minEvictableIdleTimeMillis 连接的最小空闲时间,达到此值空闲连接将被移除 1000L * 60L * 30 = 30分钟
numTestsPerEvictionRun 空闲连接检测时的采样数 3
testOnBorrow 申请连接池资源时是否做连接有效性验证,无效则移除 false
testOnReturn 归还连接池时是否做有效性验证,无效则移除 false
testWhileIdle 申请连接池资源时是否做连接空闲检测,空闲超时则移除 false
timeBetweenEvictionRunsMillis 空闲连接的检测周期 -1(不做检测)
blockWhenExhausted 连接池用尽时是否等待,配合maxWaitMillis使用 true

PipeLine

Jedis也支持通过PipeLine来实现批量操作

public void mdel(List<string> keys) {
jedis jedis = new Jedis("127.0.0.1",6379);
Pipeline pipe = jedis.pipelined();
for (String key : keys) {
pipe.del(key);
}
pipe.sync();
}

也可以使用syncAndReturnAll()执行并返回结果

Lua

Jedis执行Lua脚本与redis-clo类似,其提供了三个函数来实现Lua脚本的执行

  • Object eval(String script,int keyCount,String params)
  • Object evalsha(String sha1,int keyCount,String params)
  • String scriptLoad(String script)
String key = "hello";
String script = "return redis.call('get',KEYS[1])";
Object result = jedis.eval(script,1,key);
System.out.printin(result);

redis-py

连接Redis

import redis
client = redis.StrictRedis(host='127.0.0.1',port=6379)
key = "hello"
result = client.get(key)
print "key:" + key + ", value:" + result

PipeLine

redis-py也支持pipeline的方式执行批量语句

import redis
client = redis.StrictRedis(host='127.0.0.1",port=6379);
pipe= client.pipeline(transaction=False);
pipe.set("hello","world");
pipe.get("hello");
return pipe.execute();

transaction=False表示不开启事务

Lua

redis-py实现执行Lua脚本的方式与redis-cli类似

import redis
client = redis.StrictRedis(host='127.0.0.1',port=6379);
script = "return redis.call('get'.KEYS[1])";
print client.eval(script,1,"hello");

客户端管理

通过redis命令client list可以查看所有与Redis连接的客户端信息

127.0.0.1:6379> client list
id=6 addr=10.0.139.163:58522 fd=7 name= age=11 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:客户端连接唯一标识,随着redis连接自增长,重启后重置为0
  • addr:客户端连接的IP和端口
  • fd:socket文件描述符,如果fd=-1则表示不是外部客户端而是Redis内部的伪装客户端
  • name:客户端名称,可以通过client setName和client getName设置并获取
  • qbuf/qbuf-free:分表代表输入缓冲区的总容量及空闲流量。
  • obl:输出缓冲区中固定缓冲区长度
  • oll:输出缓冲区中动态缓冲区长度
  • omem:输出缓冲区使用的字节数
  • age/idle:分别代表客户端已经连接的时间和最近一次的空闲时间
  • flags:客户端连接标识
  • db:当前数据库索引下标
  • sub/psub:当前客户段订阅的频道或者模式数量
  • multi:当前事务中已执行命令个数
  • events:文件描述符事件,r和w分别代表客户都按套接字可读和可写
  • cmd:客户端最后一次执行的命令

输入缓冲区

Redis为每个客户端分配了输入缓冲区,将客户端发送的命令临时保存。输入缓冲区大小是根据内容大小动态调整的,但是最大不能超过1G,否则客户端将被关闭。并且输入缓冲区不受maxmemory控制,如果使用不当可能会造成OOM,键值淘汰等情况

输出缓冲区

Redis为每个客户端分配了输出缓冲区,将执行结果保存并返回客户端。可以通过参数client-output-buffer-limit来修改,并且按照客户端的不同分为:普通客户端、发布订阅客户端、slave客户端

client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
  • class:客户端类型:normal为普通客户端,slave为slave客户端,pubsub为发布订阅客户端
  • hard limit:如果客户端使用的输出缓冲区大于hard limit,客户端会被立即关闭
  • soft limit:如果客户端使用的输出缓冲区大约soft limit并且持续了soft seconds秒,客户端会被立即关闭

实际上,输出缓冲区由两部分组成,固定缓冲区和动态缓冲区,固定缓冲区为16K,用于返回小的执行结果,而动态缓冲区返回较大的结果。结构上,固定缓冲区使用的是bitmaps,动态缓冲区使用的是List。

客户端配置

maxclients:Redis提供了maxclients参数来限制最大客户端连接数,一旦超过新的连接将直接被拒绝。其默认值为10000,大部分场景足够使用了。

timeout:如果数据库存在大量idle空闲状态的会话,可以设置timeout(秒)来限制最大的空闲时间,一旦客户端空闲时间超过了timeout,连接会被关闭。默认为0,也就是不检测空闲超时,建议根据需求设置该参数。

tcp-keepalive:用于检测TCP连接活性检测,默认为0,建议设置为60,表示60秒检测一次。

tcp-backlog:TCP三次握手之后,会将接受的连接放入队列中,tcp-backlog就是队列的大小,默认值为511。该参数会受到操作系统的影响,建议设置somaxconn大于或等于511

echo 511 > /proc/sys/net/core/somaxconn

客户端类型

client list中的flag用于标识当前客户端类型,其中都有以下类型。

类型 说明
N 普通客户端
M 当前客户端是master节点
S 当前客户端是slave节点
O 当前客户端在执行monitor命令
x 当前客户端正在执行事务
b 当前客户端正在等待阻塞事件
i 当前客户端正在等待VM I/O
d 一个受监视的key已被修改,exec命令将失败
u 客户端未被阻塞
c 回复完整输出后,关闭连接
A 尽可能快地关闭连接

常用命令

client kill可以用于杀掉指定IP地址和端口的客户端

client kill ip:port

client pause可以用于阻塞客户端,对slave客户端无效

client pause timeout(毫秒)

monitor用于监控redis正在执行的命令,但其可能因为输出缓冲区暴涨导致使用大量内存,生产环境应当禁止使用