浅谈Redis变慢的原因及排查方法

目录
  • 原因1:实例内存达到上限
  • 原因2:开启内存大页
  • 原因3:使用Swap
  • 原因4:网络带宽过载
  • 原因5:其他原因

原因1:实例内存达到上限

排查思路

如果你的 Redis 实例设置了内存上限 maxmemory,那么也有可能导致 Redis 变慢。

当我们把 Redis 当做纯缓存使用时,通常会给这个实例设置一个内存上限 maxmemory,然后设置一个数据淘汰策略。而当实例的内存达到了 maxmemory 后,你可能会发现,在此之后每次写入新数据,操作延迟变大了。

导致变慢的原因

当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在 maxmemory 之下,然后才能把新数据写进来。

这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于你配置的淘汰策略:

  • allkeys-lru:不管 key 是否设置了过期,淘汰最近最少访问的 key
  • volatile-lru:只淘汰最近最少访问、并设置了过期时间的 key
  • allkeys-random:不管 key 是否设置了过期,随机淘汰 key
  • volatile-random:只随机淘汰设置了过期时间的 key
  • allkeys-ttl:不管 key 是否设置了过期,淘汰即将过期的 key
  • noeviction:不淘汰任何 key,实例内存达到 maxmeory 后,再写入新数据直接返回错误
  • allkeys-lfu:不管 key 是否设置了过期,淘汰访问频率最低的 key(4.0+版本支持)
  • volatile-lfu:只淘汰访问频率最低、并设置了过期时间 key(4.0+版本支持)

具体使用哪种策略,我们需要根据具体的业务场景来配置。一般最常使用的是 allkeys-lru / volatile-lru 淘汰策略,它们的处理逻辑是,每次从实例中随机取出一批 key(这个数量可配置),然后淘汰一个最少访问的 key,之后把剩下的 key 暂存到一个池子中,继续随机取一批 key,并与之前池子中的 key 比较,再淘汰一个最少访问的 key。以此往复,直到实例内存降到 maxmemory 之下。

需要注意的是,Redis 的淘汰数据的逻辑与删除过期 key 的一样,也是在命令真正执行之前执行的,也就是说它也会增加我们操作 Redis 的延迟,而且,写 OPS 越高,延迟也会越明显。

另外,如果此时你的 Redis 实例中还存储了 bigkey,那么在淘汰删除 bigkey 释放内存时,也会耗时比较久。

看到了么?bigkey 的危害到处都是,这也是前面我提醒你尽量不存储 bigkey 的原因。

解决方案

  • 避免存储 bigkey,降低释放内存的耗时
  • 淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多(视业务情况调整)
  • 拆分实例,把淘汰 key 的压力分摊到多个实例上
  • 如果使用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行(配置 lazyfree-lazy-eviction = yes)

原因2:开启内存大页

排查思路

  • 我们都知道,应用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。
  • Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存。
  • 应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。

导致变慢的原因

  • 当 Redis 在执行后台 RDB 和 AOF rewrite 时,采用 fork 子进程的方式来处理。但主进程 fork 子进程后,此时的主进程依旧是可以接收写请求的,而进来的写请求,会采用 Copy On Write(写时复制)的方式操作内存数据。
  • 也就是说,主进程一旦有数据需要修改,Redis 并不会直接修改现有内存中的数据,而是先将这块内存数据拷贝出来,再修改这块新内存的数据,这就是所谓的「写时复制」。
  • 写时复制你也可以理解成,谁需要发生写操作,谁就需要先拷贝,再修改。
  • 这样做的好处是,父进程有任何写操作,并不会影响子进程的数据持久化(子进程只持久化 fork 这一瞬间整个实例中的所有数据即可,不关心新的数据变更,因为子进程只需要一份内存快照,然后持久化到磁盘上)。
  • 但是请注意,主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到 Redis 性能。
  • 同样地,如果这个写请求操作的是一个 bigkey,那主进程在拷贝这个 bigkey 内存块时,一次申请的内存会更大,时间也会更久。可见,bigkey 在这里又一次影响到了性能。

解决方案

关闭内存大页机制。

首先,你需要查看 Redis 机器是否开启了内存大页:

$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

如果输出选项是 always,就表示目前开启了内存大页机制,我们需要关掉它:

