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 | 尽可能快地关闭连接 |