如何处理复杂系统 - 我在 Google 工作时学到的
好的内容甘之如饴,SRETALK 讨论各类 SRE、开源、可观测性相关主题,搜罗全网真知灼见,欢迎投稿,欢迎关注,一起进步!
你好!今天,让我们来讨论一个有趣的主题:复杂系统。
简介
在我的职业生涯中,我曾在许多繁杂的环境中工作。例如,我在一家 Uber 的竞争者公司优化过网约车司机与乘客的匹配。这种环境,和其他环境一样,技术上具有挑战性。然而,与我在 Google 的工作经历相比,后者在复杂性方面无可比拟。我在 Google 的两年经历进一步深化了我对复杂性的认识。
译者注:原文用了两个词,需要理解清楚先。Complicated vs. Complex。Complicated 是指纷繁复杂但有章可循的,比如汽车零部件有几万个,纷繁复杂,但是有章可循,再比如税务申报流程,纷繁复杂但有章可循。Complex 是指复杂的,主要指事物由许多相互关联、相互作用的部分构成,这种复杂性是事物本身所固有的,难以简单地进行拆解,比如人脑有数千亿个神经元相互连接,很复杂,气候变化涉及多个学科领域,也很复杂。
作者之前在 Uber 的工作经历是 Complicated,Google 的工作经历的是 Complex。
复杂系统的特性
在本节中,我们将讨论五种常见特征,这些特征有助于识别复杂系统。并非所有复杂系统都具备这些特征,但它们通常会表现出以下特征中的至少一些。
Emergent Behavior 涌现行为
涌现行为是指系统整体行为无法仅通过孤立分析其各个组件来预测。例如,Gemini 产生意外结果是一种 emergent 行为。虽然我不能披露根本原因,但通过单独分析所有不同组件是几乎不可能预见这种行为的。这可能是复杂系统的一个特点:它们的行为方式难以仅通过查看其各个部分来预测,这使得调试和管理变得更加困难。
Delayed Consequences 延迟的后果
复杂系统的一个可能特征是延迟的后果,即行动并不总是立即产生效果,相反,后果可能要过很久才会显现出来。例如,部署系统的新版本可能会引入一个细微的问题,这个错误可能在几天甚至几周后才会显现。这种延迟使得调试变得更加复杂,因为与即时影响相比,要找出根本原因会困难得多。在复杂的系统中,仅仅依赖即时反馈可能会造成一种虚假的稳定感,当问题最终浮现时,会导致重大惊喜。在这种环境中工作时,牢记后果可能会延迟显现这一点至关重要。
Local vs. Global Optimization 局部优化 vs. 全局优化
在复杂的系统中,优化一个部分并不一定能够改善整个系统,有时甚至会适得其反。与非复杂系统不同,在非复杂系统中,改进一个部分通常会带来积极的收益,而在复杂系统中,要理清系统各部分的关系要困难得多。组件之间以非显而易见的方式相互作用,局部优化可能会产生难以预测的连锁反应,有时会导致系统层面的负面结果。
这突显了复杂系统的一个关键特征:整体大于部分之和。因此,局部的收益并不总是转化为整体的改进,在某些情况下,甚至可能恶化整个系统。
译者注:在人脑那么多神经元的情况下,局部优化可能会导致全局的负面结果。但是在软件系统里,优化局部导致全局负面结果的情况,我还尚未遇到。除非,局部的所谓的“改进”实际不是真改进。
Hysteresis 迟滞
迟滞现象描述了系统在其过去状态的影响下继续表现的方式,即使原始原因已被移除。
一个现实世界的例子来说明迟滞现象是交通拥堵:即使道路事故被清除,由于车辆仍然聚集在一起,延误仍然存在。同样,在分布式系统中,即使根本问题得到解决,故障也可能导致级联的减速。确实,由于缓存、重试或排队请求等各种原因,依赖系统可能需要时间来恢复。
在复杂系统中,仅仅修复根本原因往往不够。因此,评估系统是否容易出现迟滞现象并提前预见其影响至关重要。
Nonlinearity 非线性
在复杂系统中,小的变化可能会产生不成比例的或不可预测的效果。
例如,在排队理论中,系统负载会按预期增加延迟。然而,当队列接近饱和时,即使请求增加一点点,响应时间也可能呈指数级激增。
复杂的系统往往会在某个临界点达到转折点,使得过去的行为模式在预测未来时变得不可靠。这种非线性意味着,传统的线性假设,即输入可以预测性地映射到输出,并不总是适用于设计、测试和推理复杂系统。
小结
总结这一部分,复杂的系统:
- 仅通过单独查看其各个部分是难以理解的。
- 它们的效果不一定马上显现,后果可能会被延迟。
- 当一部分优化并发生变化时,不要总是整体改进,有时变化会使情况变得更糟。
- 即使原始原因消失,系统仍可能持续受到过去状态的影响。
- 可以对小变化产生大的或意想不到的效果。
需要注意的是,规模本身并不能使一个系统变得复杂:即使是小型系统也可能表现出复杂行为,如涌现或非线性。
处理复杂系统的方法
鉴于这些特点,我们在复杂环境中如何有效运作?以下是一些我自己发现有效的策略。
Reversibility 可逆性
在处理复杂系统时,如果可能,我们应该倾向于可逆的决策,即可以撤销的改变。亚马逊的单向门与双向门框架很好地捕捉了这一理念:
- 单向门代表不可逆的决策,需要仔细考虑。
- 双向门代表可逆的决策,让我们可以快速行动并在较低风险下迭代。
在许多情况下,尤其是在复杂的系统中,倾向于使用双向门道会带来更好的结果,因为我们可以进行实验、学习和改进,而不是一开始就过度设计。
不过,并非所有决策都应该是可逆的。例如,一些选择,如安全策略或合规性相关的改变,需要前期的承诺。关键在于知道何时优化速度和迭代,何时需要审慎和小心。
Think Beyond Immediate Metrics 比即时指标多想一步
因为复杂的系统并不总是对局部优化作出可预测的响应,因此定义成功的正确指标可能和我们所做的改变一样重要。事实上,过于关注孤立的局部指标可能会造成一种虚假的成功感,而掩盖了系统其他部分的意外负面影响。
为了避免这种情况,在做出更改之前,我们应该定义局部和全局指标,以获得系统的全面视图。这确保我们不仅衡量直接影响范围内的影响,还会考虑整个系统。
选择合适的指标不应仅仅验证局部变化的成功;相反,它们应该帮助我们做出更好的决策,并确保在系统层面实现有意义的改进,而不仅仅是孤立的领域。
Innovation 创新
正如讨论的那样,复杂的系统往往需要独特的解决方案。由于常规策略并不总是适用,我们必须愿意跳出常规思维,拥抱创新。
我还记得我在谷歌参加的一次早期会议。有人提出了一个在复杂性和规模上看似荒谬的问题。我立刻在心里反应:“这不可能做到”。但随后,一位队友说:“但我们是谷歌,我们应该能够搞定它!”
这句话给我留下了深刻的印象。虽然不是每家公司都有谷歌那样的资源,但心态才是最重要的。面对复杂问题时,我们应该假设它是可解决的,然后将其分解,进行实验,并不断迭代,直到找到前进的道路。
这一部分可能显得有些陈词滥调,但再次强调,复杂问题需要非传统的思维方式。在面对复杂问题时,开放接受创新解决方案不仅是有帮助的,往往是必要的。
Controlled Rollout 受控发布
在复杂系统中部署更改时,我们应该依赖经过验证的最佳实践以最小化风险。这些包括:
- 功能开关:无需部署新代码即可动态启用或禁用功能,允许安全地进行实验并快速回滚。
- 金丝雀发布:仅对生产环境中一小部分受控实例进行有限发布,最适合仅包含少量生产实例的环境。
- 渐进式发布:逐步扩大发布范围,最适合大规模生产环境,涉及多个集群或区域。
- 灰度测试:在不影响真实用户的情况下,将变更与生产流量并行运行。这有助于在启用变更之前验证变更的正确性。
通过运用这些技术,我们可以减少故障的影响范围,提高对变更的信心,并实现更快的迭代。
Observability 可观测性
可观测性是复杂系统的主要支柱之一。我对于可观测性的定义(主要受到 Observability Engineering 的启发)如下:
你可以通过切片和分析高基数和高维度的遥测数据来理解系统中的任何状态(无论多么新颖或古怪),而无需提交新代码。
缺乏可观测性:
- 系统变得更为脆弱,因为隐藏的问题直到造成实际影响才被发现。
- 定位意外失败变得更加困难。
- 由于缺乏高效的反馈循环,创新被减缓。
在复杂环境中,由于未知因素不可避免,可观测性至关重要。它使团队能够应对不确定性,更安全地进行实验,并获得快速反馈循环,从而不断改进系统。
如果没有适当的可观测性,变更将只是意见而非基于信息的决策。
🎯 可观测性体系,构建起来其实也很繁杂,有时确实建设了各种零散的指标、日志、链路系统,但是故障来了,定位起来还是费劲。我们创业做的 Flashcat 就是来解决这个问题。欢迎联系我们,免费交流产品思路:https://flashcat.cloud/contact/
Simulation 模拟
预测复杂系统的行为通常并不简单,有时几乎是不可能的。
我记得曾经有一个案例,我们花费了大量时间设计一项变更,仔细用数据支持每一个假设。然而,由于未考虑到的变量等因素,这项变更最终证明是无效的。
有时,与仅依赖预测相比,在推出更改之前进行模拟可能是一种更有效的做法。利用模拟测试的方法包括:
- 重放历史事件:如果我们设计系统记录所有输入,我们可以在新版本中重放历史事件并分析其影响。这使我们能够在受控环境下验证更改,减少不确定性,提高复杂系统中的决策质量。
- 确定性仿真测试:我们可以通过创建受控的、可重复的仿真来代替依赖实际数据,这些仿真可以在特定条件下模拟系统的行为。这使得我们能够以完全确定性的方式测试系统在各种条件下的反应。
请注意,本节中提出的想法也高度依赖于可观测性。
Machine Learning 机器学习
在复杂环境中,基于规则的方法往往因为难以预料所有场景而达到极限。在这种情况下,机器学习可以变得特别有效。
确实,与静态启发式方法不同,机器学习模型可以根据反馈循环不断适应,并从实际数据中学习,而不是依赖于僵化的预定义逻辑。
这使得系统能够:
- 检测未明确编程的新兴模式。
- 动态适应变化,无需持续地人类干预。
- 做出概率性决策,而不是依赖严格的 if-else 条件。
Strong Team Collaboration 强大的团队协作
最后但同样重要的是,我相信在复杂环境中,团队协作比任何地方都更为必要。例如,清楚地传达变更的复杂性原因、讨论可用的选项以及与队友辩论权衡点,这些都是至关重要的技能。
在复杂的系统中,往往没有唯一正确的答案。因此,一个能够有效协作并共同应对模糊性的团队,可以在决策上产生巨大影响,最终做出更强有力的决策。
最终思考
再次强调,繁杂的(complicated)问题可以通过可重复的解决方案来解决,而复杂的(complex)系统则需要适应性和不同的思维方式。因此,识别系统是 complicated 还是 complex 的区别如此重要:这决定了我们应该如何解决问题。
然而,在许多环境中,系统既不是纯粹的 complicated 也不是纯粹的 complex。有些部分可以遵循结构化和可预测的解决方案,而另一些部分则需要适应性和新颖的方法。关键在于学会识别何时需要适应性,何时足够使用结构化解决方案。
希望这篇文章能帮助你识别复杂环境的特征,并提供实用的模式来有效应对这些环境。这些模式中有哪一部分引起了你的共鸣?你在复杂环境中还使用过哪些策略?请在评论中告诉我。
本文由 SRETALK 翻译,原文作者 Teiva Harsanyi,原文链接:https://www.thecoder.cafe/p/complex-systems