Redis为不同的开发语言提供了客户端,如Java的Jedis、Python的redis-py等。
Jedis
Jedis是Java使用的较为广泛的客户端。
获取Jedis
Jedis属于Java的第三方开发包,可以在https://github.com/xetorthio/jedis获取。
Jedis的使用
1  | import org.apache.commons.pool2.impl.GenericObjectPoolConfig;  | 
Jedis连接池
Jedis提供了JedisPool这个类作为对Jedis的连接池,同时使用了Apache的通用对象池工具common-pool作为资源的管理工具。
使用连接池的方式可以预先初始化好Jedis连接,所以每次只需要从Jedis连接池借用即可。
上面的代码创建了一个连接池,从连接池直接获取Jedis对象,然后执行redis的set、get操作。
GenericObjectPoolConfig提供有很多参数,例如池子中最大连接数、最大空闲连接数、最小空闲连接数、连接活性检测,等等。下表是Generic-ObjectPoolConfig参数的含义:
| 参数名 | 含义 | 默认值 | 
|---|---|---|
| maxActive | 连接池中最大的连接数 | 8 | 
| maxIdle | 连接池中最大空闲的连接数 | 8 | 
| maxWaitMillis | 当连接池资源用尽后,调用者的最大等待时间(单位为毫秒),一般不建议使用默认值 | -1:表示永远超时,一直等 | 
| jmxEnable | 是否开启jmx监控,如果应用开启了jmx端口并且jmxEnable设置为true,就可以通过jconsole或者jvisualvm看到关于连接池的相关统计,有助于了解连接池的使用情况,并且可以针对其做监控统计 | true | 
| minEvictableIdleTimeMillis | 连接的最小空闲空间,达到此值后空闲连接将被移除 | 30分钟 | 
| numTestsPerEvictionRun | 做空闲连接检测时,每次的采样数 | 3 | 
| testOnBorrow | 向连接池借用连接时是否做连接有效性检测(ping),无法连接会被移除,每次借用都执行一次ping命令 | false | 
| testOnReturn | 向连接池归还连接时是否做连接有效性检测(ping),无法连接会被移除,每次借用都执行一次ping命令 | false | 
| testWhileIdle | 向连接池借用连接时是否做连接空闲检测,空闲超时的连接会被移除 | false | 
| timeBetweenEvictionRunsMillis | 空闲连接的检测周期 | -1:表示不做检测 | 
| blockWhenExhausted | 当连接池用尽后,调用者是否要等待,这个参数是和maxWaitMillis对应的,只有当此参数为true时,maxWaitMillis才会生效 | true | 
哈希操作
1  | jedis.hset("myhash", "f1", "v1");  | 
列表操作
1  | jedis.rpush("mylist", "1");  | 
集合操作
1  | jedis.sadd("myset", "a");  | 
有序集合操作
1  | jedis.zadd("myzset", 99, "tom");  | 
Pipeline
Redis提供了mget、mset方法,但是并没有提供mdel方法,如果想实现这个功能,可以借助Pipeline来模拟批量删除,虽然不会像mget和mset那样是一个原子命令,但是在绝大数场景下可以使用。
1  | import org.apache.commons.pool2.impl.GenericObjectPoolConfig;  | 
上面的代码使用jedis.pipelined()生成一个pipeline对象,将del命令封装到pipeline中,此时命令并未执行,最后,使用pipeline.sync()执行命令。
可以使用pipeline.syncAndReturnAll()获得pipeline的执行命令后的返回值,例如下面代码将set和incr做了一次pipeline操作,并顺序打印两个命令的结果:
1  | Pipeline pipeline = jedis.pipelined();  | 
输出结果为:
OK
1
redis-py
redis-py是Python使用的较为广泛的客户端。
获取redis-py
pip install redis
redis-py的使用
1  | #!/usr/bin/env python3  | 
哈希操作
1  | r.hset('myhash', 'f1', 'v1')  | 
列表操作
1  | r.rpush('mylist', '1')  | 
集合操作
1  | r.sadd('myset', 'a')  | 
有序集合操作
1  | r.zadd('myzset', '99', 'tom')  | 
Pipeline
1  | def mdel():  | 
创建Pipeline时,参数transaction=False代表不使用事务。pipeline.delete(key)并没有执行命令,只是把命令封装到Pipeline中。调用pipeline.execute()真正执行命令,并返回相应的值。
1  | pipeline.set('hello', 'world')  | 
输出结果为:
[True, 2]
客户端API
client list
client list命令能列出与Redis服务端相连的所有客户端连接信息:
127.0.0.1:6379> client list
id=31 addr=192.168.1.1:52651 fd=6 name= age=2260 idle=2260 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=48 addr=127.0.0.1:51352 fd=7 name= age=816 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 | 客户端连接的唯一标识,这个id是随着Redis的连接自增的,重启Redis后会重置为0 | 
| addr | 客户端连接的ip和端口 | 
| fd | socket的文件描述符 | 
| name | 客户端的连接名 | 
| age | 客户端连接存活时间 | 
| idle | 客户端连接空闲时间 | 
| flags | 客户端类型标识 | 
| db | 当前客户端正在使用的数据库索引下标 | 
| sub/pub | 当前客户端订阅的频道或者模式数 | 
| multi | 当前事务中已执行命令的个数 | 
| qbuf | 输入缓冲区总容量 | 
| qbuf-free | 输入缓冲区剩余容量 | 
| obl | 固定缓冲区的长度 | 
| oll | 动态缓冲区的列表的长度 | 
| omem | 固定缓冲区和动态缓冲区使用的容量 | 
| events | 文件描述符事件(r/w):r和w分别代表客户端套接字可读和可写 | 
| cmd | 当前客户端最后一次执行的命令,不包括参数 | 
输入缓冲区:qbuf、qbuf-free
Redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送的命令临时保存,同时Redis会从输入缓冲区读取命令并执行,输入缓冲区为客户端发送命令到Redis执行命令提供了缓冲功能。
client list中qbuf和qbuf-free分别代表这个缓冲区的总容量和剩余容量,Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超过1G,超过后客户端将被关闭。
输入缓冲使用不当会产生两个问题:
- 一旦某个客户端的输入缓冲区超过1G,客户端将会被关闭。
 - 输入缓冲区不受maxmemory控制,假设一个Redis实例设置了maxmemory为4G,已经存储了2G数据,但是如果此时输入缓冲区使用了3G,已经超过maxmemory限制,可能会产生数据丢失、键值淘汰、OOM等情况。
 
