On Troubleshooting
2025-3-5
| 2025-3-9
Words 12058Read Time 31 min
type
status
date
slug
summary
tags
category
icon
password
我所做的许多事情,涉及多个领域,其实都可以归结为一个技能:排查问题(故障排除)
我将排查问题定义为:系统性地找出系统中不良行为的原因,并加以修复
排查问题这一技能通常是在明确学习某项具体技能的过程中隐性习得的,很少有人将排查问题单独拿出来作为一项独立技能去讨论。然而,有效排查问题的方法其实有许多共同点,且与具体领域无关。
我意识到,自己花在排查问题上的时间远比实际去构建或执行任务的时间要多,而排查问题本身的技能是可以独立于具体领域单独提高的。因此,我决定弄清楚该如何提升我自身的排查问题能力,以便在多个领域中提高我的整体效率。
对我而言,排查问题的方法通常可以简化为:抓耳挠腮、上网搜索错误信息、提出假设、测试这些假设,以缩小问题范围。但我经常会发现自己重复犯之前犯过的错误。因此,我特意列出了一些我在排查问题时会提醒自己的要点,以帮助自己保持正确方向,避免陷入困境。

Step 1: step back

排查问题需要一种特定的思维方式。据我所知,这种思维方式包括对系统底层结构的兴趣(用皮尔西格的话说,就是一种“经典式”的思考方式),还包括耐心、对细节的关注,以及坚持不懈的韧性。
有时,即便情况紧急,以一种缓慢、深思熟虑甚至冥想式的方法进行排查,反而更有效。
很容易陷入“头痛医头,脚痛医脚”式的被动循环,而忽略了停下来思考一下:问题真正的根源是什么?究竟发生了什么?
The troubleshooter is part of the system. Hence, probe effects and the legendary heisenbug.排查问题的人本身也是系统的一部分。因此,就会产生“探测效应”(probe effect),以及著名的“海森虫”(heisenbug,即由于观察或排查行为而改变甚至隐藏自身特性的缺陷)。
The troubleshooter is part of the system. Hence, probe effects and the legendary heisenbug.排查问题的人本身也是系统的一部分。因此,就会产生“探测效应”(probe effect),以及著名的“海森虫”(heisenbug,即由于观察或排查行为而改变甚至隐藏自身特性的缺陷)。

确保你调的是正确的弦

任何弹过一阵吉他的人,一定都体验过这种令人抓狂的感觉:你拧动的旋钮并不是你正在弹奏的那根弦的旋钮。难怪无论怎么调都不起作用!
因此,当我试图修复一个系统之前,我通常会先做一些肯定能立即看到效果的操作。
如果我觉得我知道该剪哪根线,我首先会轻轻拉一下这根线,确保线的另一端真的会动。
当我在排查一个CSS问题时,我经常会先设置一个简单粗暴的样式:
这样做能立刻让我知道,我写的代码是不是放在了正确的文件里,而且有没有真的被执行!

Determine the flows

notion image
人们很容易花大量时间去尝试「修复」问题,但这其实是相对容易的部分。真正困难的部分在于理解整个系统,然后准确地定位并理解具体的问题所在。 我通常从「东西」在系统中的流动开始入手,这里的「东西」可以是电、水、汽油、空气、力、数据、污水或任何其他东西,观察这些东西是如何在系统中流动,并在过程中转化成其他形式的。 明确系统的输入、输出以及转化过程至关重要。是否能将系统内流动的不同类型的物质划分成几个相对独立的子系统呢? 在电气系统中,沿着线路进行实地追踪通常非常有效;在机械系统中,也同样适用于输送液体或气体的管道、控制线缆、凸轮、齿轮、链条以及其他传递机械力的部件;在软件系统中,则要追踪数据流动的路径;而在社会动态的系统中——那只能祝你好运了!

Observe the symptoms 观察症状

先弄清楚系统理应出现什么现象、实际上发生了什么现象,以及二者是从哪里开始出现偏差的。
如果可能的话,根据症状尽快确定究竟是哪一个(或哪几个)子系统出了问题。比如,我的汽车刹车灯不亮,很可能是电气系统的故障;如果车底出现了一摊油迹,那大概率就不是电路的问题;但如果发动机无法启动,这可能涉及多种原因,就需要进一步调查了。
然而,有时候自认为很了解系统,反而会成为排查问题时的障碍。即便我自信「对系统了如指掌」,实际上我也不可能完全掌握它。哪怕是我亲手搭建的系统,其中也包含着许多并非由我亲自创建的部分;甚至看似简单的系统,本质上也可能复杂到难以置信。(正如卡尔·萨根所说:“如果你想从头开始做个苹果派,那你首先得创造整个宇宙才行。”)