$ echo never > /sys/kernel/mm/transparent_hugepage/enabled

其实,操作系统提供的内存大页机制,其优势是,可以在一定程序上降低应用程序申请内存的次数。

但是对于 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望 Redis 在每次申请内存时,耗时尽量短,所以我不建议你在 Redis 机器上开启这个机制。

原因3:使用Swap

排查思路

如果你发现 Redis 突然变得非常慢,每次的操作耗时都达到了几百毫秒甚至秒级,那此时你就需要检查 Redis 是否使用到了 Swap,在这种情况下 Redis 基本上已经无法提供高性能的服务了。

导致变慢的原因

什么是 Swap?为什么使用 Swap 会导致 Redis 的性能下降?

如果你对操作系统有些了解,就会知道操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是 Swap。

问题就在于,当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就需要从磁盘上读取,访问磁盘的速度要比访问内存慢几百倍!尤其是针对 Redis 这种对性能要求极高、性能极其敏感的数据库来说,这个操作延时是无法接受的。

此时,你需要检查 Redis 机器的内存使用情况,确认是否存在使用了 Swap。你可以通过以下方式来查看 Redis 进程是否使用到了 Swap:

# 先找到 Redis 的进程 ID
$ ps -aux | grep redis-server

# 查看 Redis Swap 使用情况
$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'

输出结果如下

Size:               1256 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:              63488 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:              65404 kB
Swap:                  0 kB
Size:            1921024 kB
Swap:                  0 kB
...

这个结果会列出 Redis 进程的内存使用情况。

每一行 Size 表示 Redis 所用的一块内存大小,Size 下面的 Swap 就表示这块 Size 大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了。

如果只是少量数据被换到磁盘上,例如每一块 Swap 占对应 Size 的比例很小,那影响并不是很大。如果是几百兆甚至上 GB 的内存被换到了磁盘上,那么你就需要警惕了,这种情况 Redis 的性能肯定会急剧下降。

解决方案

  • 增加机器的内存,让 Redis 有足够的内存可以使用
  • 整理内存空间,释放出足够的内存供 Redis 使用,然后释放 Redis 的 Swap,让 Redis 重新使用内存

释放 Redis 的 Swap 过程通常要重启实例,为了避免重启实例对业务的影响,一般会先进行主从切换,然后释放旧主节点的 Swap,重启旧主节点实例,待从库数据同步完成后,再进行主从切换即可。

可见,当 Redis 使用到 Swap 后,此时的 Redis 性能基本已达不到高性能的要求(你可以理解为武功被废),所以你也需要提前预防这种情况。

预防的办法就是,你需要对 Redis 机器的内存和 Swap 使用情况进行监控,在内存不足或使用到 Swap 时报警出来,及时处理。

原因4:网络带宽过载

排查思路

如果以上产生性能问题的场景,你都规避掉了,而且 Redis 也稳定运行了很长时间,但在某个时间点之后开始,操作 Redis 突然开始变慢了,而且一直持续下去,这种情况又是什么原因导致?

此时你需要排查一下 Redis 机器的网络带宽是否过载,是否存在某个实例把整个机器的网路带宽占满的情况。

导致变慢的原因

网络带宽过载的情况下,服务器在 TCP 层和网络层就会出现数据包发送延迟、丢包等情况。

Redis 的高性能,除了操作内存之外,就在于网络 IO 了,如果网络 IO 存在瓶颈,那么也会严重影响 Redis 的性能。

解决方案

  • 及时确认占满网络带宽 Redis 实例,如果属于正常的业务访问,那就需要及时扩容或迁移实例了,避免因为这个实例流量过大,影响这个机器的其他实例。
  • 运维层面,你需要对 Redis 机器的各项指标增加监控,包括网络流量,在网络流量达到一定阈值时提前报警,及时确认和扩容。

原因5:其他原因

1) 频繁短连接

你的业务应用,应该使用长连接操作 Redis,避免频繁的短连接。

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

2) 运维监控

前面我也提到了,要想提前预知 Redis 变慢的情况发生,必不可少的就是做好完善的监控。

监控其实就是对采集 Redis 的各项运行时指标,通常的做法是监控程序定时采集 Redis 的 INFO 信息,然后根据 INFO 信息中的状态数据做数据展示和报警。

