使用复杂度高的命令如果在使用redis时,发现访问延迟突然增大,如何进行排查?
首先,第一步,建议你去查看一下redis的慢日志。通过redis的慢日志命令统计功能,我们可以设置以下选项来查看哪些命令在执行时产生了较大的延迟。
首先设置redis的慢日志阈值,只有超过阈值的命令才会被记录,这里的单位是微妙,例如设置慢日志的阈值为5毫秒,同时设置只保留最近1000条慢日志记录:
# 命令执行超过5毫秒记录慢日志config set slowlog-log-slower-than 5000# 只保留最近1000条慢日志config set slowlog-max-len 1000
设置完成之后,所有执行的命令如果延迟大于5毫秒,都会被redis记录下来,我们执行slowlog get 5查询最近5条慢日志:
127.0.0.1:6379> slowlog get 51) 1) (integer) 32693 # 慢日志id 2) (integer) 1593763337 # 执行时间 3) (integer) 5299 # 执行耗时(微妙) 4) 1) "lrange" # 具体执行的命令和参数 2) "user_list_2000" 3) "0" 4) "-1"2) 1) (integer) 32692 2) (integer) 1593763337 3) (integer) 5044 4) 1) "get" 2) "book_price_1000"...
通过查看慢日志记录,我们就可以知道在什么时间执行哪些命令比较耗时,如果你的业务经常使用o(n)以上复杂度的命令,例如sort、sunion、zunionstore、keys、scan,或者在执行o(n)命令时操作的数据量比较大,这些情况下redis处理数据时就会很耗时。
如果redis实例的cpu使用率很高,但你的服务请求量并不大,很可能是因为使用了具有高复杂度的命令所导致的。
解决方案就是,不使用这些复杂度较高的命令,并且一次不要获取太多的数据,每次尽量操作少量的数据,让redis可以及时处理返回。
存储bigkey如果查询慢日志发现,并不是复杂度较高的命令导致的,例如都是set、delete操作出现在慢日志记录中,那么你就要怀疑是否存在redis写入了bigkey的情况。
当redis写入新数据时,会为其分配内存空间,而当数据被从redis中删除时,相应的内存空间也会被释放。
当一个键写入的数据非常大时,redis分配内存也会变得更加耗时。同样的,当删除这个key的数据时,释放内存也会耗时比较久。
你需要检查你的业务代码,是否存在写入bigkey的情况,需要评估写入数据量的大小,业务层应该避免一个key存入过大的数据量。
针对bigkey的问题,redis官方在4.0版本推出了lazy-free的机制,用于异步释放bigkey的内存,降低对redis性能的影响。即使这样,我们也不建议使用bigkey,bigkey在集群的迁移过程中,也会影响到迁移的性能,这个后面在介绍集群相关的文章时,会再详细介绍到。
集中过期有时你会发现,平时在使用redis时没有延时比较大的情况,但在某个时间点突然出现一波延时,而且报慢的时间点很有规律,例如某个整点,或者间隔多久就会发生一次。
如果出现这种情况,就需要考虑是否存在大量key集中过期的情况。
如果有大量的key在某个固定时间点集中过期,在这个时间点访问redis时,就有可能导致延迟增加。
redis的过期策略采用定期删除+惰性删除两种策略;
注意,redis的定期删除的定时任务,也是在redis主线程中执行的,也就是说如果在执行主动过期的过程中,出现了需要大量删除过期key的情况,那么在业务访问时,必须等这个过期任务执行结束,才可以处理业务请求。此时就会出现,业务访问延时增大的问题,最大延迟为25毫秒。
而且这个访问延迟的情况,不会记录在慢日志里。慢日志中只记录真正执行某个命令的耗时,redis主动过期策略执行在操作命令之前,如果操作命令耗时达不到慢日志阈值,它是不会计算在慢日志统计中的,但我们的业务却感到了延迟增大。
解决方案是,在集中过期时增加一个随机时间,把这些需要过期的key的时间打散即可。
实例内存达到上限有时我们把redis当做纯缓存使用,就会给实例设置一个内存上限maxmemory,然后开启lru淘汰策略。
当实例的内存达到了maxmemory后,你会发现之后的每次写入新的数据,有可能变慢了。
导致变慢的原因是,当redis内存达到maxmemory后,每次写入新的数据之前,必须先踢出一部分数据,让内存维持在maxmemory之下。
这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于配置的淘汰策略
fork耗时严重如果你的redis开启了自动生成rdb和aof重写功能,那么有可能在后台生成rdb和aof重写时导致redis的访问延迟增大,而等这些任务执行完毕后,延迟情况消失。
遇到这种情况,一般就是执行生成rdb和aof重写任务导致的。
生成rdb和aof都需要父进程fork出一个子进程进行数据的持久化,在fork执行过程中,父进程需要拷贝内存页表给子进程,如果整个实例内存占用很大,那么需要拷贝的内存页表会比较耗时,此过程会消耗大量的cpu资源,在完成fork之前,整个实例会被阻塞住,无法处理任何请求,如果此时cpu资源紧张,那么fork的时间会更长,甚至达到秒级。这会严重影响redis的性能。
绑定cpu很多时候,我们在部署服务时,为了提高性能,降低程序在使用多个cpu时上下文切换的性能损耗,一般会采用进程绑定cpu的操作。
但在使用redis时,我们不建议这么干,原因如下。
绑定cpu的redis,在进行数据持久化时,fork出的子进程,子进程会继承父进程的cpu使用偏好,而此时子进程会消耗大量的cpu资源进行数据持久化,子进程会与主进程发生cpu争抢,这也会导致主进程的cpu资源不足访问延迟增大。
所以在部署redis进程时,如果需要开启rdb和aof重写机制,一定不能进行cpu绑定操作
使用swap如果你发现redis突然变得非常慢,每次访问的耗时都达到了几百毫秒甚至秒级,那此时就检查redis是否使用到了swap,这种情况下redis基本上已经无法提供高性能的服务。
我们知道,操作系统提供了swap机制,目的是为了当内存不足时,可以把一部分内存中的数据换到磁盘上,以达到对内存使用的缓冲。
但当内存中的数据被换到磁盘上后,访问这些数据就需要从磁盘中读取,这个速度要比内存慢太多!
尤其是针对redis这种高性能的内存数据库来说,如果redis中的内存被换到磁盘上,对于redis这种性能极其敏感的数据库,这个操作时间是无法接受的。可以临时关闭操作系统swap
网卡负载过高特点就是从某个时间点之后就开始变慢,并且一直持续。此时,你需要检查一下机器的网卡流量是否存在被耗尽的情况。
高网络负载会导致在网络层和tcp层级上出现数据发送延迟和数据丢失等问题。除了内存之外,redis之所以具有高性能,是因为其网络io表现出色。然而,随着请求量不断增加,网卡负载也会相应地增加。
如果出现这种情况,你需要排查这个机器上的哪个redis实例的流量过大占满了网络带宽,然后确认流量突增是否属于业务正常情况,如果属于那就需要及时扩容或迁移实例,避免这个机器的其他实例受到影响。
以上就是redis常见延迟问题怎么解决的详细内容。