Isolate the problem 定位问题

下一步就是要找出故障子系统具体在哪个环节失效了。
notion image
我的基本方法是:对系统进行一次“科学探究”。
  1. 首先,提出一个关于问题所在的假设。这可能是根据初步症状做出的直观判断,也可能是经过长时间观察后的最佳猜测。
      • 先排查最容易检查、最可能发生故障的地方。这些通常是系统中设计用于维护的、过去曾经出过问题的、或者机械负担较重的部件。优秀的系统设计通常会把容易出问题的部分安排得易于维护,比如:电路中的保险丝和断路器、皮带和链条、滤芯、各种端子和连接件,以及输入/输出设备。
      • 如果没有简单的方法能快速确定问题位于系统的哪个区域,那就执行一次非正式的“二分法”排查。
  1. 找到最简单的方式去检验自己的假设。通常,这意味着在我认为问题出现的环节的上游或下游直接“切割”系统,检查切割点处是否正常,从而判断假设是否成立。
💡
如果我是那些“酷炫的高手”,可能会在这里讲一讲贝叶斯定理。但我不是,而且我认识的那些排障高手也没人会在给火花塞做故障排查时用到贝叶斯定理。

Disconnect the subsystem 隔离子系统

如果条件允许,我会将正在调试的子系统从整体系统中完全断开。
这么做有三个好处:
  1. 避免子系统与系统其他部分产生奇怪的交互,简化诊断过程。
    1. (当我让子系统独立工作正常后,再把它重新接回系统,看看是否依旧能正常运转。)
  1. 防止我的错误操作对整个系统造成更大损害。
  1. 通常能大大缩短反馈循环,更快地找到问题根源。

Or not 如果无法(或不想)完全将子系统分离,还有另一种方法:

可以在不同位置进行探测,或者“切断并探测”。
如果我了解或能直觉判断出系统正常运转时,测试点上某个参数的合理范围,那么实际测得的数值就能帮助我定位问题出现的具体位置。

Find good cut points

notion image
在不影响整体功能的情况下,我能在系统中设置多少个“切割点”进行测试?
火花塞就是一个典型的子系统之间的切割点。
例如,如果发动机无法启动,但我能够成功看到火花,那么问题很可能就不在电路子系统中。
除了系统内部的切割点外,我也始终会尝试在不同子系统之间的接口处进行测试。
如果问题并不在电路系统内部,而在系统接口处就能确认其功能正常,就能避免浪费大量时间,防止我在实际上根本没有故障的地方白费功夫。

在“获取信息”与“尝试修复”之间保持平衡

到底该花多少精力去尝试修复问题,又该花多少精力去获取更多的信息?
在排查问题时,我们总要在两种努力之间取得平衡:一边是尝试快速修复问题,另一边是继续收集信息以理解问题本质。
我们通常凭直觉(或经验)判断问题的难度,来决定究竟是立即着手解决,还是继续调查和获取更多信息。
但问题在于,我们的这种关于难度的直觉(“难度先验”)经常是错误的。因此,我们还需要发展出一种“元先验”(meta-prior),即我们对自身在判断问题难度方面准确性的估计。这种元层次的先验应当结合我们个人的倾向性以及专业领域的知识和经验,帮助我们更准确地判断我们自己的直觉究竟有多可靠。
💡
一位我认识的出色但未受过正规训练的“实操型”排查专家,曾向我描述过他与一名受过正规培训的技师共同修车的经历:
这位技师会从系统的一端开始,按部就班地依照官方的排查清单逐一排查问题。而他自己则更偏向直觉式排查,迅速瞄准故障点并直接修复。
然而,一位接受过专业训练的技工却会从系统的一端开始,按部就班地逐一排查,严格按照官方的故障诊断清单进行核对。
另一个例子是,我认识的一位朋友曾花费数天时间专门搭建了一套日志记录系统,尽管在建立日志系统上耗费了大量额外时间,但这套系统最终使他比其他直接去排查的人更快地定位了问题所在。
这两个例子表明,有时先花精力建立一套信息记录或诊断工具,看似是在浪费时间,但从长期来看,这样的方式反而可能更高效,更快地找到问题的根本原因。

