1.慢查询分析:
客户端命令的生命周期:
慢查询只统计第3步的耗时,无慢查询不代表客户端不存在超时问题,如网络带宽问题
与慢查询相关的配置:
a.slowlog-log-slower-than:预设阀值,单位为微秒,默认10000,执行时间超过该值的命令会被记录到慢查询日志(PS:设为0会记录所有命令,<0则不记录任何命令)
b.slowlog-max-len:设置慢查询日志最多存储多少条,超过该值则最早的一条慢查询日志将会被移出列表(可定期执行slowlog get将慢查询持久化到其他存储中)
修改慢查询配置的2种方法:
1.修改配置文件
2.使用config set命令动态修改(若不能使用,看版本是否太低不支持)
对慢查询日志的操作:
a.获取n条日志:slowlog get n
b.获取慢查询日志当前长度:slowlog len
c.重置慢查询日志:slowlog reset
慢查询日志=id+时间戳+命令耗时+命令+参数
2.pipeline机制:
pipeline可以将一组redis命令进行封装,通过一次RTT(往返时间)传输给redis,再将这组redis命令的执行结果按顺序返回给客户端,但是pipeline不保证该组命令执行时候的原子性且一次性传输的数据量最好不要太多
没有pipeline执行n次命令 pipeline执行n次命令
测试服务器的redis-3.2.10,测试常用的redis命令执行10000次,测试数据100字节,使用pipeline与不使用pipeline的耗时(ms),测试结果如下表:
使用pipeline | 不使用pipeline | |
---|---|---|
set | 187 | 72711 |
hset | 199 | 62369 |
sadd | 213 | 69048 |
lpush | 237 | 69688 |
zadd | 216 | 72587 |
原生批量命令与pipeline对比:
a.前者是原子性操作,而后者不是
b.前者是一个命令对应多个key,而后者可以支持多个命令
c.前者是redis服务端支持实现的,而后者需要redis客户端与服务端共同支持实现
3.bitmap:
bitmaps实际上就是字符串,只是它可以对字符串的位进行操作,可以将其看作是以位为单位的数组,数组每个单元只能存储0和1
bitmaps相关命令:
a.设置键第offset位的值: setbit key offset value(0 or 1)
b.获取键第offset位的值: getbit key offset(return 0 or 1),不存在的位返回0
c.获取键指定范围值为1的个数: bitcount key [start] [end]
d.多个键之间的运算保存结果到destkey: bitop op destkey key[key...](op=or/end/not/xor)
e.计算键第一个值为target的偏移量: bitpos key target [start] [end]
bitmaps的用处:
a.网站流量统计分析:
以日期为key,用户id(id为整型数字)为bitmap的下标,若用户登陆则对其下标值设为1,通过多个key之间的运算(or/and/not/xor)可实现不同的统计,对于大访问量的网站统计可节省很多内存
测试:使用bitmap方式存储与使用set存储用户id来实现登陆流量统计,100w用户,bitmap占用内存大约200k,而使用set存储id则占用约60M内存
b.做黑名单限制(如:ip、手机号码等):将ip、手机号码等hash或使用其他算法计算出一个下标,将其下标对应的值记为1,之后使用getbit取得对应下标判断是否为1即可
4.对客户端进行监控与管理:
A.client list命令:可以列出与redis服务端相连的所有客户端连接信息
连接信息包含如下属性:
输入缓冲区:
每一个redis客户端都有一个输入缓冲区,用于临时保存客户端发送的命令,同时redis会从输入缓冲区拉去命令并执行,大小不能超过1G且不受maxmemory参数控制,超过则关闭客户端,输入缓冲区过大则需要留意是否发生阻塞
造成redis输入缓冲区过大的主要原因:
a.Redis处理速度跟不上输入缓冲区的输入速度且每次进入缓冲区的命令包含大量bigkey
b.redis发生阻塞,不能处理命令,命令积压在输入缓冲区
如何监控:
a.定期执行client list命令查看qbuf与qbuf-free并分析,找出问题客户端
b.执行info clients找出最大输入缓冲区
两种方式优劣对比:
命令 | 优点 | 缺点 |
---|---|---|
client list | 能精准分析每个客户端来定位问题 | 相比于info client执行速度比较慢(尤其是在连接数很多的情况下),频繁执行存在阻塞redis的可能 |
info clients | 执行速度比client list快,但分析过程较为简单,只是一个统计数据 | 不能精确定位到客户端;不能显示所有输入缓冲区的总量,只能显示最大量 |
输出缓冲区:
Redis每一个客户端配置了一个输出缓冲区,用于保存命令执行的结果返回给客户端,并且输出缓冲区分为3种:普通客户端、发布订阅客户端、slave客户端,不受maxmemory控制,可通过client-output-buffer-limit进行配置
输出缓冲区的配置(redis.conf文件):
client-output-buffer-limit <class> <hard-limit> <soft-limit> <soft-seconds>
参数说明:
class:客户端类型,normal(普通客户端)、slave(slave客户端)、pubsub(发布订阅客户端)
hard-limit:若输出缓冲区大于该值,客户端立即关闭
soft-limit和soft seconds:若客户端输出缓冲区超过soft-limit并持续soft-seconds秒,客户端立即关闭
输出缓冲区=固定缓冲区(16k)+动态缓冲区
固定缓冲区返回较小结果,动态缓冲区返回较大结果,且固定缓冲区使用的是字节数组,动态缓冲区使用的是列表,固定缓冲区用尽则使用动态缓冲区
监控输出缓冲区:
a.定期执行client list命令查看obl、oll、omem并分析,找出问题客户端
b.执行info clients命令,找出输出缓冲区列表最大对象数(client_longest_output_list)
预防输出缓冲区异常:
a.监控并设置阀值,超过阀值则处理
b.限制普通客户端输出的缓冲区
c.适当增大slave端的输出缓冲区
d.限制容易让输出缓冲区增大的命令,如monitor、keys等命令
B.对客户端进行限制:
maxclients:用于限制最大客户端连接数,一旦超过该值,新的连接将被拒绝,默认为10000,可通过info client的connected_clients查看当前连接数,亦可以通过config set maxclients动态设置最大连接数timeout:用于限制连接最大的空闲时间,一旦超过该时间,连接将关闭,客户端抛出JedisConnectionException,默认为0,不会出现连接异常,可以通过config set maxclients动态设置
tcp-keepalive:检测TCP连接活性的周期,默认为0,即不进行检测,若需设置,建议为60,可防止大量死连接占用系统资源
tcp-backlog:TCP三次握手后,会将接受的连接放入队列,该值就是队列的大小,默认为511,一般无需调整
C.对客户端的操作:
client getName:获取客户端名称
client setName XXX:为客户端设置名称
client kill ip:port:杀掉指定ip地址与端口的客户端
client pause timeout(ms):阻塞客户端timeout毫秒(ps:该命令只对普通客户端与发布订阅客户端有效,对主从复制的节点是无效的)
monitor:监控其他客户端正在执行的命令,并发量大的话,执行该命令的客户端内存会暴涨,建议禁用or屏蔽
D.客户端片段的统计:
info clients命令:
a.connected_clients:代表当前redis节点的客户端连接数,需重点监控,一旦超过maxclients,新的客户端连接将被拒绝
b.client_longest_output_list:当前所有输入缓冲区中队列对象个数的最大值
c.client_biggest_input_buf:当前所有输入缓冲区中占用的最大容量
d.blocked_clients:正在执行阻塞命令的客户端个数
info stats命令:
a.Total_connections_received:redis自启动以来处理的客户端连接数总数
b.rejected_connections:redis自启动以来拒绝的客户端连接数,需重点监控
5.客户端常见异常:
A.无法从连接池获取到连接:jedisPool中的jedis对象默认是8个,若全部被占用没有归还,再次使用则需要等待,若在maxWaitMillis时间内仍无法获取带jedis对象则抛JedisConnectionException异常(Timeout waiting for idle object)或者设置了blockWhenExhausted=false,则发现连接池中没有资源时抛异常不进行等待(Pool exhausted)
原因如下:
a.客户端高并发下连接池设置过小,供不应求
b.客户端没有正确使用连接池,连接获取后没释放
c.客户端存在慢查询操作,释放连接比较慢
d.服务端原因,命令阻塞等
B.客户端读写超时:读写超时会抛JedisConnectionException异常(Read timed out)
原因如下:
a.读写超时间设置过短
b.命令本身就比较慢
c.客户端与服务端网络不正常
d.Redis自身发生阻塞
C.客户端连接超时:连接超时会抛JedisConnectionException(connect timed out)异常
原因如下:
a.连接超时设置过短(connectionTimeout)
b.Redis发生阻塞,造成tcp-backlog已满
c.客户端与服务端网络不正常
D.客户端缓冲区异常:客户端数据流异常,会抛JedisConnectionException异常(Unexpected end of... )
原因如下:
a.输出缓冲区已满
b.长时间闲置连接被服务器主动断开
c.不正常的并发读写,jedis对象同时被多个线程并发操作,可能出现该异常
E.lua脚本正在运行:若redis当前正在执行lua脚本并超过lua-time-limit,此时jedis调用redis会抛JedisDataException异常(BUSY Redis is busy running a script......)
F.redis正在加载持久化文件:Jedis调用redis时,若redis正在加载持久化文件,则抛JedisDataException异常(LOADING Redis is loading the dataset in memory)
G.redis使用的内存超过maxmemory配置:超过maxmemory配置,则抛JedisDataException异常(OOM command not allowed when used memory>’maxmemory’)
H.客户端连接数过大:客户端连接数超过maxclients,新申请连接则抛JedisDataException异常(ERR max number of client reached)