当前位置: 首页 > 科技观察

记得一次生产事故:30万订单就这样消失了!

时间:2023-03-19 16:04:25 科技观察

背??景大家好,我是童哥。昨晚下班回到家,在地铁上,老板突然打来电话。B系统生产环境响应慢,影响了A系统的使用,上万个订单无法接收,约30万个订单卡住。请帮助定位自己。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没什么问题。但是经过多次压力测试,时间一直没有上调。我很无奈。此时,已经是半夜三点多了。领导说话,把华为云的人都叫了起来。华为云排查结束后,我们召集了华为云相关人员一起排查问题。他们当然不情愿,但谁让我们出钱呢^^华为云负责人招了redis专家,帮我们看看redis的指标。最后发现redis的带宽满了,于是触发了限流机制。他们临时把redis的带宽提高了三倍,让我们再做一波压力测试。拿着一块草后,平均响应时间下降到200~300毫秒!!!!真的是抱着一根草,有点坑爹。可以限流,带宽满了不报警。..这真是一件令人头疼的事。至此,我们以为问题就这样解决了,领导睡觉了~~现在问题原因找到了,我们就去生产压制吧~我们请了华为云专家加带宽生产三倍大小。从生产提交拉一个hotfix分支,关闭签名,重启服务,再过一波压测。完了,生产环境更差,平均响应时间5-6秒。我们在测试环境更改了连接池的配置,生产环境还是jedis。没有实际效果,还是5~6秒。多么痛苦啊。查看监控查看华为云中redis的监控。这次带宽和流控正常。这次异常换到了CPU,压测的时候redis的CPU压力直接飙升到100%,导致应用响应变慢。再次叫醒华为云redis专家已经是凌晨四点了,大家也没什么想法。华为云的redis高手,又来唤醒我了!再次唤醒华为云redis专家,帮我们分析后台,发现10分钟内进行了14万次扫描~~邪恶扫描问研发人员扫描用在了哪里(他们之前改过,不知道),发现每次请求都会调用scan获取某个前缀开头的key,每次扫描都是1000条数据,查看rediskey总数,大概有11万个,也就是说一个请求需要扫描100个次,1000个并发,大约10万次扫描,我们知道redis中的scan和keys需要进行全表扫描,非常消耗CPU,14万次扫描直接让CPU飞上天。为什么测试环境的CPU上不去天?对比测试环境和生产环境的rediskey总数,测试环境只有900个key,而且每次请求只扫描一次或者keys*一次,不存在羊毛问题。为什么生产环境会有这么多key?请问研发人员,为什么生产环境有那么多key,是不是没有设置过期时间?研发人员说另一个同事写的代码就是另一个同事写的代码。打开代码,果然是一段神奇的代码。具体代码我不方便贴出来。有条件判断是否设置过期时间。经分析,大部分情况是没有设置过期时间成功。当前解决方案此时,已经是凌晨4:30了。虽然大家还是很兴奋,但是经过领导的决定,我们暂时先不动了,因为目前A系统已经暂停了对B系统的调用,所以这个时候B系统可以说流量是几乎为0,我们将在白天分两个阶段修复此问题。第一步是清理生产环境的redis数据,只保留一小部分必要的数据。第二步,修改扫描的某个前缀开头的数据,改为hash存储,可以缩小扫描范围。好了,本次生产事故调查到此结束,后续,桐哥,会继续跟进的。综上所述,此次生产事件与以往略有不同。总结一下:以前是应用服务本身的CPU、内存、磁盘、JVM问题。第一次遇到redis的带宽和限流问题;加入华为云后,很多东西还没有精通,包括监控指标,还需要慢慢摸索;redis必须禁用键和扫描命令,大多数键都应该有一个过期时间!嗯,这件事大概就写到这里了,以后如果有新的情况,童哥会继续跟进。当然最好不要有新情况^^关注。转载本文请联系童哥阅读源码公众号。