明确风险

排查问题所涉及的风险可能千差万别:从几乎为零(比如业余的软件项目),到可能改变人生轨迹(例如医疗诊断),再到可能威胁生存本身(例如通用人工智能或核武器)。而风险级别应当决定我们处理问题的具体方式。
所以我经常会问自己:
  1. 最糟糕的情况会怎样? 如果我搞砸了,会有什么后果?而我搞砸的可能性又有多大?
      • 对排查者本人的危险: 系统本身对操作人员有多大风险?是否存在高压电、易燃材料、有毒化学物质、易坠重物,或者数据丢失风险?
      • 对旁观者的危险: 和上述情况类似,但还要考虑影响的波及范围,旁观者对危险可能缺乏认知或缺少防护装备的情况。
      • 对系统本身的危险: 系统本身有多脆弱?是否容易替换?
      • 对其他人的危险: 如果系统以不可预测的方式运行,会带来怎样的后果?哪些人或哪些系统处于该系统的下游,会受到影响?
  1. 尝试修复与不尝试修复的风险比较如何?
      • 如果系统已经损坏到非常危险的地步,修复它可能反而更安全,即便修复本身存在失败的风险。
  1. 系统归属于谁?
      • 这个系统是我弄坏的吗?我有义务去修复吗?
      • 我是否拿报酬修复这个系统?如果是,那成功、失败或灾难性失败的概率分别是多少?客户是否清楚这些风险?(我已不再给那些没有备份系统数据的人做免费技术支持了。)
  1. 我能怎样降低介入修复操作的风险?
      • 一般而言,我可以更缓慢、更谨慎地进行操作,避免犯错;在测试环境而不是正式环境中操作;提前做好系统备份。
在这个情境下,“危险”至少包含四个要素:时间范围、强度、影响范围和物理性
最显而易见的危险通常是即时的、物理的危险。但从实际的利害关系来看,为大型投资银行开发关键代码,从长期来看可能与物理危险同样严重;只不过这种危险通常是分散的、延迟的,并且是隐形的。
  1. 一个业余软件项目可能存在长期的正面或负面的风险,但在短期内,它几乎没有多少下游用户,也没人真正依赖它,因此无论是在物理上还是其他方面,都不会产生什么重大影响。
  1. 维修汽车的刹车属于中等危险级别的工作,但如果你把事情搞砸了,整条高速公路上的人(以及他们所负责的一切)都会受到威胁。
  1. 如果我的车在黄昏时分抛锚在一个治安混乱的地方,那么临时凑合着重新接上电池,也许比到处寻找旅馆或修理厂要安全得多。
  1. 做出医学诊断对一个人的影响是即时且可能彻底改变生命轨迹的;而开发医疗软件则会对更多人造成一种延迟的、长期的重大影响;至于参与外交政策或核战略的制定,则可能对一部分甚至整个地球上的所有人,造成延迟但关乎生存的巨大影响。

不要过度思考

不要预设问题很复杂。仅仅因为排查过程很复杂,并不意味着问题的根源也一定复杂。
但也别假设问题一定简单。

保持耐心

无需多言。

收集关于系统的信息

从定义上来说,排查问题的人必然会遇到超出自己能力范围的情况。优秀的排障人员必须习惯于身处“未知状态”。
需要收集哪些信息,以及如何获取这些信息,都取决于具体系统和所面临问题的细节。

知道针对哪类信息应该用什么搜索工具

有时候(例如机械维修,或钓不到鱼),我可能需要去认识一个懂行的人,带上一箱啤酒去向他请教。
还有时候,我得知道如何使用资料库或说明手册。(举个例子:我妈妈的斯巴鲁汽车电动车窗失灵了,好几个汽修人员试图修理,但问题依然存在。我也查过保险丝,毫无进展。但最后却是她自己灵机一动,决定仔细看一下说明书,而不是粗略地浏览保险丝章节,结果发现是儿童安全锁开启了。)
在现代世界中,我大部分时候需要的其实是熟练使用搜索引擎。但不同的搜索引擎适合不同的用途,因为Google如今已无法很好地收录一些小众网站的信息。
有时候我可能需要用大语言模型(LLM),因为我可能连如何准确表述问题都不清楚;或者需要到领域特定的论坛搜索,以排除无关内容;也可能需要用YouTube搜索一些难以用语言清楚描述的实际操作技巧;再或者,我直接去系统官方的文档或手册中查找相关信息。

