背景

前几天发布一个应用过程中,刚发完一批次,突然出现了一些 RPC 接口超时报错的报警。通过 traceId 看到调用链路上有个查询接口超时,再去看数据层的慢 SQL 日志,有个 SELECT ... FOR UPDATE 的 SQL 耗时较长。再看数据库监控,RT 不稳定猫池,大量毛刺,连接数上涨,大量的 SELECT ... FOR UPDATE 语句,拿到 SQL 中的 traceId 和报错的 traceId,确定是查询接口,并且这个接口的访问量再开始发布时间点飙升,并且一直持续降不下去。

排查过程

首先检查了所有发布分支,确定没有与报错的接口相关变更,并且报错都集中再一个单一的分库,所以简单的判断是因为用户访问量大的原因吗?带着这个疑问和同事一起找到了调用 SELECT ... FOR UPDATE 的代码,这块是陈年老代码了,现在线上只有少量的访问,并且可以做降级处理,所以紧急发布了一个可以通过黑名单降级处理的分支。线上发布一批以后,毛刺减少,由于当时已经过了凌晨,所以也有可能是用户已经不再访问了,继续观察也没有意义,就决定第二天早上再继续观察,如果出问题,就执行紧急预案。

第二天,虽然监控看到 RT 的毛刺没有前一天晚上那么明显,但是也还是存在,而且有团队同事观察到其他分库也会有这样的问题,所以就比较奇怪了,并且再一次确认了,与后端发布无关。此时,值班的同事遇到另一个问题,找前端团队的同学一起排查,这才发现是前端代码问题导致会重复发送大量的请求,这就是后端接口监控中看到的访问量飙升。前端回滚后,恢复正常。

因果关系复盘

对于这个问题,最终找到的原因,其实是一个特别简单的原因,就是前端代码有 bug,并且未针对接口做限流。更深层的原因其实是没有确定到什么是因,什么是果。看到接口访问量飙升的同时,也看到了失败量的增加,并且只有单库出现这样的问题,那么从后端的角度来看,可能怀疑是新发布的代码问题或者是数据库的问题导致了用户不断的充实,从而接口层面体现为接口调用量的上升,这是可能出现的。从前端来看,前端的访问量的上升,必然就会导致后端数据库的连接占用量较大,如果连接数不够用,那么就会出现 RT 的毛刺以及接口的超时调用。所以对于现象比较多而杂的这种问题,首先应该确定什么是因,什么是果。

如果快速确定因果呢?对于有发布的情况,最有效的手段是理清所有的发布分支,并且判断是否是发布分支引入的问题。如果没有任何发布,那就需要确定是否是真实的流量上升引起的。就如上述的这个例子,查明问题原因以后,从后端的角度来看,其实就是流量飙升引起的,但是从前端的角度来看,用户访问量并没有多大的变化,但是前端的 bug 导致一次页面加载对某个查询接口请求上几十上百次。如果有前端后端的监控对比,或许当时问题定位会快很多。

另外一种常见的不需要快速定位原因的分析手段就是初中物理实验中学过的【控制变量法】,通过有效的控制影响输出结果的变量,来确定问题的原因是由哪些输入引起的。这种方式对于疑难问题的排查是特别有效的。

总结

如何有效的排查问题,一直都不是简单的事情,但是只要通过有效的手段确定现象的因果关系,问题的排查效率就会高很多。而对于线上问题,最需要的就是监控了。多个维度的监控对比,历史监控数据的对比,都能有效的帮助排查问题。