【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!

【问题】11号早上10点左右redis的cpu突然飙高,过一会儿部分节点突然因为连接超时而被熔断掉

一看到服务名立马就意识到前几天刚用scan替代keys的服务

2020-12-11 10点 redis的cpu突然飙高到100%,过一会儿部分节点突然因为连接超时而被Hystrix熔断并服务降级了

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第1张图片

2020-12-11 10点12分 运维人员在群里问谁操作redis了,我登上去看了一下阿里云Redis的慢日志,没看到任何慢日志查询, latency:eventloop 是阿里云redis的内置任务

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第2张图片

2020-12-11 10点18分 运维人员说可能出在XX微服务,我立马想起了前几天刚用scan替代keys解决cpu飙高和耗时长的问题,赶紧回滚了代码,服务恢复正常

你以为这样本文就算结束了?不!正文才刚刚开始

现在,来复盘本次问题发生的原因

出现问题的原因是,我时常查看redis慢日志,每次会发现一条keys命令,耗时基本都是600ms+,虽然它不会导致服务出问题,但看着就是难受

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第3张图片

引起这个慢查询的罪魁祸首就是 @CacheEvict(allEntries = true) , 看一下 allEntries = true 的源码实现

此处暂时一下,我去补一下这块源码解析,之前有看但是没写源码解析

【Redis】@CacheEvict 部分源码解读 还好之前有在内部文库写过文章,直接抄就完事了, 可以先看完这篇,然后再继续往下

继续

通过源码,我们可以知道allEntries = true 其实是通过keys命令先获取所有键值,然后再一起删除,keys命令会导致cpu和内存飙高,且同时因为需要扫描全表导致阻塞请求

然后

这时就会想起使用scan替代keys命令,因为度娘就是这么告诉大家的,可以说99%涉及到keys命令都说使用scan命令替代keys

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第4张图片

误人子弟!

误人子弟!!

误人子弟!!!

正确的姿势应该是, 禁用keys命令,尽可能禁用scan命令, 不能禁用scan命令就对scan操作进行加锁,不然就调整缓存结构来规避使用这2个操作!

因为scan操作同样是扫描表操作,同样会导致cpu飙高,也同样会阻塞请求

scan和keys的区别在于:scan只会扫部分表,通过游标接着往下扫,所以扫的数据比keys少,相对于keys比较不容易照成阻塞,但不代表它不会导致阻塞

但是

有些坑在不踩之前觉得它是真理,踩了才知道它是坑 -- NDSC流氓

起因

有个方法加了@Cacheable 注解,且增删改的地方都会使用@CacheEvict(allEntries = true) 来删除相关缓存

没踩之前,我觉得scan就是解决keys问题的不错方案,所以我直接重写了DefaultRedisCacheWriter的clean方法

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第5张图片

看着没啥问题,欣赏一下改写之后的“佳作”后,就提交合并请求,没想到这却是噩梦的开始

2020-12-11 9点56分的时候后台妹子开始操作相关方法,去看了一下后台操作日志表,结果因为一开始时间属性的格式化只有年月日,导致不知道具体执行时间,但可以知道那几分钟里妹子执行了12次

嗯,12次scan操作直接把 阿里云Redis 32G 4节点 集群给搞崩了

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第6张图片

看了一下集群里总共有1.2kw+的键值对,每次扫描长度1k,我们来做一道简单的计算题

假设它就只有1.2kw键值,每次扫描1k个键值

12000000 / 1000 = 12000 次 scan扫描,但实际肯定不止1.2w次,且增删会导致rehash也同样会增加扫描次数

这是后台一次操作所需要的scan次数,但妹子直接点了12次,平均每分钟4次的频率,就把集群给搞崩了

相当于至少要访问redis 12000 * 12 = 144000 次,导致至少堆积了12个scan请求,直接把redis的cpu搞到100%,同时还严重阻塞了redis请求

导致服务请求redis因为响应慢,接连导致返回结果也跟着慢了,被Hystrix熔断并服务降级了,好几个节点挂掉

但有些节点还在正常运作,说明redis其实还在运作,只是响应比较慢,只要请求够快,scan就无法阻塞它,这~!@#$%^&*()_+=, 真是一言难尽

只是为了删除不到10个key的缓存对象,真是捡了芝麻丢了西瓜

这看起来还不如keys靠谱呢!起码它没让服务因为连锁反应挂掉

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第7张图片

当然,这是程序猿的问题!谁让俺代码写得菜

至此

本次事故也算理清了,不说了,我要去写检讨报告了

【Redis】还在用scan替代keys解决cpu飙高的问题?误人子弟!_第8张图片

 

参考资料:

https://github.com/StackExchange/StackExchange.Redis/issues/1060

https://www.itread01.com/content/1600935124.html

刚好都有提到在生产环境不要使用keys和scan,99%的文章都是scan替代keys的解决方案

 

如果帮到你,请点个赞吧 O(∩_∩)O~

你可能感兴趣的:(Redis,redis)