了解如何利用高级搜索条件缩小搜索范围

虽然并不存在专门针对排障的搜索技巧,但如果你对搜索操作符还不熟悉,可以参考以下三篇文章作为入门:

知道如何扩大搜索范围,并过滤无关信息

大部分系统都会输出各种各样的信息,其中相关性差异很大。
在软件的日志中,可能充斥着大量重复而无用的信息,而真正有价值的信息可能只有一句话,比如带有“error”(错误)、“fail”(失败)或者提及具体受影响的子系统。
同理,引擎也会产生大量噪音,但大部分声音都是正常的。关键在于分辨声音发生了怎样的变化。当从总噪音中减去预期的正常噪音后,剩下的声音就是问题本身的“声音指纹”。
甚至连错误信息本身,也只有其中某些部分是有用的。
多数软件错误信息直接完整复制到Google里搜索往往不会找到有价值的信息,因为这些信息通常包含特定于自己设备的内容。因此,我一般会从完整错误信息开始,把明显与自己设备有关的部分去掉。如果还搜索不到有效结果,我就扩大搜索范围:
  • 不再关注软件的具体版本;
  • 不再关注特定硬件信息;
  • 甚至暂时不再关注特定的软件。
随后,我可能会在论坛里找到一些人,他们虽然用着完全不同的笔记本、运行着不同的应用程序,却和我遇到了相同依赖项的问题。或许他们的解决方案不能直接用,但却可以帮我指出问题的根源所在。接下来我就可以搜集更多数据,重复上述过程,逐步靠近真正的答案。

学会“钓鱼”

当某样东西需要维修时,我可以尝试自己动手解决,但可能速度很慢、效率也不高。另一种办法是直接找专家帮我解决,这种方式快捷又轻松。但问题在于,让别人代替我解决问题的话,我几乎无法从中学到任何东西。
当我需要排查某个自己完全不了解领域的故障时,有一种非常有效的方法:
  • 找到一个精通该领域或系统问题的专家;
  • 不要让他们替我解决,而是让他们和我一起解决。
短期来看,这种方式可能会耗费更多时间,双方也未必会感到轻松愉快。但从长期而言,我能够从中学到大量知识,下一次再遇到类似问题时,我就可能自己搞定。
这其实是一个连续的过程,一端是我在旁边观看专家操作,另一端则是专家在旁边指导而我亲自操作。越是让我自己动手,就意味着过程越缓慢、对双方而言越有挫折感——但也越能迫使我真正学会如何排查和修复问题。

从系统中获取信息

notion image
在排查问题时,获取尽可能多的系统信息,尤其是在错误发生的当下,是最理想的情况
我会尽量收集系统中来自不同位置的更多输出,并使其更具体、更详细。
  • 软件系统:增加日志记录(logging),或者在进程运行时附加调试器(debugger)进行实时监控。
  • 电子设备:通常的第一步是使用万用表(multimeter)测量不同点的状态,检查电压、电流、阻抗等。
  • 机械系统:虽然我在机械方面不太擅长,但我注意到,我的哥哥通常会让机器在运转状态下进行观察,打开引擎盖,仔细观察、嗅闻、聆听是否有异常。

获取更多信息 = 更好地理解系统

埃里克·巴克(Eric Barker)在《Plays Well With Others》一书中讨论了类似的概念。我们对人际关系的理解往往比我们以为的要差,但增加互动、获取更多信息是提升判断力的有效方式。例如,在识别谎言时,科学验证的方法之一就是让说谎者提供更多信息,因为谎言最终会自相矛盾。
类似地,很多人能看似权威地谈论复杂话题,但通过两三个精准的问题深入细节,往往就能区分真正的专家与那些只是复述表面观点、试图显得专业的人。

自我排查:信息本身就是修复的关键

