版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 redis为什么变慢了常见延迟问题定位与分析 导读redis作为内存数据库,拥有非常高的性能,单个实例的qps能够达到10w左右。但我们在使用redis时,经常时不时会出现访问延迟很大的情况,如果你不知道redis的内部实现原理,在排查问题时就会一头雾水。很多时候,redis出现访问延迟变大,都与我们的使用不当或运维不合理导致的。这篇文章我们就来分析一下redis在使用过程中,经常会遇到的延迟问题以及如何定位和分析。来源:http:/kaito-redis作为内存数据库,拥有非常高的性能,单个实例的qps能够达到10w左右。但我们在使用redis时,经常时不时会出现访问延迟很大的情况,如果你
2、不知道redis的内部实现原理,在排查问题时就会一头雾水。很多时候,redis出现访问延迟变大,都与我们的使用不当或运维不合理导致的。这篇文章我们就来分析一下redis在使用过程中,经常会遇到的延迟问题以及如何定位和分析。使用复杂度高的命令如果在使用redis时,发现访问延迟突然增大,如何进行排查?首先,第一步,建议你去查看一下redis的慢日志。redis提供了慢日志命令的统计功能,我们通过以下设置,就可以查看有哪些命令在执行时延迟比较大。首先设置redis的慢日志阈值,只有超过阈值的命令才会被记录,这里的单位是微妙,例如设置慢日志的阈值为5毫秒,同时设置只保留最近1000条慢日志记录:12
3、34 # 命令执行超过5毫秒记录慢日志config set slowlog-log-slower-than 5000# 只保留最近1000条慢日志config set slowlog-max-len 1000 设置完成之后,所有执行的命令如果延迟大于5毫秒,都会被redis记录下来,我们执行slowlog get 5查询最近5条慢日志:1234567891011121314 :6379 slowlog get 51) 1) (integer) 32693 # 慢日志id 2) (integer) 1593763337 # 执行时间 3) (integer) 5299 # 执行
4、耗时(微妙) 4) 1) lrange # 具体执行的命令和参数 2) user_list_2000 3) 0 4) -12) 1) (integer) 32692 2) (integer) 1593763337 3) (integer) 5044 4) 1) get 2) book_price_1000. 通过查看慢日志记录,我们就可以知道在什么时间执行哪些命令比较耗时,如果你的业务经常使用o(n)以上复杂度的命令,例如sort、sunion、zunionstore,或者在执行o(n)命令时操作的数据量比较大,这些情况下redis处理数据时就会很耗时。如果你的服务请求量并不大,但redis实
5、例的cpu使用率很高,很有可能是使用了复杂度高的命令导致的。解决方案就是,不使用这些复杂度较高的命令,并且一次不要获取太多的数据,每次尽量操作少量的数据,让redis可以及时处理返回。存储大key如果查询慢日志发现,并不是复杂度较高的命令导致的,例如都是set、delete操作出现在慢日志记录中,那么你就要怀疑是否存在redis写入了大key的情况。redis在写入数据时,需要为新的数据分配内存,当从redis中删除数据时,它会释放对应的内存空间。如果一个key写入的数据非常大,redis在分配内存时也会比较耗时。同样的,当删除这个key的数据时,释放内存也会耗时比较久。你需要检查你的业务代码
6、,是否存在写入大key的情况,需要评估写入数据量的大小,业务层应该避免一个key存入过大的数据量。那么有没有什么办法可以扫描现在redis中是否存在大key的数据吗?redis也提供了扫描大key的方法:1 redis-cli -h $host -p $port -bigkeys -i 0.01使用上面的命令就可以扫描出整个实例key大小的分布情况,它是以类型维度来展示的。需要注意的是当我们在线上实例进行大key扫描时,redis的qps会突增,为了降低扫描过程中对redis的影响,我们需要控制扫描的频率,使用-i参数控制即可,它表示扫描过程中每次扫描的时间间隔,单位是秒。使用这个命令的原理,
7、其实就是redis在内部执行scan命令,遍历所有key,然后针对不同类型的key执行strlen、llen、hlen、scard、zcard来获取字符串的长度以及容器类型(list/dict/set/zset)的元素个数。而对于容器类型的key,只能扫描出元素最多的key,但元素最多的key不一定占用内存最多,这一点需要我们注意下。不过使用这个命令一般我们是可以对整个实例中key的分布情况有比较清晰的了解。针对大key的问题,redis官方在4.0版本推出了lazy-free的机制,用于异步释放大key的内存,降低对redis性能的影响。即使这样,我们也不建议使用大key,大key在集群的迁
8、移过程中,也会影响到迁移的性能,这个后面在介绍集群相关的文章时,会再详细介绍到。集中过期有时你会发现,平时在使用redis时没有延时比较大的情况,但在某个时间点突然出现一波延时,而且报慢的时间点很有规律,例如某个整点,或者间隔多久就会发生一次。如果出现这种情况,就需要考虑是否存在大量key集中过期的情况。如果有大量的key在某个固定时间点集中过期,在这个时间点访问redis时,就有可能导致延迟增加。redis的过期策略采用主动过期+懒惰过期两种策略: 主动过期:redis内部维护一个定时任务,默认每隔100毫秒会从过期字典中随机取出20个key,删除过期的key,如果过期key的比例超过了25
9、%,则继续获取20个key,删除过期的key,循环往复,直到过期key的比例下降到25%或者这次任务的执行耗时超过了25毫秒,才会退出循环 懒惰过期:只有当访问某个key时,才判断这个key是否已过期,如果已经过期,则从实例中删除注意,redis的主动过期的定时任务,也是在redis主线程中执行的,也就是说如果在执行主动过期的过程中,出现了需要大量删除过期key的情况,那么在业务访问时,必须等这个过期任务执行结束,才可以处理业务请求。此时就会出现,业务访问延时增大的问题,最大延迟为25毫秒。而且这个访问延迟的情况,不会记录在慢日志里。慢日志中只记录真正执行某个命令的耗时,redis主动过期策略
10、执行在操作命令之前,如果操作命令耗时达不到慢日志阈值,它是不会计算在慢日志统计中的,但我们的业务却感到了延迟增大。此时你需要检查你的业务,是否真的存在集中过期的代码,一般集中过期使用的命令是expireat或pexpireat命令,在代码中搜索这个关键字就可以了。如果你的业务确实需要集中过期掉某些key,又不想导致redis发生抖动,有什么优化方案?解决方案是,在集中过期时增加一个随机时间,把这些需要过期的key的时间打散即可。伪代码可以这么写:12 # 在过期时间点之后的5分钟内随机过期掉redis.expireat(key, expire_time + random(300) 这样redi
11、s在处理过期时,不会因为集中删除key导致压力过大,阻塞主线程。另外,除了业务使用需要注意此问题之外,还可以通过运维手段来及时发现这种情况。做法是我们需要把redis的各项运行数据监控起来,执行info可以拿到所有的运行数据,在这里我们需要重点关注expired_keys这一项,它代表整个实例到目前为止,累计删除过期key的数量。我们需要对这个指标监控,当在很短时间内这个指标出现突增时,需要及时报警出来,然后与业务报慢的时间点对比分析,确认时间是否一致,如果一致,则可以认为确实是因为这个原因导致的延迟增大。实例内存达到上限有时我们把redis当做纯缓存使用,就会给实例设置一个内存上限maxme
12、mory,然后开启lru淘汰策略。当实例的内存达到了maxmemory后,你会发现之后的每次写入新的数据,有可能变慢了。导致变慢的原因是,当redis内存达到maxmemory后,每次写入新的数据之前,必须先踢出一部分数据,让内存维持在maxmemory之下。这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于配置的淘汰策略: allkeys-lru:不管key是否设置了过期,淘汰最近最少访问的key volatile-lru:只淘汰最近最少访问并设置过期的key allkeys-random:不管key是否设置了过期,随机淘汰 volatile-random:只随机淘汰有设置过
13、期的key allkeys-ttl:不管key是否设置了过期,淘汰即将过期的key noeviction:不淘汰任何key,满容后再写入直接报错 allkeys-lfu:不管key是否设置了过期,淘汰访问频率最低的key(4.0+支持) volatile-lfu:只淘汰访问频率最低的过期key(4.0+支持)具体使用哪种策略,需要根据业务场景来决定。我们最常使用的一般是allkeys-lru或volatile-lru策略,它们的处理逻辑是,每次从实例中随机取出一批key(可配置),然后淘汰一个最少访问的key,之后把剩下的key暂存到一个池子中,继续随机取出一批key,并与之前池子中的key比
14、较,再淘汰一个最少访问的key。以此循环,直到内存降到maxmemory之下。如果使用的是allkeys-random或volatile-random策略,那么就会快很多,因为是随机淘汰,那么就少了比较key访问频率时间的消耗了,随机拿出一批key后直接淘汰即可,因此这个策略要比上面的lru策略执行快一些。但以上这些逻辑都是在访问redis时,真正命令执行之前执行的,也就是它会影响我们访问redis时执行的命令。另外,如果此时redis实例中有存储大key,那么在淘汰大key释放内存时,这个耗时会更加久,延迟更大,这需要我们格外注意。如果你的业务访问量非常大,并且必须设置maxmemory限制
15、实例的内存上限,同时面临淘汰key导致延迟增大的的情况,要想缓解这种情况,除了上面说的避免存储大key、使用随机淘汰策略之外,也可以考虑拆分实例的方法来缓解,拆分实例可以把一个实例淘汰key的压力分摊到多个实例上,可以在一定程度降低延迟。fork耗时严重如果你的redis开启了自动生成rdb和aof重写功能,那么有可能在后台生成rdb和aof重写时导致redis的访问延迟增大,而等这些任务执行完毕后,延迟情况消失。遇到这种情况,一般就是执行生成rdb和aof重写任务导致的。生成rdb和aof都需要父进程fork出一个子进程进行数据的持久化,在fork执行过程中,父进程需要拷贝内存页表给子进程,
16、如果整个实例内存占用很大,那么需要拷贝的内存页表会比较耗时,此过程会消耗大量的cpu资源,在完成fork之前,整个实例会被阻塞住,无法处理任何请求,如果此时cpu资源紧张,那么fork的时间会更长,甚至达到秒级。这会严重影响redis的性能。具体原理也可以参考我之前写的文章:redis持久化是如何做的?rdb和aof对比分析。我们可以执行info命令,查看最后一次fork执行的耗时latest_fork_usec,单位微妙。这个时间就是整个实例阻塞无法处理请求的时间。除了因为备份的原因生成rdb之外,在主从节点第一次建立数据同步时,主节点也会生成rdb文件给从节点进行一次全量同步,这时也会对r
17、edis产生性能影响。要想避免这种情况,我们需要规划好数据备份的周期,建议在从节点上执行备份,而且最好放在低峰期执行。如果对于丢失数据不敏感的业务,那么不建议开启aof和aof重写功能。另外,fork的耗时也与系统有关,如果把redis部署在虚拟机上,那么这个时间也会增大。所以使用redis时建议部署在物理机上,降低fork的影响。绑定cpu很多时候,我们在部署服务时,为了提高性能,降低程序在使用多个cpu时上下文切换的性能损耗,一般会采用进程绑定cpu的操作。但在使用redis时,我们不建议这么干,原因如下。绑定cpu的redis,在进行数据持久化时,fork出的子进程,子进程会继承父进程的
18、cpu使用偏好,而此时子进程会消耗大量的cpu资源进行数据持久化,子进程会与主进程发生cpu争抢,这也会导致主进程的cpu资源不足访问延迟增大。所以在部署redis进程时,如果需要开启rdb和aof重写机制,一定不能进行cpu绑定操作!开启aof上面提到了,当执行aof文件重写时会因为fork执行耗时导致redis延迟增大,除了这个之外,如果开启aof机制,设置的策略不合理,也会导致性能问题。开启aof后,redis会把写入的命令实时写入到文件中,但写入文件的过程是先写入内存,等内存中的数据超过一定阈值或达到一定时间后,内存中的内容才会被真正写入到磁盘中。aof为了保证文件写入磁盘的安全性,提
19、供了3种刷盘机制: appendfsync always:每次写入都刷盘,对性能影响最大,占用磁盘io比较高,数据安全性最高 appendfsync everysec:1秒刷一次盘,对性能影响相对较小,节点宕机时最多丢失1秒的数据 appendfsync no:按照操作系统的机制刷盘,对性能影响最小,数据安全性低,节点宕机丢失数据取决于操作系统刷盘机制当使用第一种机制appendfsync always时,redis每处理一次写命令,都会把这个命令写入磁盘,而且这个操作是在主线程中执行的。内存中的的数据写入磁盘,这个会加重磁盘的io负担,操作磁盘成本要比操作内存的代价大得多。如果写入量很大,那
20、么每次更新都会写入磁盘,此时机器的磁盘io就会非常高,拖慢redis的性能,因此我们不建议使用这种机制。与第一种机制对比,appendfsync everysec会每隔1秒刷盘,而appendfsync no取决于操作系统的刷盘时间,安全性不高。因此我们推荐使用appendfsync everysec这种方式,在最坏的情况下,只会丢失1秒的数据,但它能保持较好的访问性能。当然,对于有些业务场景,对丢失数据并不敏感,也可以不开启aof。使用swap如果你发现redis突然变得非常慢,每次访问的耗时都达到了几百毫秒甚至秒级,那此时就检查redis是否使用到了swap,这种情况下redis基本上已经
21、无法提供高性能的服务。我们知道,操作系统提供了swap机制,目的是为了当内存不足时,可以把一部分内存中的数据换到磁盘上,以达到对内存使用的缓冲。但当内存中的数据被换到磁盘上后,访问这些数据就需要从磁盘中读取,这个速度要比内存慢太多!尤其是针对redis这种高性能的内存数据库来说,如果redis中的内存被换到磁盘上,对于redis这种性能极其敏感的数据库,这个操作时间是无法接受的。我们需要检查机器的内存使用情况,确认是否确实是因为内存不足导致使用到了swap。如果确实使用到了swap,要及时整理内存空间,释放出足够的内存供redis使用,然后释放redis的swap,让redis重新使用内存。释放redis的swap过程通常要重启实例,为了避免重启实例对业务的影响,一般先进行主从切换,然后释放旧主节点的swap,重新启动服务,待数据同步完成后,再切换回主节点即可。可见,当redis使用到swap后,此时的redis的高性能基本被废掉,所以我们需要提前预防这种情况。我们需要对redis机器的内存和swap使用情况进行监控,在内存不足和使用到swap时及时报警出来,及时进行相应的处理。网卡负载过高如果以上产生性能问题的场景,你都规避掉了,而且redis
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025版木材采购合同与木材质量保证协议4篇
- 2025八年级上学期期末历史试卷
- 2025年度二零二五年度智能交通管理系统设计与实施合同4篇
- 二零二五年度木制品表面处理合同样本4篇
- 2025版学校教室租赁合同示范文本2篇
- 2025年度个人毛坯房租赁与租金支付方式合同4篇
- 公共基础-2020年试验检验师助理《公共基础》真题
- 宝石矿物学在宝石加工中的应用研究考核试卷
- 2025版土地居间业务规范合同样本(2025版)6篇
- 2025版图书销售代理居间服务合同模板
- 加强教师队伍建设教师领域学习二十届三中全会精神专题课
- 2024-2025学年人教版数学七年级上册期末复习卷(含答案)
- 2024年决战行测5000题言语理解与表达(培优b卷)
- 四年级数学上册人教版24秋《小学学霸单元期末标准卷》考前专项冲刺训练
- 2025年慢性阻塞性肺疾病全球创议GOLD指南修订解读课件
- (完整版)减数分裂课件
- 银行办公大楼物业服务投标方案投标文件(技术方案)
- 第01讲 直线的方程(九大题型)(练习)
- 饭店管理基础知识(第三版)中职PPT完整全套教学课件
- 2023年重庆市中考物理A卷试卷【含答案】
- 【打印版】意大利斜体英文字帖(2022年-2023年)
评论
0/150
提交评论