译者:
更易读的大纲在:
[https://www.xiaobu.net/ai/facecompex.html](https://www.xiaobu.net/ai/facecompex.html)
1. 引言
• 复杂系统与工作经验的关联(Uber 与 Google 对比)
2. 复杂 vs. 复杂化
• 复杂化问题可预测、有既定解法
• 复杂问题需创新、适应性强的解决方案
3. 复杂系统的五大特征
• 涉及涌现行为、延迟后果、局部 vs 全局优化、迟滞效应、非线性响应
4. 应对复杂系统的策略
• 可逆性决策、关注系统整体指标、鼓励创新
• 渐进式部署、增强可观测性、使用仿真测试与机器学习
• 强调团队协作与沟通
5. 总结
• 辨别系统属性决定解决方式;融合结构化与灵活性是关键。
我在谷歌工作的所学所得
作者:泰瓦·哈尔萨尼(Teiva Harsanyi)
日期:2025 年 5 月 7 日
大家好!今天,让我们来探讨一个引人入胜的话题:复杂系统。
引言
在我的职业生涯中,我曾在许多复杂的环境中工作过。例如,我曾在优步的一家竞争对手公司负责优化网约车的司机与乘客匹配算法。和其他工作场景一样,这个工作在技术上颇具挑战性。然而,就复杂性而言,没有什么能比得上我目前在谷歌的工作经历,在这里的两年让我对复杂性有了更深刻的认识。
在这篇文章中,我们将剖析复杂性这一概念。接下来,我们会退一步,探讨是什么让某些环境变得复杂而非仅仅是繁琐,然后探索有效应对复杂系统的模式。
繁琐与复杂的区别
理解繁琐问题和复杂问题之间的区别至关重要,因为这两类问题需要从根本上不同的解决方法:
- 繁琐问题虽然错综复杂,但具有可预测性。它们有结构化、可重复的解决方案。例如,报税是一项繁琐的工作,但它是一个结构化的常规问题,因为每年的流程大致相同。
复杂问题同样错综复杂,但具有独特性。它们需要适应性强且往往是新颖的解决方案。例如,缓解气候变化就是一个复杂问题,因为它需要新的、适应性的解决方案,仅靠现有的方法无法真正应对其不断演变的挑战。
回到软件工程领域,在优步的竞争对手公司,其中一个挑战是为乘客高效地找到最近的司机。这绝非易事,但严格来说,它并非“复杂”问题。实际上,有许多解决方案可供选择,比如应用地理哈希算法(举例),选择并实施其中一个解决方案就是正确的做法。
在谷歌,我担任站点可靠性工程师(SRE),专注于为谷歌的机器学习基础设施提供支持的系统。在这里,我认为所面临的挑战才是真正的复杂,因为需要新的范式和调度方法,尤其是在谷歌这样的规模下。
识别一个系统是繁琐还是复杂非常重要。实际上,我们提到过,繁琐的系统从定义上来说是可重复的,而复杂的系统则需要独特且定制化的方法。因此,如果我们试图用通用的解决方案来解决复杂问题,可能无法取得有效的结果。复杂系统的特征
在这一部分,我们将讨论五个有助于识别复杂系统的常见特征。并非所有复杂系统都具备所有这些特征,但它们往往至少表现出其中的一些特征。
涌现行为
当一个系统的整体行为无法仅通过孤立地分析其各个组成部分来预测时,就会出现涌现行为。
例如,双子座模型产生意外结果就是一种涌现行为。虽然我不能透露根本原因,但仅通过分别分析各个不同的组件,几乎不可能预见到这种行为。
这是复杂系统的一个可能特征:它们的行为很难仅通过观察其组成部分来预测,这使得调试和管理变得更加困难。延迟后果
复杂系统的另一个可能特征是延迟后果,即行动并不总是立即产生影响,相反,后果可能要很久之后才会显现。
例如,部署系统的新版本可能会引入一个细微的问题,这个问题可能要过几天甚至几周才会出现。这种延迟使得调试变得更加复杂,因为与即时影响相比,确定根本原因变得困难得多。
在复杂系统中,仅仅依赖即时反馈可能会营造出一种虚假的稳定感,当问题最终出现时,就会让人措手不及。在这样的环境中工作时,牢记后果可能需要时间才能显现是至关重要的。局部优化与全局优化
在复杂系统中,优化某一部分并不一定能改善整个系统,在某些情况下,甚至可能会使情况变得更糟。
与非复杂系统不同,在非复杂系统中,改善某一部分通常会带来积极的收益,而复杂系统则更难进行推理。各个组件之间以不明显的方式相互作用,局部优化可能会产生难以预测的连锁反应,有时会导致系统层面的负面结果。
这凸显了复杂系统的一个关键特征:整体大于部分之和。因此,局部的收益并不总是能转化为全局的改善,在某些情况下,甚至可能会降低整个系统的性能。滞后性
滞后性描述了一个系统的过去状态如何继续影响其行为,即使最初的原因已经消除。
一个现实世界中说明滞后性的例子是交通拥堵:即使道路事故已经清理完毕,交通延误仍然会持续,因为车辆仍然聚集在一起。同样,在分布式系统中,故障可能会导致级联式的减速,即使根本问题已经解决。实际上,由于各种原因,如缓存、重试或排队请求,依赖系统可能需要时间才能恢复。
在复杂系统中,仅仅解决根本原因并不总是足够的。因此,评估一个系统是否容易出现滞后性,并预测其影响是至关重要的。非线性
在复杂系统中,微小的变化可能会产生不成比例的巨大或不可预测的影响。
例如,在排队论中,系统负载会可预测地增加延迟。然而,当队列接近饱和时,即使请求量有微小的增加,也可能导致响应时间呈指数级飙升。
复杂系统常常会达到临界点,此时行为会突然发生变化,使得过去的趋势无法可靠地用于预测。这种非线性意味着传统的线性假设(即输入可预测地映射到输出)在设计、测试和推理复杂系统时并不总是有效的。总结
总结这一部分,复杂系统具有以下特点:
- 仅通过分别观察其组成部分很难理解。
- 并不总是立即显示其影响,后果可能会延迟出现。
- 当某一部分得到优化时,整个系统并不总是会得到改善,而且改变有时会使情况变得更糟。
- 即使最初的原因已经消失,仍然可能受到过去状态的影响。
可能会对微小的变化做出巨大或意外的反应。
需要注意的是,仅仅规模大并不意味着一个系统就是复杂的:即使是小型系统也可能表现出复杂的行为,如涌现或非线性。
订阅应对复杂系统的模式
鉴于这些特征,我们如何在复杂的环境中有效地运作呢?以下是一些我个人认为有效的策略。
可逆性
在处理复杂系统时,我们应尽可能选择可逆的决策,即如果决策行不通,可以撤销的更改。
亚马逊的单向门和双向门框架很好地体现了这一理念:- 单向门代表不可逆的决策,需要谨慎考虑。
双向门代表可逆的决策,使我们能够快速行动并以较低的风险进行迭代。
在许多情况下,尤其是在复杂系统中,倾向于选择双向门会带来更好的结果,因为我们可以进行实验、学习和改进,而不是一开始就过度设计。
话虽如此,并非所有决策都应该是可逆的。例如,一些选择,如安全策略或合规相关的更改,需要事先做出承诺。关键是要知道何时应追求速度和迭代,何时应深思熟虑、谨慎行事。超越即时指标进行思考
由于复杂系统对局部优化的响应并不总是可预测的,因此定义正确的成功指标可能与我们所做的更改同样重要。实际上,过于关注孤立的局部指标可能会营造出一种虚假的成功感,同时掩盖系统其他地方意想不到的负面后果。
为了避免这种情况,在做出更改之前,我们应该同时定义局部和全局指标,以全面了解系统的健康状况。这确保我们不仅衡量了关注区域的直接影响,还考虑了整个系统。
精心选择的指标不应仅仅证实局部更改的成功;相反,它们应该帮助我们做出更好的决策,并确保在系统层面上实现有意义的改进,而不仅仅是在孤立的区域。创新
如前所述,复杂系统通常需要独特的解决方案。由于传统策略可能并不总是适用,我们必须愿意跳出框框思考并拥抱创新。
我记得在谷歌的第一次会议上,有人提出了一个在复杂性方面似乎荒谬的问题,尤其是考虑到谷歌的规模。我当时脑海中的第一反应是:“这不可能”。但随后,一位同事说:“但我们是谷歌,我们应该能够解决它!”
这句话一直留在我的脑海中。虽然并非每个公司都有谷歌那样的资源,但我认为这种思维方式才是真正重要的。当面对复杂问题时,我们应该假设问题是可以解决的,然后将其分解、进行实验并迭代,直到找到前进的道路。
有人可能会觉得这一部分很陈词滥调,但再次强调,复杂问题需要非传统的思维方式。在许多情况下,面对复杂问题时,对创新解决方案持开放态度不仅有帮助,而且是必要的。可控部署
在复杂系统中部署更改时,我们应该依靠经过验证的最佳实践来降低风险。这些实践包括:
- 功能开关:无需部署新代码即可动态启用或禁用功能,允许进行安全的实验和更快的回滚。
- 金丝雀发布:向一小部分受控的生产环境进行有限的部署,适用于只有少数生产实例的环境。
- 渐进式部署:逐步扩大部署范围,最适合具有多个集群或区域的大规模生产环境。
影子测试:在不影响真实用户的情况下,与生产流量并行运行更改。这有助于在启用更改之前验证其正确性。
通过利用这些技术,我们可以减少故障的影响范围,增强对更改的信心,并实现更快的迭代。可观测性
可观测性是复杂系统的主要支柱之一。我对可观测性的工作定义(主要受《可观测性工程》的启发)如下:
你可以通过对高基数和高维度的遥测数据进行切片和切块,而无需部署新代码,来理解系统的任何状态(无论多么新颖或奇特)。
如果没有可观测性:- 系统会变得更加脆弱,因为未知问题会一直隐藏,直到造成实际影响。
- 调试意外故障会变得非常困难。
由于缺乏有效的反馈循环,创新会受到阻碍。
在复杂的环境中,未知因素是不可避免的,可观测性至关重要。它使团队能够应对不确定性,更安全地进行实验,并获得短反馈循环,以持续改进系统。
如果没有适当的可观测性,更改就只是主观意见,而不是基于充分信息的决策。模拟
预测复杂系统的行为很少是简单的,有时甚至几乎不可能。
我记得有一次,我们花了大量时间设计一个更改,并仔细地用数据支持每一个假设。然而,由于未考虑到的因素,如潜在变量,最终这个更改并没有达到预期效果。
有时,与其仅仅依赖预测,更有效的方法是在部署更改之前进行模拟。有多种利用模拟测试的方法,包括:- 重放过去的事件:如果我们设计一个系统来记录其所有输入,我们可以针对新版本重放过去的事件,并分析其影响。这使我们能够以可控的方式验证更改,减少不确定性,并改善在复杂系统中的决策。
确定性模拟测试:不依赖真实世界的数据,我们可以创建可控的、可重复的模拟,对特定条件下的系统行为进行建模。这使我们能够以完全确定的方式测试系统在各种条件下的反应。
需要注意的是,本节中提出的想法也在很大程度上依赖于可观测性。机器学习
在复杂的环境中,基于规则的方法往往会达到其极限,因为很难预测所有的情况。在这些情况下,机器学习可能会特别有效。
实际上,与静态启发式方法不同,机器学习模型可以根据反馈循环不断调整,并从现实世界的数据中学习,而不是依赖于僵化的、预定义的逻辑。
这使得系统能够:- 检测未明确编程的新兴模式。
- 动态适应变化,而无需持续的人工干预。
做出概率性决策,而不是依赖严格的“如果-那么”条件。
强大的团队协作
最后但同样重要的是,我认为在复杂的环境中,比在其他任何地方都更需要强大的团队协作。例如,清晰地传达为什么某个更改是复杂的、讨论可用的选项以及与队友辩论权衡取舍都是至关重要的技能。
在复杂系统中,往往没有单一正确的答案。因此,一个能够有效协作并共同应对不确定性的团队可以产生巨大的影响,最终导致更强大的决策。结语
再次强调,繁琐的问题可以用可重复的解决方案来解决,而复杂的系统则需要适应性和不同的思维方式。这就是为什么识别一个系统是繁琐还是复杂如此重要:它决定了我们应该如何解决问题。
然而,在许多环境中,系统既不完全是繁琐的,也不完全是复杂的。有些部分可以采用结构化、可预测的解决方案,而其他部分则需要适应性和新颖的方法。关键是要学会识别何时需要适应性,何时结构化的解决方案就足够了。
💬我希望这篇文章能帮助你识别复杂环境的特征,并为你提供有效应对这些环境的实用模式。这些模式中有没有哪个引起了你的共鸣?你在复杂环境中还使用过哪些其他策略?请在评论中告诉我。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。