造成输入缓冲区过大的原因:
- 输入缓冲区的命令包含了大量bigkey
 - Redis发生了阻塞,短期内不能处理命令
 
监控输入缓冲区异常的方法有两种:
- 通过定期执行
client list命令,收集qbuf和qbuf-free找到异常的连接记录并分析,最终找到可能出问题的客户端。 - 通过
info命令的info clients模块,找到最大的输入缓冲区,client_biggest_input_buf代表最大的输入缓冲区。 
输出缓冲区:obl、oll、omem
与输入缓冲区不同的是,输出缓冲区的容量可以通过参数client-outputbuffer-limit来进行设置,并且输出缓冲区做得更加细致,按照客户端的不同
分为三种:普通客户端输出缓冲区、发布订阅客户端输出缓冲区、slave客户端输出缓冲区。
对应redis.conf的配置规则:
client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
<class>:客户端类型,分为三种:
- normal:普通客户端
 - slave:slave客户端
 - pubsub:发布订阅客户端
 
<hard limit>:如果客户端使用的输出缓冲区大于<hard limit>,客户端会被立即关闭。
<soft limit>和<soft seconds>:如果客户端使用的输出缓冲区超过了<soft limit>并且持续了<soft limit>秒,客户端会被立即关闭。
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新的返回结果存放在动态缓冲区的队列中。
client list中的obl代表固定缓冲区的长度,oll代表动态缓冲区列表的长度,omem代表使用的字节数。
监控输入缓冲区异常的方法有两种:
- 通过定期执行
client list命令,收集obl、oll、omem找到异常的连接记录并分析,最终找到可能出问题的客户端。 - 通过
info命令的info clients模块,找到输出缓冲区列表最大对象数,client_longest_output_list代表输出缓冲区列表最大对象数。 
输出缓冲区预防:
- 进行上述监控,设置阀值,超过阀值及时处理。
 限制普通客户端输出缓冲区,可以进行如下设置:
client-output-buffer-limit normal 20mb 10mb 120- 适当增大slave的输出缓冲区的,如果master节点写入较大,slave客户端的输出缓冲区可能会比较大,一旦slave客户端连接因为输出缓冲区溢出被kill,会造成复制重连。
 - 限制容易让输出缓冲区增大的命令,例如,高并发下的monitor命令就是一个危险的命令。
 - 及时监控内存,一旦发现内存抖动频繁,可能就是输出缓冲区过大。
 
客户端的限制maxclients和timeout:
Redis提供了maxclients参数来限制最大客户端连接数,一旦连接数超过maxclients,新的连接将被拒绝。maxclients默认值是10000,可以通过info clients来查询当前Redis的连接数:
127.0.0.1:6379> info clients
# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
127.0.0.1:6379> 
Redis提供了timeout(单位为秒)参数来限制连接的最大空闲时间,一旦客户端连接的idle时间超过了timeout,连接将会被关闭,Redis默认的timeout是0,也就是不会检测客户端的空闲,例如设置timeout为30秒:
127.0.0.1:6379> config set timeout 30
OK
客户端类型:
client list中的flag是用于标识当前客户端的类型,例如flag=S代表当前客户端是slave客户端、flag=N代表当前是普通客户端。
| 客户端类型 | 说明 | 
|---|---|
| N | 普通客户端 | 
| M | 当前客户端是master节点 | 
| S | 当前客户端是slave节点 | 
| O | 当前客户端正在执行monitor命令 | 
| x | 当前客户端正在执行事务 | 
| b | 当前客户端正在等待阻塞事件 | 
| d | 一个受监视的键已被修改,EXEC命令失败 | 
| u | 客户端未被阻塞 | 
| c | 回复完整输出后,关闭连接 | 
| A | 尽可能快地关闭连接 |