0
点赞
收藏
分享

微信扫一扫

Redis访问变慢总结

简要

大家都知道Redis是以高性能据称,单个实例在不使用pipeline的情况下QPS能够达到10万QPS,Redis是一个单线程的模型,如果主线程阻塞了,就会造成响应变慢,但为什么会变慢?具体会有那些原因呢?下面从两个大的方面介绍一下访问Redis变慢的原因

原理分析

Redis内部提供了慢查询日志命令的统计功能,记录了执行命令的耗时,访问ip,访问时间,通过慢查询日志就可以看到相关命令的执行耗时,主要有两个参数控制

"slowlog-log-slower-than"    记录命令执行耗时超过时间,单位为秒
"slowlog-max-len" 最多保留多少条慢查询日志

内在原因

复杂度过高的命令

使用过于复杂的命令可能导致延迟变大可能主要在如下几点

  • 经常使用O(N)相对复杂的命令,例如KEYS, SORT、SUNION、ZUNIONSTORE 聚合类命令,需耗费大量的cpu资源
  • 使用O(N)复杂的命令,但N值非常大,比如HMSET,HMGET,HGETALL,ZADD在我们的业务中也经常出现,一次性获取或写入大量的数据,写入的数据主要在内存分配上,获取主要在返回给客户端过多的数据,更多的花费在网络传输层

Redis访问变慢总结_耗时长

操作bigkey

bigkey就是存储本身的key值空间太大,如hash,list,set等存储中value值过多。Redis在写入数据时,需要为新的数据分配内存,当从Redis中删除数据时,它会释放对应的内存空间,对一个bigkey进行操作时,会有如下影响

  • 内存空间不均匀(平衡):如 Redis Cluster 中,bigkey 会造成节点的内存空间分布不均匀
  • 超时阻塞:由于 Redis 单线程的特性,操作 bigkey 会较耗时,意味着会阻塞 Redis。
  • 网络阻塞:获取 bigkey 的网络传输较大,如:bigkey 为 2MB,每秒 2000次,每秒产生的流量 4000MB/s

我们可以通过数据库管理平台(​​开源工具rdr​​)的redis实例管理的缓存分析去发现bigkey:

Redis访问变慢总结_耗时长_02


key的集中过期

这类变慢,会在某个时间点,或者间隔时间,就会发生延迟,很有规律。主要原因是应用将key的过期时间设置的比较集中,Redis的过期数据采用惰性删除和定时主动删除,主动过期的的定时任务,是由主线程完成,也就是说当有大量删除过期key的情况,那么势必会阻塞,直至任务完成。一般可通过如下方法解决:

  • 业务上给每个key设置随机的过期时间,对过期时间进行打散,从而降低清理过期key的压力
  • 开启lazy-free 机制,当删除过期key时,放到后台线程执行
达到实例内存上线

一般都会给redis设置一个maxmemory,开启默认的LRU的淘汰策略,淘汰策略可参考​​Redis过期key删除和逐出策略​​,当达到maxmemory上限时,redis会在你访问数据时,按照淘汰策略,进行逐出一部分数据,从而保障内存在maxmemory以内,我们最常使用的一般是allkeys-lru或volatile-lru策略,它们的处理逻辑是,每次从实例中随机取出一批key(默认20),然后淘汰一个最少访问的key,之后把剩下的key暂存到一个池子中,继续随机取出一批key,并与之前池子中的key比较,再淘汰一个最少访问的key。以此循环,直到内存降到maxmemory之下。这样就会在我们应用执行命令时,增加淘汰key的耗时

fork耗时长

阿里云的Redis为了保障数据的完整性,在每个Redis上例上进行RDB和AOF数据持久化,虽然是后台异步线程进行重写,也会导致Redis访问延迟变大,主要原因是在生成RDB和AOF时,都需要父进程fork一个子进程进行数据的持久化,在fork过程中,父进程会拷贝内存页表给子进程,如果单个实例内存较大,那么拷贝的内存页就相对比较耗时,就会消耗大量的cpu资源,在fork进程完成前,会阻塞整个实例,从业务的表现看就是会有大量的timeout,可通过info查看,最后一次rdb和aof的耗时

 info Persistence
# Persistence
loading:0
rdb_changes_since_last_save:472528
rdb_bgsave_in_progress:0
rdb_last_save_time:1588988675
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

碎片整理

内存碎片整理是在主线程中执行的,内存碎片整理操作会 scan (通过迭代进行)整个 redis 节点,并进行内存复制、转移等操作 碎片整理的参数主要有

 # 开启自动内存碎片整理(总开关)
activedefrag yes
# 当碎片达到 100mb 时,开启内存碎片整理
active-defrag-ignore-bytes 100mb
# 当碎片超过 10% 时,开启内存碎片整理
active-defrag-threshold-lower 10
# 内存碎片超过 100%,则尽最大努力整理
active-defrag-threshold-upper 100
# 内存自动整理占用资源最小百分比
active-defrag-cycle-min 25
# 内存自动整理占用资源最大百分比
active-defrag-cycle-max 75

数据持久化
  • AOF 配置为 appendfsync always,那么 Redis 每处理一次写操作,都会把这个命令写入到磁盘中才返回,整个过程都是在主线程执行的,这个过程会加重 Redis 写负担。操作磁盘要比操作内存慢几百倍,采用这个配置会严重拖慢 Redis 的性能
  • 当 Redis 后台线程在执行 AOF 文件刷盘时,如果此时磁盘的 IO 负载很高,那这个后台线程在执行刷盘操作(fsync系统调用)时就会被阻塞住。此时的主线程依旧会接收写请求,紧接着,主线程又需要把数据写到文件内存中(write 系统调用),但此时的后台子线程由于磁盘负载过高,导致 fsync 发生阻塞,迟迟不能返回,那主线程在执行 write 系统调用时,也会被阻塞住,直到后台线程 fsync 执行完成后,主线程执行 write 才能成功返回。

外在原因

网络抖动

由于一些网络链路长,某个网络的抖动,导致数据的发送延迟,丢包,从而拖慢访问redis,程序上也应该考虑重连机制,从而因为网络抖动问题导致的不可访问

网络带宽限制

网卡负载过高,在网络层和TCP层就会出现数据发送延迟、数据丢包等情况。Redis的高性能除了内存之外,就在于网络IO,请求量突增会导致网卡负载变高。

短链接

频繁的短连接会导致 Redis 大量时间耗费在连接的建立和释放上,TCP 的三次握手和四次挥手同样也会增加访问延迟。所以建议业务上尽量使用长连接

使用swap

当物理机内存不充足,导致操作系统使用swap,从而导致内存中的数据被存储到磁盘上,当应用程序访问数据时,是从磁盘上读取数据,而磁盘的速度比内存要慢得多,导致访问Redis变慢

服务器资源抢占

由于物理机可能存在混部的情况,就会产生服务器cpu,内存,网络,磁盘等资源的抢占,导致Redis因为资源不足导致变慢

总结

实际上,影响Redis系统性能的影响很多,这里有一份​​redis开发规范​​可做参考

  • 作为研发人员,我们需要了解 Redis 的基本原理,更合理地使用 Redis 命令,并且结合业务场景进行优化。
  • 作为运维人员,需要了解 Redis 运行机制及架构,及时的关注监控,及早地去预防和发现问题,结合操作系统,网络,Redis运行机制去分析问题,解决问题
举报

相关推荐

0 条评论