图片来自Pexels。8:30左右到家,马上在网上入会了。。。等我重新开始入会的时候,已经有同事帮忙定位了。俗话说,重启可以解决80%的问题。如果重启还不能解决,那肯定是重启的次数不够了。呸,不行,重启也解决不了,所以我们真的需要定位一下。事实证明,重启后跑一波压力测试还是没用的。1000并发,平均响应时间3到4秒,连续压力几次结果都一样。升级配置重启貌似无效,进入第二阶段-升级配置,4核8G的2个实例升级为8核16G的6个实例,数据库的配置也翻倍。能用钱解决的问题,我们一般不会投入太多的人力!事实证明,加配置是没有用的。1000并发,压测平均响应时间还是3~4秒。有意思,这个时候,我介入了。查看监控上线后,我查看了监控。实例的CPU、内存、磁盘、网络IO和JVM堆内存使用似乎都没有问题。这真是令人头疼。我们把局部压力测试分为两组,一组准备局部压力测试,另一组继续分析。经过本地压力测试,我们发现本地环境,单机,1000并发,正常,没有毛病,平均响应基本维持在几百毫秒。看来服务本身确实没有问题。真的没有办法遍历代码。拿出代码,一群老头一起看代码,研发同学给我们讲解业务逻辑。当然,他被所有的老板都骂死了,写的什么破代码。其实在我介入之前,他们已经改了一波代码了。在一个地方,redis命令scan被更改为keys*。这里有个坑,不过现在不是主要问题,后面再说。一路看代码,发现redis的操作很多,有一个for循环调用redis的get命令。其他都是常规的数据库操作,都是有索引的。所以,初步排查,应该是数据库没有问题。主要问题可能还是集中在redis,调用过于频繁。查看日志代码,除了scan改成了keys*(这个我还不知道),基本没问题,所以我们来添加日志吧,分小段添加日志,OK,重启服务,开始压力测试浪潮。当然结果没有变化,分析日志。通过日志,我们发现在调用redis时,有时很快,有时又很慢。好像是连接池不够用,就是一批请求先走,一批请求在等待空闲的redis连接。修改redis连接数查看redis配置。采用单机模式,1G内存,默认连接数8个。客户端还是比较老的jedis,果断改成springboot默认的lettuce。首先,调整连接数为50,重启服务,按下wave。平均响应时间从3~4秒下降到2~3秒,并不明显。继续增加连接数,因为我们有1000并发,每个请求都有很多redis操作,所以肯定会有等待。这次我们直接把连接数增加到1000,重启服务,压制。事实证明,没有明显的改善。再次检查日志。在这一点上,没有很好的解决办法。我们回到日志查看redis相关操作的时间。我们发现99%的get操作返回的速度非常快,基本在0~5毫秒内返回,但总有少数达到800~900毫秒后返回。我们以为redis没什么问题。但是经过多次压力测试,时间一直没有上调。很无奈,这个时候,已经是半夜三点多了,领导说话把XX云里的人叫了起来。云排查结束,我们召集XX云相关人员一起排查问题。他们当然不愿意,但谁要我们出钱呢!XX云负责人请来了redis高手帮我们查看了redis的各项指标,最后发现是redis的带宽满了,于是触发了限流机制。他们临时把redis的带宽提高了三倍,让我们再做一波压力测试。如果你拿着草,平均响应时间下降到200~300毫秒!!!!真是个草,有点坑。可以限流,带宽满了不报警。......这真是让人头疼。至此,我们以为这样问题就解决了,领导睡了!既然在生产中找到了问题的原因,那我们就去生产中压制一下吧!我们找了XX云的专家,把生产的带宽提高了三倍。从生产提交拉一个hotfix分支,关闭签名,重启服务,再过一波压测。完了,生产环境更差,平均响应时间5-6秒。在测试环境中,我们更改了连接池的配置。生产环境还是jedis。没什么实际作用,还是5~6秒。多么痛苦啊。查看监控,查看XX云中redis的监控。这次带宽和流控正常。这次异常变成了CPU,压测的时候redis的CPU压力直接飙升到100%,导致应用响应变慢。再次叫醒XX云的redis高手,凌晨四点多了,大家都没有思路了,XX云的redis高手,请再醒来!再次叫醒XX云的redis高手,帮我们分析后台,发现10分钟之内扫描了14万次。。。邪恶扫描问研发人员扫描用在了哪里(他们之前改过,我不tknow),发现每次请求都会调用scan获取某个前缀开头的key。每次扫描1000条数据,查看redis的key总数,大概有11万条。也就是说一个请求需要扫描100次,1000个并发扫描大概就是10万次扫描。我们知道redis中的scan和keys*需要扫描整张表,对CPU的消耗很大。14万次扫描操作,直接让CPU上天了。为什么测试环境的CPU上不去天?对比测试环境和生产环境的rediskey总数,测试环境只有900个key,而且每次请求只扫描一次或者keys*一次,没有发问题。为什么生产环境会有这么多key?请问研发人员,为什么生产环境有那么多key,是不是没有设置过期时间?研发人员说另一个同事写的代码就是另一个同事写的代码。打开代码,果然是一段神奇的代码。具体代码我不方便贴出来。有条件判断是否设置过期时间。经分析,大部分情况是没有设置过期时间成功。目前的解决方案此时,已经是凌晨4点30分了。虽然大家还是很兴奋,但是经过领导的决定,我们暂时先不行动了。因为目前A系统已经停止调用B系统,所以可以说此时B系统的流量几乎为0。白天我们会分两个阶段修复这个问题:第一步是清理生产环境的redis数据,只保留一小部分必要的数据。第二步,将scan中aprefix开头的数据修改为hash存储,可以缩小扫描范围。好了,本次生产事故调查到此结束。综上所述,本次制作活动与以往略有不同。粗略总结一下:以前是应用服务本身的CPU、内存、磁盘、JVM问题。第一次遇到redis的带宽和限流问题。上了XX云之后,很多东西还没有精通,包括监控指标,还是需要慢慢摸索的。Redis必须禁用键和扫描命令,并且大多数键都应该有一个过期时间!好了,这个事件大概就写这么多了,以后有新情况我会继续跟进的。当然,最好不要有新的情况!作者:桐哥编辑:陶家龙来源:转载自公众号桐哥阅读源码
