故障排除:永不过时的技能
从工作至今,我始终认为故障排除是一项永不过时的技能。而且这个技能需要时间积累,需要经验沉淀,需要不断学习。
所谓的故障排查,也就是查找系统中不希望出现的行为的原因,并对其进行修复。记得刚步入工作时,遇到故障问题,首先就是挠头,然后开始谷歌错误信息。针对搜索的结果一行行的进行过滤筛选,最终思考测试找到问题所在。我相信这也是大多数新手工程师的通病,遇到问题首先就是谷歌,然后根据搜索结果进行排查。但是这样排查的效率很低,而且很容易陷入死胡同。而现如今AI如此发达的今天,或许过滤筛选这一步的省略掉了,很多同学都是直接将错误扔给AI,然后AI给于答案和解决方案,尝试,错误,再尝试,直到成功。不能说哪种方法好,哪种方法不好,但是我认为故障排查这个技能,还是需要我们自己一步一步的积累经验,才能有质的飞跃。每一次排查故障,都是一次成长,都是一次经验的积累。借助于AI很容易让我们陷入舒适区,而舒适区往往是我们进步的绊脚石,会让我们一步步丧失思考和假设的能力。思考和测试假设是故障排查的时候的重要步骤,可以帮助我们以缩小搜索空间。但是即使常常提示自己,我也经常发现自己犯以前犯过的错误🤣。
切勿着急
在排查故障的时候,很容易急于求成,想要尽快解决问题。但是这样往往会让自己陷入死胡同,而忽略了问题的本质,甚至一些很明显的错误,在高压或者紧张的状态下,都会忽略掉。因此,我很讨厌在解决问题的时候出现着急的情绪,这会影响自己的判断和思维。因此,对于我来说有时更有效的方法是慢慢地、深思熟虑地、沉思地处理故障,即使在匆忙的情况下也是如此。
理解系统
在写代码的时候,对我来说,思路比写代码更重要。清晰的思路和合理的设计往往能让自己事半功倍,不仅是后续的维护,还是排查故障,亦或是功能迭代,都能减少很多不必要的麻烦。而排查故障的时候,思路同如此。而清晰的思路来源于对系统、对代码的理解,花费大量时间试图"解决"问题是很容易的。但这是容易的部分。困难的部分是理解系统,然后分离和理解问题。
输入、转换【或者叫程序】、输出。这是从学习编程开始就接触到的,但是随着时间的推移,某些时候越来越不关注这个本质,而是一头扎进业务代码中。而系统的故障,大多数也来自于输入对应的输出不符合预期。因此在问题排查的中,首先要明确系统的输入、输出和转换是什么?流经系统的不同类型的部分能否被分组成不同的子系统?
好在在规范的系统中,都会对数据进行追踪打印成日志,因此,在排查故障的时候,可以先从日志入手,分析日志,找出流经的输入、输出的数据,进而判断是在哪一步导致了数据不符合预期,缩小排查的边界。
举个例子,在处理一个订单状态异常的问题时:
输入:
- 用户提交订单的请求数据
- 支付系统的回调数据
- 物流系统的状态更新
转换过程:
- 订单创建逻辑
- 支付状态处理
- 库存扣减
- 物流状态更新
输出:
- 订单状态变更
- 用户通知
- 库存更新结果
通过分析每个环节的日志,我们可以清晰地看到数据在各个子系统间的流转,快速定位是哪个环节出现了问题。比如,如果发现支付系统的回调数据正常,但订单状态未更新,那么问题很可能出在支付状态处理的转换逻辑中。
另外补充一点,永远不要自我认为自己了解了该系统。即使是自己建立的系统。甚至看似简单的系统也极其复杂。而这往往也是我们排查故障的障碍。
观察症状
对于开发环境的故障,我们往往可以通过一些方式重现,然后进行排查。但是生产环境出现的某些问题不是偶发的,仅仅通过梳理数据的流向不容易重现(比如数据a,在8点为10【正确应该是20】,此时导致了系统故障,但是9点数据a为20,系统正常,而又没有相应的数据追踪,此时排查就无从下手了),只能通过观察症状来排查。
这里应该发生什么,实际发生了什么,两者有什么不同?如果可能,可以根据观察到的症状缩小受影响的子系统范围。
举个例子:
接口响应变慢
- 预期行为:接口响应时间在200ms以内
- 实际情况:响应时间超过1s
- 观察方向:
- 查看系统负载是否异常
- 检查数据库慢查询日志
- 分析接口调用链路的耗时分布
用户反馈数据不一致
- 预期行为:订单状态显示"已完成"
- 实际情况:用户端显示"已完成",但后台显示"进行中"
- 观察方向:
- 检查缓存与数据库的同步状态
- 查看状态流转相关的日志
- 分析是否存在并发更新问题
通过这样的对比分析,我们可以更有针对性地进行故障排查,而不是漫无目的地查找问题。
假设问题
当对于系统的问题症状有一定的了解后,接下来便需要对这个问题形成一个假设,这可能是来自现象的直觉第一印象(就好像经验老道的医师,一眼就能看出病症所在),也可能是来自长期观察的最佳猜测。
首先从自身出发,思考问题可能出在哪个区域,是否是自身的问题,是否是之前遇到过的问题,是否是之前解决过的问题。
如果没有简单的方法来猜测问题出在系统的哪个区域,那么就像二分搜索一样,先缩小范围,再逐步缩小范围。
设计一个最简单的方法来证明自己的假设。一般来说,就是提取出问题所在的边界,然后制定一个最简单的测试。
换句话说,就是类似分治法一样,先缩小范围,从小范围一步一步进行验证。这样有几个好处:
- 缩小问题范围,减少排查时间
- 减少排查成本,避免不必要的资源浪费
- 减少排查难度,避免陷入死胡同
- 可以保护系统的其他部分不受错误行为的影响
在假设问题的过程中,可以考虑切断与三方系统的交互,或者进行mock,假设依赖的三方系统是正常的,尽量先从自身找出问题,不去麻烦他人。 如果最终确定问题出现在三方系统,那么再和依赖的系统的开发人员进行沟通,寻求帮助。(有可能别人也在忙,或者一时半会解决不了,这些都是需要 考虑的因素,及时做好反馈和备用方案)
找准平衡点
在排查故障的时候,需要平衡成本。许多时候既承担了许多开发工作,还有其他一些文档、沟通、协调等工作。如果此时恰好有遇到了一个棘手的问题,那么此时就需要找准平衡点。
应该投入多少精力来尝试解决问题,又应该投入多少精力来获取有关问题的信息?
如果我的直觉没错,直接解决问题要快得多。但如果不是这样,从长远来看,系统地收集信息会更有效率。
我们在进行故障排除时,会预先了解我们所面临的问题的困难程度和紧急程度,这可以告知我们何时尝试介入并解决问题,以及何时尝试收集更多有关问题的信息。
了解风险
解决问题之前,了解问题导致的风险是必要的。就好比医院治病也并不是包治百病,不同的人的体质、病情、条件等不一样,承担的风险就不一样。 解决系统问题是同理,在开始着手解决问题前,可以先思考一下。
能发生的最坏的事情是什么?如果我搞砸了会有什么风险?我搞砸的可能性有多大?
尝试修复和不尝试修复的相对风险是什么?
这个问题影响的用户是哪些?
我怎样才能降低这种干预的下行风险?
信息过滤
问题排查的时候,很多时候取决于系统和问题的细节。当遇到不熟悉的领域,可以从官方文档入手,了解系统,了解问题。而如果身边有相关领域的大拿, 也可以向他们请教(请教的时候最好带上一杯咖啡亦或完事后请对方吃一顿饭)。
回到文章开始,搜索引擎和AI现在是必不可缺的问题排查工具。但是如何使用搜索引擎和AI,也是需要技巧的。 具体可以下来仔细想想。
其他
在排查故障的时候,经常会进行一些更改。但我发现有时候,还没确定更改是否生效就进行了下一步,这样第一步就错了,还会扰乱排查的思路,改着改着发现问题越来越复杂,越来越乱。明明是这样,为什么是那样,开始怀疑自己。因此,如果在更改代码亦或配置还是其他的什么,更改后的第一步是确保更改有效,再进行下一步。
例如,当排除CSS bug时,通常可以设置* {color: red !important;},在更改了配置后,可以先查看是否生效。不管是通过控制台输出,还是日志收集还是其他方式。选择自己最顺手的方式,确保更改有效。
另外检索、debug等工具,也是排查故障的利器。在真正排查线上问题的时候,仍然有许多细节和技巧需要学习。上面的内容也只是泛泛而谈,想在工作中遇到问题时,能有所帮助。还是需要多积累经验,多思考,多总结。 特别是初入职场,遇到问题,不要着急,不要慌,不要急着解决问题,先思考,再动手。如果身边有经验丰富的同事,那就更好了,可以向他们请教。
有些路和坑唯有自己走过,才能深刻体会。搜索引擎也好、AI也好,都是工具,都是辅助。而真正排查故障的时候,还是需要自己一步一步的思考。