这里我需要提醒你的是,在写一些监控脚本,或使用开源的监控组件时,也不能掉以轻心。

在写监控脚本访问 Redis 时,尽量采用长连接的方式采集状态信息,避免频繁短连接。同时,你还要注意控制访问 Redis 的频率,避免影响到业务请求。

在使用一些开源的监控组件时,最好了解一下这些组件的实现原理,以及正确配置这些组件,防止出现监控组件发生 Bug,导致短时大量操作 Redis,影响 Redis 性能的情况发生。

我们当时就发生过,DBA 在使用一些开源组件时,因为配置和使用问题,导致监控程序频繁地与 Redis 建立和断开连接,导致 Redis 响应变慢。

3)其它程序争抢资源

最后需要提醒你的是,你的 Redis 机器最好专项专用,只用来部署 Redis 实例,不要部署其他应用程序,尽量给 Redis 提供一个相对「安静」的环境,避免其它程序占用 CPU、内存、磁盘资源,导致分配给 Redis 的资源不足而受到影响。

到此这篇关于浅谈Redis变慢的原因及排查方法的文章就介绍到这了,更多相关Redis变慢内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Redis分析慢查询操作的实例教程

    什么是慢查询 慢查询的作用:通过慢查询分析,找到有问题的命令进行优化. 和mysql的慢SQL日志分析一样,redis也有类似的功能,来帮助定位一些慢查询操作. Redis slowlog是Redis用来记录查询执行时间的日志系统. 查询执行时间指的是不包括像客户端响应(talking).发送回复等IO操作,而单单是执行一个查询命令所耗费的时间. 另外,slow log保存在内存里面,读写速度非常快,因此你可以放心地使用它,不必担心因为开启slow log而损害Redis的速度. 慢查询日志四个

  • 一分钟搞懂Redis的慢查询日志操作

    目录 什么是慢查询? 什么是慢查询日志? 如何获取慢查询日志? 如何获取慢查询日志的长度? 如何清理慢查询日志? 怎么配置慢查询的参数? slowlog-log-slower-than slowlog-max-len 如何进行配置 总结 什么是慢查询? 慢查询,顾名思义就是比较慢的查询,但是究竟是哪里慢呢?首先,我们了解一下Redis命令执行的整个过程: 发送命令 命令排队 命令执行 返回结果 在慢查询的定义中,统计比较慢的时间段指的是命令执行这个步骤.没有慢查询,并不表示客户端没有超时问题,有

  • 浅谈Redis的keys命令到底有多慢

    keys命令的用法: keys pattern 查找符合正则匹配的key的列表.扫描对象是Redis服务中所有的key,想想都很慢对不对? 同时执行keys命令的同时,Redis进程将被阻塞,无法执行其他命令,假如超过了哨兵的down-after-milliseconds配置,还会进行主从切换,切换过程中,如果主节点恢复正常,还可能出现脑裂等一系列问题. 所以,生产环境中,建议直接禁用keys命令. Keys命令的替代方案 1.scan扫描,避免阻塞 2.将需要统计的数据放入一个set中 (但是

  • 详解Redis的慢查询日志

    Redis慢查询日志帮助开发和运维人员定位系统存在的慢操作.慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(慢查询ID,发生时间戳,耗时,命令的详细信息)记录下来. Redis客户端一条命令分为如下四部分执行: 需要注意的是,慢查询日志只是统计步骤3)执行命令的时间,所以慢查询并不代表客户端没有超时问题.需要注意的是,慢查询日志只是统计步骤3)执行命令的时间,所以慢查询并不代表客户端没有超时问题. 一.慢查询的配置参数: 慢查询的预设阀值 slow

  • 浅谈Redis变慢的原因及排查方法

    目录 原因1:实例内存达到上限 原因2:开启内存大页 原因3:使用Swap 原因4:网络带宽过载 原因5:其他原因 原因1:实例内存达到上限 排查思路 如果你的 Redis 实例设置了内存上限 maxmemory,那么也有可能导致 Redis 变慢. 当我们把 Redis 当做纯缓存使用时,通常会给这个实例设置一个内存上限 maxmemory,然后设置一个数据淘汰策略.而当实例的内存达到了 maxmemory 后,你可能会发现,在此之后每次写入新数据,操作延迟变大了. 导致变慢的原因 当 Red

  • 浅谈Redis高并发缓存架构性能优化实战

    目录 场景1: 中小型公司Redis缓存架构以及线上问题实战 场景2: 大厂线上大规模商品缓存数据冷热分离实战 场景3: 基于DCL机制解决热点缓存并发重建问题实战 场景4: 突发性热点缓存重建导致系统压力暴增 场景5: 解决大规模缓存击穿导致线上数据库压力暴增 场景6: 黑客工资导致缓存穿透线上数据库宕机 场景7: 大V直播带货导致线上商品系统崩溃原因分析 场景8: Redis分布式锁解决缓存与数据库双写不一致问题实战 场景9: 大促压力暴增导致分布式锁串行争用问题优化 场景10: 利用多级缓

  • 浅谈Redis常见延迟问题定位与分析

    目录 使用复杂度高的命令 存储bigkey 集中过期 实例内存达到上限 fork耗时严重 绑定CPU 使用Swap 网卡负载过高 使用复杂度高的命令 如果在使用Redis时,发现访问延迟突然增大,如何进行排查? 首先,第一步,建议你去查看一下Redis的慢日志.Redis提供了慢日志命令的统计功能,我们通过以下设置,就可以查看有哪些命令在执行时延迟比较大. 首先设置Redis的慢日志阈值,只有超过阈值的命令才会被记录,这里的单位是微妙,例如设置慢日志的阈值为5毫秒,同时设置只保留最近1000条慢

  • 浅谈redis的maxmemory设置以及淘汰策略

    redis的maxmemory参数用于控制redis可使用的最大内存容量.如果超过maxmemory的值,就会动用淘汰策略来处理expaire字典中的键. 关于redis的淘汰策略: Redis提供了下面几种淘汰策略供用户选择,其中默认的策略为noeviction策略: ·   noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错. ·   allkeys-lru:在主键空间中,优先移除最近未使用的key. ·   volatile-lru:在设置了过期时间的键空间中,优

  • 浅谈redis整数集为什么不能降级

    目录 前言 基本结构 何时使用intset intset 添加元素 类型变动 升级 加入65535 旧数据移位 降级 为什么不实现降级 小结 前言 整数集合相信有的同学没有听说过,因为redis对外提供的只有封装的五大对象!而我们本系列主旨是学习redis内部结构.内部结构是redis五大结构重要支撑! 前面我们分别从redis内部结构分析了redis的List.Hash.Zset三种数据结构了.今天我们再来分析set数据结构内部是如何存储的 基本结构 在src/t_set.c中我们发现这样一段

  • 浅谈Redis中的内存淘汰策略和过期键删除策略

    目录 8种淘汰策略 过期键的删除策略 总结 redis是我们现在最常用的一个工具,帮助我们建设系统的高可用,高性能. 而且我们都知道redis是一个完全基于内存的工具,这也是redis速度快的一个原因,当我们往redis中不断缓存数据的时候,其内存总有满的时候(而且内存是很贵的东西,尽量省着点用),所以尽可能把有用的数据,或者使用频繁的数据缓存在redis中,物尽其用. 那么如果正在使用的redis内存用完了,我们应该怎么取舍redis中已存在的数据和即将要存入的数据呢,我们要怎么处理呢? re

  • 浅谈Redis对于过期键的三种清除策略

    目录 Pre Redis Key的超时设置处理 被动删除 主动删除 当前已用内存超过maxmemory限定时,触发主动清理策略 对于过期键一般有三种删除策略 定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作: 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键:如果没有过期,那就返回该键: 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键.至于删除多少过期

  • 浅谈Redis跟MySQL的双写问题解决方案

    目录 写在前面 三种读写缓存策略 Cache-AsidePattern(旁路缓存模式) Read-Through/Write-Through(读写穿透) WriteBehindPattern(异步缓存写入) 旁路缓存模式解析 CacheAsidePattern的一些疑问 CacheAsidePattern的缺陷 项目中有遇到这个问题,跟MySQL中的数据不一致,研究一番发现这里面细节并不简单,特此记录一下. 写在前面 严格意义上任何非原子操作都不可能保证一致性,除非用阻塞读写实现强一致性,所以缓

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

随机推荐