在调试我们自身的思维和行为时,获取系统信息的过程本身有时就能部分解决问题。例如,**写日记(journaling)**这一简单的行为,往往能够帮助人更清晰地梳理思路,并自然地找到解决方案。

直觉判断系统的容差范围

在修复问题的过程中,有时候不可避免地会破坏一些东西——无论是无意的,还是在所难免的
不同的系统和材料有着不同的容差(tolerances)。某些部件即使受到一定程度的损坏,系统依然可以正常运行;而另一些部位如果承受相同程度的损坏,则可能直接导致整个系统崩溃。
  • 在物理系统中,需要有机械直觉(mechanical intuition)来施加恰当的力度,并理解哪些部件能够承受一定程度的损坏,哪些部件不能。例如:
    • 轴承孔(bore)或密封件(seal) 通常比外壳(casing)更脆弱,对损坏更敏感。
    • 金属部件 可以经受一定的弯折或刮伤,而电子元件 可能因轻微的短路就彻底失效。
  • 在软件系统中,有的代码即使有Bug,程序依然能运行,但某些关键路径上的Bug可能导致整个应用崩溃。
  • 在社交系统中,一些沟通错误是可以容忍和修复的,但某些错误可能会破坏整个关系。
直觉地理解哪些部分可以承受一定的损伤,而哪些部分不能,是排查问题和修复系统时的重要能力。
💡

经验比完美更重要

最近,我看着(并尝试帮忙)我的哥哥修复了一台 Jabsco 叶轮泵——一台用于 海洋柴油发动机 的盐水冷却泵。
老实说,他并不完全知道自己在做什么。这台泵里充满了各种意想不到的问题。但最终,他还是把它修好了。
如果换做是我,我可能早就放弃了。因为在修复过程中,他 把它弄坏了好几次。但真正重要的不是它被破坏的方式,而是它被修复的方式

修复的过程

这台泵的结构是 过盈配合(press-fit),通常需要 液压机(hydraulic press) 来拆装。但因为没有合适的工具,他用 锤子、钳子和一些钢块 硬生生地把它敲开了。
在这个过程中:
  • 叶轮(impeller)彻底损坏—— 这几乎是不可避免的,因为没有正确的拆卸工具。
  • 轴周围的铸件在拆解时断裂—— 但它的功能不受影响,所以不重要。
  • 轴承密封(bearing seal)装反了,拆卸时只能彻底破坏它;但重建套件里竟然 缺少新的密封件
  • 新轴承的规格并不完全匹配
  • 新的叶轮型号错误,安装时磨出了一堆 铜屑—— 但这是装上后才发现的,而它已经卡死在里面,根本拿不出来了。

为什么他能修好?

他并不知道 这台泵的具体结构,但他 懂得压配件、密封、轴承等机械原理。因此,他能 通过零件的形状推测其功能,并相应地调整修复方法。
结果是:
  • 他敢于施加比我更大的力道(甚至达到破坏的程度!)。
  • 同时,在关键点上,他又异常小心
    • 让我仔细 打磨密封件的安装位,确保表面光滑无毛刺;
    • 认真检查内孔是否有划痕,避免影响密封效果。
尽管在整个修复过程中,几乎 每一步都出了问题,但最终:
泵被拆解开了,然后 又成功装回去了

经验比完美更重要

这次经历让我明白,真正的技术并不是从不犯错,而是知道如何应对错误
有时候,在排查和修复问题时,你不必完全知道自己在做什么,只要你足够了解 基本原理,并能 在关键时刻做出合理的判断,就能修好系统——即使过程中不断出错。

与系统保持良好的关系

我注意到:讨厌电脑的人往往不擅长使用电脑,而讨厌人的人往往无法从别人那里得到自己想要的东西——除非他们能很好地隐藏自己的厌恶。
这听起来可能有点玄乎,但我相信,欣赏一个系统的美感和复杂性,会让人更擅长排查它的问题。如果把系统当作敌人,它就真的会成为你的敌人。

对待系统的态度决定你的排查方式

在我看来,“敌人”“对手” 是不同的:
  • “对手” 是值得尊敬和理解的,想要打败它,就必须先弄清它的运行逻辑。
    • 例如,把系统当作一个竞技对手,去挑战它、战胜它,这并不会影响排查能力。
  • “敌人” 则是你讨厌、无法接受的对象。
    • 许多人在电脑或机器不听使唤时,会陷入**“蠢电脑!坏狗!”** 这种情绪化的思维方式,这正是需要避免的。
