记一次生产事故:30万单就这样没了!

背景

你好,我是彤哥。redis

昨天晚上下班回家,在地铁上,老大忽然打来电话,B系统生产环境响应缓慢,影响了A系统的使用,几万小哥收不了单,大概有30万单卡住了,你去帮忙定位一下。spring

我8点半左右到家,立立刻线入会。数据库

重启

我入会的时候,已经有同事在帮忙定位了,俗话说的好,重启能解决80%的问题,若是重启解决不了,那确定是重启的次数还不够,呸,不对,重启解决不了,就真的要去定位了。springboot

事实证实,重启后走一波压测依然没什么用,1000个并发,平均响应时间在3~4秒,连续压了几回都是这样的结果。网络

升级配置

重启看来是无效了,进入第二个阶段——升级配置,2台4核8G的实例升级到6台8核16G,数据库的配置也翻了一倍,能用钱解决的问题,咱们通常不会投入太多的人力^^并发

事实证实,加配置也没什么卵用,1000个并发,压测的平均响应时间仍是在3~4秒。ide

有点意思了。测试

此时,彤哥我介入了。日志

查看监控

我上线以后,查看了一下监控,实例的CPU、内存、磁盘、网络IO、JVM堆内存使用状况好像都没啥问题,这真是个头疼的问题。code

本地压测

咱们分红两波同窗,一波去准备本地压测,一波继续分析,通过本地压测,咱们发现,本地环境,单机,1000个并发,妥妥的,毛问题都没有,平均响应基本维持在几百毫秒。

看来,确实跟服务自己没有问题。

代码走查

实在没有办法了,拿出代码,一群大老爷们一块儿看代码,研发同窗给咱们讲解业务逻辑,固然,他已经被各位大佬给骂死了,写的什么破代码,其实,在彤哥介入以前,他们已经改过一波代码了,有个地方把redis命令scan改为了keys *,这里埋了个坑,可是,如今不是主要问题,后面咱们会说。

代码一路走读下来,发现有不少的redis操做,还有个for循环里面在调redis的get命令,其它的都是常规的数据库操做,并且都加了索引的,因此,初步排查,数据库这里应该是没有什么问题,主要问题可能仍是集中在redis这块,调用太频繁了。

加日志

代码走查下来,除了那个scan改为了keys *(这个我还不知道),基本上没有什么问题,加日志吧, 一小段一小段的加上日志,OK,重启服务,压测来一波。

固然了,结果没有什么变化,分析日志。

经过日志,咱们发现,调用redis的时候时而很快,时而很慢,看起来像是链接池不够的样子,也就是一批请求先行,一批请求在等待空闲的redis链接。

修改redis链接数

查看redis配置,用的是单机模式,1G内存, 链接数默认的8,客户端仍是比较老的jedis,果断改为springboot默认的lettuce,链接数先调整为50,重启服务,压一波。

平均响应时间从3~4秒降到了2~3秒,并不明显,继续加大链接数,由于咱们是1000个并发,每一个请求都有不少次redis操做,因此,确定会有等待,此次咱们把链接数直接干到了1000,重启服务,压一波。

事实证实,并无明显地提高。

再次查看日志

此时,已经没有什么好的解决办法了,咱们再次回到日志中,查看redis相关操做的时间,发现99%的get操做都是很快返回的,基本上是在0~5毫秒之间,可是,总有那么几个达到了800~900毫秒才返回。

咱们觉得redis这块没什么问题了。

可是,压测了好几回,时间一直提不上去。

很无奈了,此时,已经半夜3点多了,领导发话,把华为云的人喊起来。

华为云排查

最后,咱们把华为云相关的人员喊起来一块儿排查问题,固然,他们是不情愿的,可是,谁让咱们给钱了呢^^

华为云的负责人,把redis的专家搞起来,帮咱们看了下redis的指标,最后,发现是redis的带宽满了,而后触发了限流机制。

他们临时把redis的带宽增大三倍,让咱们再压测一波。

握了颗草,平均响应时间一会儿降到了200~300毫秒!!!!

真的是握了颗草了,这就有点坑了,你限流就算了,带宽满了也不报警一下的么。。

这真是个蛋疼的问题。

到这里,咱们觉得问题就这样解决了,领导们也去睡觉了~~

上生产

既然问题缘由找到了,那就上生产压一波吧~

咱们让华为云的专家把生产的带宽也增大了三倍大小。

从生产提交拉一个hotfix分支,关闭签名,重启服务,压测走一波。

完蛋,生产环境更差,平均响应时间在5~6秒。

测试环境咱们是改了链接池配置的,生产环境仍是jedis,改之,走一波。

并无什么实际做用,仍是5~6秒。

真是个蛋疼的问题。

查看监控

查看华为云中redis的监控,此次带宽、流控都是正常的。

此次不正常的变成了CPU,redis的CPU压测的时候直接飙到了100%,导到应用响应缓慢。

再次唤醒华为云redis专家

已经凌晨四点多了,你们已经没什么思路了,华为云的redis专家,你给我再起来!

再次唤醒华为云的redis专家,帮咱们分析了下后台,发现10分钟内进行了14万次scan~~

关注公主号彤哥读源码,查看更多干货好文!

万恶的scan

询问研发人员哪里用到了scan(前面他们改的,我不知道),发现,每次请求都会调用scan去拿某个前缀开头的key,每次扫描1000条数据,查看redis键总数,大概有11万条,也就是说,一个请求就要scan100次,1000并发,大概就是10几万次scan,咱们知道,redis中scankeys *是要进行全表扫描的,很是消耗CPU,14万次scan操做,直接让CPU上天了。

为何测试环境CPU没有上天呢?

对比了下,测试环境和生产环境redis的键总数,测试环境只有900个key,每次请求也就scan一次或者keys *一次,毛线问题都没有。

为何生产环境有这么多key?

询问研发人员,为何生产环境有这么多key,没有设置过时时间吗?

研发人员说设置了的,是另外一个同事写的代码,打开代码,真是一段魔性的代码,具体代码我就不方便贴出来了,里面有根据条件判断要不要设置过时时间,通过分析,大部分状况下,都没有设置过时时间成功。

当前解决办法

此时,已经凌晨4点半了,虽然你们还很兴奋,可是,通过领导决策,暂时先不动了,由于,目前A系统已经暂停调用B系统了,因此,此时B系统能够说流量几乎为0了,咱们白天再分两个阶段来修复这个问题。

第一步,先清理掉生产环境redis的数据,只保留一小部分必要的数据。

第二步,修改scan某前缀开头的数据,改为hash存储,这样能够减小扫描的范围。

好了,本次生产事故排查就到这里了,后续,彤哥,也会继续跟进的。

总结

本次生产事件跟以往遇到的事件都略有不一样,大概总结一下:

  1. 以往都是应用服务自己的CPU、内存、磁盘、JVM这些问题,redis的带宽和限流仍是第一次碰见;
  2. 上了华为云之后,不少东西尚未弄得熟练,包括监控指标这些,还须要慢慢摸索;
  3. redis必定要禁用掉keys和scan命令,且大部分key应该设置过时时间!

好了,本次事件大概就写这么多,后续有新的状况彤哥也会继续跟进的,固然,最好不要有新的状况^^

相关文章
相关标签/搜索