Redis为什么慢了
Redis作为我们常用的缓存中间件广泛应用到生产中,应用过程中或多或少可能会出现一系列的性能问题,Redis响应速度变慢了如何处理呢?命令执行变慢了如何处理呢?Redis某个时间段突然变慢了之后又恢复正常了如何排查呢?等等这篇文章来记录下Redis性能问题的排查方案。
Redis基线测试
某业务逻辑处理变慢了首先需要考虑的是到底是不是Redis自身影响的呢?因为整个业务链路包括多个参与者(APP、Redis、Mysql等等)每个参与者的阻塞都有可能导致整个业务的性能问题,这时需要知晓每个参与者的响应时间,如果真是Redis链路响应过慢,就可以进行接下来的排查。
Redis响应过慢判断指标可以通过基准性能来确定
什么是基准性能呢?
基准性能指的就是在一个系统在低压力、无干扰下的基本性能,不同的服务器配置,基准性能不同,不能一概而论
如下所示,得到100秒内的最大延时(测试机器配置较低,指标仅参考)
###?表示100秒内的响应延迟
###?如果redis是默认端口可以采用下面的命令,如果端口非默认需要指定端口./redis-cli?-p?port?--intrinsic-latency?100
[root@zzf993?bin]#?./redis-cli?--intrinsic-latency?100
Max?latency?so?far:?1?microseconds.
Max?latency?so?far:?7?microseconds.
Max?latency?so?far:?12?microseconds.
Max?latency?so?far:?34?microseconds.
Max?latency?so?far:?70?microseconds.
Max?latency?so?far:?3140?microseconds.
Max?latency?so?far:?4210?microseconds.
Max?latency?so?far:?23074?microseconds.
Max?latency?so?far:?104617?microseconds.
2165236605?total?runs?(avg?latency:?0.0462?microseconds?/?46.18?nanoseconds?per?run).
Worst?run?took?2265206x?longer?than?the?average?latency.
我们还能使用以下命令得到最小、最大、平均延时等
###?i表示间隔多久统计一次单位秒
[root@zzf993?bin]#?./redis-cli?--latency-history?-i?1
min:?0,?max:?2,?avg:?0.27?(98?samples)?--?1.01?seconds?range
min:?0,?max:?7,?avg:?0.33?(96?samples)?--?1.00?seconds?range
min:?0,?max:?2,?avg:?0.21?(97?samples)?--?1.00?seconds?range
min:?0,?max:?2,?avg:?0.18?(97?samples)?--?1.00?seconds?range
min:?0,?max:?6,?avg:?0.30?(94?samples)?--?1.01?seconds?range
min:?0,?max:?1,?avg:?0.17?(98?samples)?--?1.01?seconds?range
min:?0,?max:?1,?avg:?0.24?(97?samples)?--?1.00?seconds?range
min:?0,?max:?2,?avg:?0.19?(98?samples)?--?1.01?seconds?range
知道基准性能概念后,我们可以按照如下步骤判断
Redis变慢排查方向
Redis实例确实出现变慢的情况,这时需要更加细致的排查,一般分为三大块Redis自身特性、文件系统、操作系统
Redis自身特性的影响
慢查询命令
在Redis中慢查询命令一般分为两类
这两者的操作都是在主线程中完成,都有可能造成网络阻塞,但这只是有可能,我们可以通过慢查询日志slowlog观察,慢查询日志可以在redis.conf文件中配置参数slowlog-log-slower-than N 表示执行时间超过N毫秒开始记录慢日志,当N=0时表示所有的命令都会记录到慢查询日志中测试时可以这样设置,查询慢日志命令是SLOWLOG get N 获取最新的多少个慢查询日志,结果如下
127.0.0.1:6379>?SLOWLOG?get?5
1)?1)?(integer)?1946?###?每个慢查询条目的唯一的递增标识符
?? 2)?(integer) 1651750547 ###?处理记录命令的unix时间戳。
?? 3)?(integer) 19 ###?命令执行所需的总时间,以微秒为单位。
???4)?1)?"SLOWLOG"?###?组成该命令的参数数组
??????2)?"get"
??????3)?"20"
???5)?"127.0.0.1:57602"
???6)?""
当slowlog中出现上述慢查询命令时,我们可以进行如下优化
操作bigkey
如果在慢日志中包含有get、set等命令那就需要考虑bigkey的情况,因为get、set命令时间复杂度是O(1),在slowlog-log-slower-than N 配置设置正常的情况下是不会出现这种情况的,最大可能就是bigkey,我们可以采用命令查看实例的bigkey分布情况
###?如果redis是默认端口可以采用下面的命令,如果端口非默认需要指定端口./redis-cli?-p?6379?--bigkeys
[root@zzf993?bin]#?./redis-cli?--bigkeys
#?Scanning?the?entire?keyspace?to?find?biggest?keys?as?well?as
#?average?sizes?per?key?type.??You?can?use?-i?0.1?to?sleep?0.1?sec
#?per?100?SCAN?commands?(not?usually?needed).
##?得到比目前统计要大的bigkey(如果新加入的key比上面的bigkey要大就会统计到,如果小就不会统计)
[00.00%]?Biggest?list???found?so?far?'"database"'?with?3?items
[00.00%]?Biggest?string?found?so?far?'"key2"'?with?4?bytes
[00.00%]?Biggest?list???found?so?far?'"test3"'?with?13?items
[00.00%]?Biggest?string?found?so?far?'"name"'?with?8?bytes
--------?summary?-------
Sampled?7?keys?in?the?keyspace!
Total?key?length?in?bytes?is?32?(avg?len?4.57)
Biggest???list?found?'"test3"'?has?13?items
Biggest?string?found?'"name"'?has?8?bytes
2?lists?with?16?items?(28.57%?of?keys,?avg?size?8.00)
0?hashs?with?0?fields?(00.00%?of?keys,?avg?size?0.00)
5?strings?with?22?bytes?(71.43%?of?keys,?avg?size?4.40)
0?streams?with?0?entries?(00.00%?of?keys,?avg?size?0.00)
0?sets?with?0?members?(00.00%?of?keys,?avg?size?0.00)
0?zsets?with?0?members?(00.00%?of?keys,?avg?size?0.00)
如果是查询存在bigkey那么需要优化业务逻辑解决,如果是删除bigkey阻塞,可以优化如下
即使删除有UNLINK方案和lazy-free同样需要注意的是在业务中应避免bigkey的存在,在很多场景bigkey都有性能限制。
key的过期操作
key过期后的删除机制分为两种情况,定时自动删除和被动删除。
定时自动删除这里的比例超过25%后重复删除过程这里会在主线程的运行,一但过期key的比例高那么将阻塞主线程。
而过期key的比例高换句话说就是key的集中过期,导致了定时自动删除拖延主线程任务处理时间,这个场景大部分出现在EXPIREAT给大量key设置相同的过期时间,解决办法有如下方案
-
将过期时间上一个小的随机时间,确保单个时间点上不会有大量key过期的情况。 -
如果Redis是4.0以上版本,可以开启lazy-free异步执行键值删除逻辑 ###?redis.conf中配置默认no不开启
lazyfree-lazy-expire?yes
我们还需要关注实例过期key的数量,如果新增需要立即排查,我们可以通过如下命令
###?统计的是实例过期key的数量,如果激增表明key有集中失效的情况
[root@zzf993?bin]#?./redis-cli?info?|?grep?expired_keys
expired_keys:1
另外补充注意点,如果过期删除时删除的是一个bigkey,这时Redis的命令执行延时高,但是慢日志不会记录,主要耗时就在执行Redis的key值删除的逻辑上。
Redis内存达到maxmemory
在redis.conf文件中配置了最大内存后 maxmemory <bytes> 如果内存使用量超过这个配置,那么内存淘汰机制就将触发,内存淘汰机制需要先按照淘汰规则淘汰部分key后才能让新数据写入,自然操作的延迟会增加。
另外需要注意的是,内存淘汰和删除是类似的,如果淘汰的是一个bigkey那么可能导致Redis命令的执行延时高
内存淘汰规则如下所示
-
noeviction:不淘汰任何 key,实例内存达到 maxmeory 后,再写入新数据直接返回错误 -
allkeys-lru:淘汰目标是所有的key,淘汰最近访问最少的。 -
allkeys-random:淘汰目标是所有的key,随机淘汰key。 -
allkeys-ttl:淘汰目标是所有的key,淘汰即将过期的 key。 -
volatile-lru:淘汰目标是设置了过期时间的 key,淘汰最近访问最少的。 -
volatile-random:淘汰目标是设置了过期时间的 key,随机淘汰key。 -
allkeys-lfu:淘汰目标是所有的key,淘汰访问频率最低的 key(4.0+版本支持) -
volatile-lfu:淘汰目标是设置了过期时间的 key,只淘汰访问频率最低(4.0+版本支持)
lru规则就是每次从实例中随机抽取一定数量的key,淘汰一个最少使用的,然后将其余的放入到一个池子中,继续随机从实例中随机抽取一定数量的key,并与之前池子中的key作比较,选取一个最少使用的反复执行,将内存占用控制到maxmemory以下。
lfu规则是对lru的改进,lfu是保留访问相对频繁的key,丢弃访问不频繁的key,而lru是去淘汰最近访问最少的,相对概念稍有不同。
如果出现上述场景,可以进行如下调整
频繁短连接
如果客户端和服务端连接采用短连接的形式那么Redis的部分性能消耗在连接建立和释放上,可能会造成业务延迟,所以业务应用建议尽量使用长连接的形式。
|