如果把系统当作“敌人”,就容易忽视它的细节,只会看到自己想看到的刻板印象,而不是它真正的结构和运行机制。而刻板印象无法被排查和修复,因为它并不对应真实的系统

两个修车师傅的对比

我认识两位当地的修车师傅,他们的风格截然不同:
  1. 第一位:骂骂咧咧,砸东西,有时候还会怒摔工具。
      • 人们叫他 “愤怒的[某某]”
      • 他是本地 最便宜 的修车师傅。
  1. 第二位:讲故事,说他如何用胶带和丝袜修好了丰田车。
      • 不管别人对他本人是什么看法,所有人都认可他的修车技术。
      • 和他聊天,会感觉他热爱汽车,享受修理的过程
      • 他是本地公认最好的修车师傅,同时也是最贵的
这个对比让我明白了一点:对待系统的态度,不仅影响你的排查能力,还决定了你能达到的专业水平。
真正的高手,并不是那些与系统“对抗”的人,而是那些愿意理解、尊重,并与系统协作的人。

善用现有资源

拥有合适的工具来测试、拆解、修理和重新组装系统确实有帮助,但现实中,更重要的是即兴应变的能力
在工具和零件不足的情况下,能否找到替代方案,并基于零件的本质功能,而不是标签或刻板印象来选择替换件,往往决定了修复的成败

缩短反馈循环

要修复一个系统,我必须能够复现问题。
为了获得足够的数据来可靠地复现问题,我通常需要在不同条件下多次运行系统。一旦能够稳定复现问题,我还需要不断调整参数,反复运行,试图找出哪个具体的因素导致了故障。

当系统有延迟或依赖复杂步骤时

有时候,系统本身存在内置延迟或依赖一系列复杂步骤,导致故障难以复现。这种情况下,我会问自己:
“有没有办法缩短反馈循环?”

不同领域的优化方法

在软件调试中
  • 减少硬编码的超时时间,加快调试过程
  • 单独测试子组件,用虚拟输入/输出代替完整系统
  • 本地测试,避免网络请求的延迟
  • 启用热重载(hot-reloading),避免重复启动整个应用
  • 自动化部署流程,减少手动操作的等待时间
在电子设备调试中
  • 直接把万用表用胶带固定到终端,这样我可以一边操作一边观察数值变化,而不必反复调整测试点
在机械维修中
  • 尽可能靠近工具和信息来源,避免来回跑动
  • 将手册或电脑支在维修现场,而不是反复切换设备查找信息

一个简单但有效的例子

当我在排查问题时,经常会犯一个低效的错误:
在小屏幕的手机上查资料,或者在维修现场和参考资料之间来回跑。
一个简单的解决方案
直接把手册或笔记本电脑放在工作区域,这样可以随时查阅,而不用浪费时间走来走去。这个看似微不足道的调整,往往能节省大量时间,提高修复效率。

核心原则:减少等待,提高迭代速度

反馈循环越短,排查和修复的速度就越快。
关键在于减少不必要的等待、移动和重复步骤,让每一次调整都能尽快得到反馈。

减少噪声

在进行干预之前,我会尽量减少系统中的干扰因素和“杂散变量”,以确保测试结果更加准确。

如何减少噪声?

断开子系统
  • 这样可以减少不同子系统之间的交互影响,避免某个错误的根源被其他因素掩盖。
  • 例如,在调试一个电子电路时,可能需要先断开所有外部设备,仅测试核心电路部分。
缩短反馈循环
  • 系统存在时间延迟时,可能会有其他变量在输入和输出之间影响系统状态。
  • 例如,在调试软件时,减少超时等待或后台进程的干扰,可以更快、更精准地观察输入对输出的影响
减少系统“拖泥带水”(slop)
  • 确保测试环境尽量稳定和一致,减少多余的随机变量。
  • 例如,机械系统中的松动零件、软件系统中的日志噪声、电子电路中的信号干扰等,都会增加不必要的复杂性,使问题难以诊断。

把问题写下来

我经常通过以下两种方式,将写作本身作为排查问题的工具:

① 专业级的“小黄鸭调试法”(Rubber Duck Debugging)

很多时候,我会先尝试在论坛上写一篇详细描述问题的求助帖,但最终并不会真正发布出去。
因为为了能清楚地表达问题而不显得愚蠢,我必须更深入、更系统地整理问题相关的背景和细节,而这个过程往往让我发现之前没有注意到的关键信息,进而自己找到了解决方案,而根本无需真正发帖。
换句话说:
强迫自己准确表达问题,通常能让我立刻变得更聪明。

② 维护一份“排障笔记”,留下面包屑线索(breadcrumbs)

在排查一个问题时,特别是跨多个会话、跨天或多次进行的项目,我往往高估了:
  • 我下一次继续排查时,能够记得多少上下文信息;
  • 我多久以后才会再回来继续修复这个问题。
因此,无论当时写下的信息多么显而易见或多么零散不全,**建立一份“排障笔记”**都是至关重要的,因为它为未来的自己留下了一条清晰的线索。
当我下次回来重新面对这个问题时,即使过去了很长时间,也可以快速恢复到上一次排查的情境,从而省下大量重复工作的时间。

不要高估记忆力的可靠性
问题可能比你想象的更难、更复杂,记录的必要性也因此远比想象中重要得多。

通过“投放特殊输入”,观察“黑盒”系统的输出

尽管之前提到了隔离子系统的策略,但有时我们也不得不面对一个无法打开的“黑盒”系统(Black Box)。这时,一个非常有效的方法就是向系统投入一些异常具体且奇怪的输入,然后观察系统会有什么反应。
具体来说,有两种主要的方法:

方法一:逐步剥离法(Isolation by Removal)

  • 从出现问题的输入开始,逐步移除某些特定条件或参数。
  • 每次移除后重新测试系统,观察是否仍然出错。
  • 不断重复此过程,直到定位到引发问题的最小条件或单个参数为止。
换句话说,逐步减少引发问题的因素,直至精准定位根本原因(或尽可能接近)。

方法二:逐步添加法(Isolation by Addition)

  • 从一个已知正常的输入或环境出发;
  • 一次添加一个可能导致问题的条件或参数;
  • 每次添加后,重新测试系统;
  • 重复此过程,直到系统再次出现问题为止。
换句话说,每次只加入一个新因素,直至找到导致问题的关键因素。

核心思路

这两种方法都是通过刻意操控输入条件,从外部来“透视”黑盒子内部的机制。
虽然系统本身可能是“黑盒”,无法被直接观测,但通过这种方法,我们能逐渐缩小问题范围,直至找到具体的故障根源。
💡

食物过敏测试是这一方法的典型例子

食物过敏测试本质上就是黑盒测试的一种应用。
  • 通过 剥离法,人们通常会采用 消除饮食法(Elimination Diet),先去掉所有可能导致过敏的食物,然后逐步添加它们,观察是否出现过敏反应。
  • 通过 添加法,也可以从一个已知安全的基础饮食出发,逐个引入可能的问题食物,直到过敏反应再次出现,从而找出真正的过敏原。

理解问题的本质

也许系统里某个部件**“烧毁了”**,但关键问题是:
它为什么会烧毁?
  • 这种部件是不是本来就容易在周二下午烧毁?(显然不太可能!)
  • 还是因为短路、进水、散热不良,甚至是受到附近雷达的干扰?(这是真实发生过的案例)
如果某个组件本不应该故障,却故障了,那问题肯定不只是更换部件这么简单——一定有更深层次的原因

更换前,确保不再重蹈覆辙

在更换新部件之前,我需要确认:
它不会遭遇相同的命运。
如果旧部件损坏了,要么是:
  1. 部件本身的规格不足(under spec),长期工作在超出设计负荷的环境下;
  1. 系统中的某个问题(短路、电流过大、散热问题、环境干扰等)正在给它施加过度压力,导致它提前报废。
如果不找出真正的原因,即使换上新零件,它仍然可能很快再次损坏。

解决问题

理解了问题,就已经解决了一大半,除非零件难以获取,或者安装极为麻烦
在大多数情况下,“修理”基本等同于“更换损坏的部件”。但真正优秀的排查者,并不会盲目更换整个系统或大部件,而是找到最小的可替换单元

“换件工” vs. 优秀的排查者

  • “换件工”(part swapper) 是对低水平维修人员的贬义称呼,他们习惯于大范围更换零件,而不去真正理解问题
  • 优秀的排查者 能够精准确定损坏的最小组件,从而减少浪费,提高修复效率。
举个例子:
这些陈述都可能是正确的:
  1. 这个音响系统坏了。
  1. 这个MP3 播放器坏了。
  1. 这个 MP3 播放器的耳机插孔坏了。
  1. 这个耳机插孔上的焊点坏了。
可能的解决方案:
  • 我可以 换掉整个音响系统
  • 我可以 换掉 MP3 播放器
  • 我可以 换掉耳机插孔
  • 我可以 重新焊接一个焊点
结果是一样的,但成本和复杂度截然不同。

高效修复 vs. 过度纠结

虽然更换超过必要范围的零件既浪费缺乏优雅,但优秀的排查者同样需要知道:
什么时候不再执着于问题的根源,而是接受一个更实际、更省资源的权宜之计(band-aid solution)。
  • 有时候,我能找到问题的真正根源。
  • 有时候,我找不到,但这并不重要。
关键在于平衡:“彻底修复” vs. “合理的折中方案”

排查问题的能力可以被教授吗?

自 2024 年 5 月以来,我就一直在断断续续地写这篇文章。也许这只是我的自我安慰,但我似乎确实在排查问题方面变得更厉害了。
最大的变化是——现在我更愿意去做排查工作了。
我开始主动接受一些原本不太感兴趣的问题,仅仅是为了测试我的理论。
即使它们看起来不值得我花时间,我也愿意去尝试。
另外,由于我花了大量时间思考和讨论排查问题,我现在感觉自己应该算是某种程度上的“本地专家”了——即便这个称号可能只是我自己想象出来的,我仍然希望配得上它。

如果有预算,我会怎么科学验证这个问题?

如果预算允许,我会设计一个对照实验
  1. 找一批人,随机分成两组。
  1. 一半人阅读这篇文章,另一半人阅读一篇长度和风格相同但无关的文章
  1. 两组人都尝试解决 Linux 服务器的排查问题,比如在 sadservers.com 这样的网站上进行测试。
  1. 比较两组人的表现,看看这篇文章是否真正提高了他们的排查能力。
如果读过这篇文章的人表现更好,那就意味着排查问题的能力可能是可以教授的——甚至可以继续实验,看看这种技能在其他领域是否同样适用。

更有趣但不够严谨的测试方法

我还考虑过一个更有趣但不太科学的方案:
👉 向《连线》(Wired)杂志提议一个报道,做一篇“沉浸式新闻报道”(gonzo journalism)。
在这个实验中,我会尝试把这篇文章中的所有建议应用到各种我完全不擅长的领域的排查问题中,看看这些技巧是否依然奏效。
  • 能否用它来修复一台无法启动的拖拉机?
  • 能否用它来诊断一支交响乐团的演奏问题?
  • 能否用它来帮助某个迷路的徒步旅行者重新找到方向?
如果这些方法在各种完全不同的领域都能有效,那说明排查问题的技能确实是可以教授的,并且是可迁移的

最终的问题

也许真正的挑战并不是**“能否教授排查能力”,而是“如何让人们愿意去培养它”**。
毕竟,我变得更擅长排查问题,最关键的变化并不是学到了多少新技巧,而是:
我愿意去尝试更多问题,即便它们看起来不值得我的时间。
我愿意去思考、去总结、去探索这个过程本身。
所以,也许排查问题的能力确实可以被教授,但前提是,我们必须先让人愿意去做排查这件事。

结论

一旦我进入排查问题的思维模式,世界上的大多数事情都开始看起来像是一个系统-问题-解决方案的结构。
在某些情况下,这种视角确实非常有效。
但它并不是唯一的视角,也不是适用于所有事情的最佳方法
当我遇到真正的故障或系统性问题时,主动以“排查问题”的方式去分析和解决,能帮我节省大量时间。
但同样重要的是,我也要意识到——并不是所有事情都需要被视为一个“问题”,也不是所有事情都需要一个“解决方案”。
有时候,事情本身并不是“坏掉了”,只是它本来的样子就是这样。
而学会区分这两者,可能才是最重要的能力。
 
  • 思考
  • 排障
  • troubleshooting
  • 浮点数的编码Distributed Systems Programming Has Stalled
    Loading...
    Catalog
    0%