今年,我有幸亲临现场参加了所有的 keynote,每一场都让我感受到深深的震撼。无论是全新的功能发布,还是令人眼前一亮的新特性展示,每一场 keynote 都精彩纷呈,充满干货,值得反复学习和回味。
恰好,我在其他平台上发现了一些整理得非常好的文章版本。于是,我决定将其制作成中文版本。在翻译和整理的过程中,不仅加深了自己的理解,也希望将这些内容分享给大家,方便更多人学习和阅读。
Simplexity 剧集:S3 的复杂性与简单性的故事
今天,我们将探讨从简单的起源到复杂性如何发展的概念。我们早期的许多服务都带有"Simple"这个词。例如,S3(简单存储服务)。虽然名字可能是 Simple,但实际上使事情变得简单可能会变得非常复杂。在这一集的 Simplexity 中,我们将讲述世界上最大的存储系统之一,以及为保持其简单而努力的创新者团队的故事。
S3 的诞生和"两个披萨团队"的概念
那是 2000 年代初头的事情。我们大约有四、五个人 - 我、Werner、Al,以及现在已经不在的几位成员。那时只有 REST API 和少数功能。Alex 为 S3 背后的简单性提供了很多灵感。Werner 发现了其他人没有注意到的东西。我认为他真的很欣赏我的方法。我无论如何都很重视简单性。
有一天晚上,我们点了比萨。他带着一堆大约 6 米高的比萨盒子进来了。他把大部分比萨掉在地上,并说了"每个团队 2 个比萨"这句话。显然有 2 个比萨是可以救回来的。从那时起,"2 个比萨团队"的概念就诞生了。我们拆解了功能性层级结构,重新组织成小型自律团队。每个团队都被组织成足够小的团队,以便用 2 个比萨就能满足他们。
在考虑减少存储成本的方法时,我想出了一个非常简单的计划。Alex 拿着披萨刀出现,开始切片。现在,我正在处理 Franken pizza。这是免费的披萨。欢迎来到纠删码的世界。我们必须重写整个存储引擎。你听说过 Glacier 事件吗?Alex 让人们继续把他们的披萨一半直接放进冰箱里。剩下了很多。
关于冰川存储类,您能告诉我吗?这是针对不同访问模式的不同成本层吧?我们始终致力于让客户的体验保持简单,无论我们面临多大的复杂性。这是我们学习、接受甚至珍视的事情。至于 Alex 是否真正引发了所有这些创新,没人知道。他就像家具的一部分。这是强烈的。这是 2016 年的事。他在这里。这个家伙非常喜欢你。
这样的成功实际上是由简单的披萨引发的。我原以为这是关于复杂性和简单性的纪录片。是的,没错。实际上,为了追求简单性,我雇佣了多年的合作伙伴,那位 Maverick 就在这里。这个人是谁呢?难道不是雇佣了 Alex 吗?他提出了两张披萨团队的想法。我记得你应该是让 Al 这个早期 S3 的主要开发者来这里的。
Dr. Werner Vogels 的 20 年 AWS 旅程
欢迎亚马逊公司的副总裁兼首席技术官 Werner Vogels 博士。早上好。
每年,大家的热情让我更加谦虚。希望能回应早上从 ACM 排队来听我演讲的大家的期待。今年是一个特别的年份。20 年前,45 岁不再年轻的我决定放弃学术道路,在书店工作。如果当时有人问我是否会在同一个雇主那里工作 20 年,我可能会把那个人送到精神病院。然而,在亚马逊的 20 年真的是令人惊叹的。每一年都不同,这 20 年来取得的技术成就令人难以置信。我还没有结束。
在过去的 20 年中,一直以来的都是持续学习。这是因为作为学者,我实际上并不知道自己要踏入什么领域。虽然我可能认为自己是在为实际用户构建系统,但老实说,我从未与客户直接接触过。在这方面,我确实得到了一个非常严峻的教训。我需要转变思维。当时的我充满热情,精力充沛,独断且傲慢,但我的学术背景并没有为即将发生的事情做好充分的准备。机器因停止而故障,或者故障与障碍之间没有相关性这样的学术前提,完全是不切实际的。
就职数个月后,Jeff Bezos 和 Rick Dalzell 问我是否愿意担任 CTO 的职务。我不能错过这个构建几乎所有人都会使用的世界最大分布式系统的机会。当时的 CTO Alvin Mullins 并不想要这个职位,他真的希望回到工程师的岗位,自己编写下一代技术。我们非常感谢 Al,因为他奠定了 AWS 的基础。所有在后台运行的核心技术都是他的作品,AWS 内的技术文化显然留下了他的足迹。我将要穿上非常大的鞋子,但由于没有人知道 CTO 的工作意味着什么,我的傲慢战胜了这一切,决定接受这个职位。
在过去的 20 年中有许多亮点,但其中第一次的 re:Invent 尤其令人印象深刻。因为我有机会与大家交谈,并展示我对下一代开发的原则。我们一直希望以一种我们之前无法实现的方式来彻底构建应用程序。在这次首次演讲中,我展示了四个类别的建议,让我们回顾一下。
初回 re:Invent 的四个类别的建议
我认为有些事情真的做得很好。我很高兴看到许多人在至少两个可用区中运行生产环境的工作负载。持续集成和持续部署并不是因为我说了才这样,但现在每个人都理所当然地在做这件事。另一方面,也有一些更具争议性或耗时的项目。将系统分解为小的组成部分,并使其成为松耦合的无状态服务,这是亚马逊一直在做的,这使我们能够扩展规模、确保可靠性,并独立演进每个部分。然而,这也让我们陷入了关于是单体、服务、微服务还是纳米服务的某种宗教争论之中。架构是基于需求,有时也基于所选择的技术来决定的。
如果使用 Rails,将自动选择单体架构,因此构建纳米微服务非常困难。我在《All Things Distributed》上写了一篇关于单体架构和微服务之间区别的文章,以及在这种情况下应该做什么。
另一个提议是与业务部门合作。毕竟,作为工程师,我们并不是为了自己而开发技术,而是为了客户。无论这个客户是内部的业务部门还是外部客户,他们都是出于某种目的而构建的,因此我们需要与业务部门密切合作,构建他们能够控制的架构。我建议将应用程序分层,并与业务部门进行对话。例如,讨论每一层需要多大程度的可靠性。业务部门可能会说"所有东西都需要 100%的可靠性",但如果在这里解释成本,就可以进行建设性的对话。
正如刚才所述,考虑成本的架构设计一直被大家完全忽视。突然间,各种可能性变得可行,公司的数据中心的物理硬件限制也消失了。开始将成本纳入架构考虑,直到去年才终于开始。希望去年的节俭架构师在这方面能提供一些指导。我们目前正在重新设计面向架构师的网站,并增加了许多关于许多工程师如何实践节俭的深度内容,配合众多播客。
韧性与复杂性的管理
让我们来看看韧性。我有一句常被引用的名言。然而,这个引用总是不完整的。它不仅仅是"所有事物总是会失败"。实际上,第二句话才是最重要的。如果你在计划中考虑到故障,那么就不会有任何故障。生存不是偶然的事情,而是需要有计划地去应对。
回顾那次最初的演示,我认为许多原则至今仍然适用,但现在我有一些想要补充的观点。可演变性,也就是随着时间的推移能够适应变化的能力,以及可能与数据库选择相关的指导。2012 年时选择并不多,但现在有丰富的针对特定目的构建的数据库,因此在特定情况下应该使用哪种数据库的指导是必要的。
然而,在 2012 年时,有一件事还没有充分完善。当时,我将其介绍为奥卡姆剃刀------即不应将事情复杂化的原则。为了那些在高中没有学过的人解释一下,这意味着"保持简单"。有一个 KISS 原则------保持简单,傻瓜,但我不明白为什么称保持简单的人为傻瓜。复杂性总是潜伏着,我们真的需要控制它。我们需要考虑如何管理复杂性。
实际上,对于我们许多人来说,复杂性是不可避免的。系统随着时间的推移变得越来越复杂,但这是一件好事。因为我们需要添加更多功能,扩展规模,并解决安全问题。请记住,许多 AWS 服务至今仍然带有"Simple"这个名字------SimpleDB、简单存储服务、简单队列服务、简单通知服务等。这些服务在内部已经不再简单,实际上都变得非常复杂,但不知为何我们能够实现这一点。我们能够管理复杂性,许多服务在应对这种复杂性方面做得非常出色。虽然我们在过程中遇到了一些困难,但我们努力管理复杂性。
今天,我想谈谈在亚马逊中这些复杂系统的演变,以及它们是如何安全、可靠和简单地实现的。关于为了对抗简单性而构建的复杂系统,以及可管理性的原则。在这个背景下,相关的法则之一是特斯勒法则。
Tesler 的定律与复杂性的种类
特斯勒定律指出,复杂性既无法创造也无法破坏,只能转移到其他地方。当我加入亚马逊时,拉里·特斯勒在那里,我从他身上学到了很多东西。他主要关注用户界面及其背后的功能,特别是向客户提供的功能。根本思考复杂性是不可避免的这一事实是非常重要的。
然而,并不是所有的复杂性都是相同的。显然,存在着有意的复杂性,这是必不可少的。它有助于提高系统的可扩展性,支持新功能,并满足不断变化的客户需求。这是有意为之,具有明确的目的。另一方面,还有由于技术变化或架构监控不足而悄然出现的无意复杂性。这会降低系统的开发速度,并使维护变得困难。区分这两种类型是很重要的,如果做不到,系统将迅速从灵活变为脆弱。
意图之外的复杂性潜入的迹象是什么?我们都很清楚,因为我们都有这样的经验。功能开发速度的下降无疑是其中之一。在亚马逊,从单体到服务,从服务到微服务,再到微服务到共享服务基础设施的转变,主要原因是我们意识到创新正在减缓。后台的艰难工作使得新功能的实现变得更加困难。升级频繁,工单增加,代码库变得过于庞大,以至于没有人能够把握全局。这些模式都妨碍了进展,使得系统保持灵活和可靠的状态变得困难。
作为复杂性如何移动的一个好例子,我在考虑客户和 AWS。客户拥有应用程序路由器,下面有特定于域的应用程序,所有这些都在 AWS 上运行。大多数客户实现了基础服务,并且还实现了在各个应用程序中共享的特定于域的共享服务。我们与客户合作,确认共享服务层的复杂性,并考虑是否有因为我们没有做到足够简单而导致客户不得不实现的内容。
一个这样的例子是结果一致性和 Amazon S3。在 2006 年推出时,确保系统可靠并能够从故障中恢复的唯一方法是这样的普遍看法。对于许多客户来说,这非常难以处理。即使需要立即创建存储桶并进行写入,但存储桶仍处于不可用状态,这并不能算是良好的客户体验。许多客户试图在 Amazon S3 上实现强一致性,但这是一个高风险的选择。因为强一致性的实现很困难,并且需要处理所有边缘情况。
正确的处理方法不是消除复杂性,而是将其移到应有的位置。因此,我们在 S3 内部实现了 Amazon S3 强大的一致性,并将复杂性移到了本应在的位置。通过使用自动推理,我们能够确保一切都是正确的,并且所有边缘情况都得到了适当的处理。这种处理复杂性的方法不仅是我一个人的做法,亚马逊的其他成员以及其他公司也同样如此。我们所有人都在面对复杂性,因为应用程序随着时间的推移不断演变。
复杂性信号与自行车的比喻
那么,当我们看看表示复杂性的信号时,有一个常常被误解的信号。许多人倾向于认为,复杂性是由系统内的组件数量决定的,但事实完全不同。计算组件的数量并不是准确衡量复杂性的方法。
相反的是,复杂性是指系统有多混乱。Colm McCarthy 有一个很好的比喻。我们来考虑骑自行车。在这种情况下,我们可以把独轮车看作最简单的形式。它的组件数量非常少且简单,如果能掌握它,那将是非常棒的。它可以在原地旋转,但实际上骑上去是非常困难的。
作为衡量系统整体简单性的指标,仅仅计算组件的数量是不够的。以三轮车为例,骑行非常简单,不容易摔倒。大多数人通过三轮车学习骑自行车,但实际上转弯相当困难,因此缺乏灵活性。因此,自行车成为理想的解决方案。尽管组件数量较多,但提供了出色的灵活性。掌握起来比三轮车难,但比独轮车简单。复杂性体现在整体体验上。自行车虽然组件数量多,但从整体角度来看是最简单的形式。
我成长的地方到处都是自行车。在阿姆斯特丹,自行车被用来进行货物配送,邮政服务也使用自行车将信件送到每个家庭。考虑到简单性,这并不是偶然。保持简单而增加复杂性需要纪律。如果设计能够被不属于原设计团队的团队理解,那么可以说这是一个好兆头。
Canva 的增长和技术挑战
有一家企业在保持事物简单的同时,隐藏着巨大的复杂性。让我们来听听这家公司的故事。我们欢迎 Canva 的 CTO 布伦丹·汉弗里斯。Canva 成立于十多年前,使命是赋予全球人们设计的力量。我们开发了产品,使得无论技能水平如何,任何人都可以大规模创建美丽的视觉内容。
现在,Canva 在 190 个国家拥有超过 2 亿 2,000 万用户,广泛用于创建和协作各种内容,如演示文稿、视频和白板。这里有工程师们,所以我想分享一些值得骄傲的数字。我们的平台每秒处理约 120 万条请求,管理超过 162PB 的媒体,每天增加约 230TB。到目前为止,已经创建了超过 300 亿个设计,用户每秒添加超过 450 个新设计。
这些数字几乎为零的时候,我想追溯到这段旅程的开始。我在 2014 年加入了 Canva,当时只有几个人。大部分当时的后端工程团队都在这张照片中,但那时我们聚集在一个租金便宜的房间里,保险丝盒的状态也很可疑。冬天的时候,我们不得不选择使用取暖器还是外部显示器。当然,我们选择了显示器,并适应了环境。这张照片是那时候的,我在寒冷中戴着无指手套进行编码。
当时,我们头痛的问题是关于架构的两难。我们如何在快速推出市场的同时,构建能够迅速演进并具备可扩展性的方法。我们知道最终会需要微服务架构。这将使得组件能够单独扩展,同时也能扩展在平台上高效工作的工程师数量。因此,我们在展望微服务的未来时,构建并发布了单体架构。
这是完全无状态的,使用 Amazon Elastic Load Balancing 和 Auto Scaling 进行了水平扩展。我们仔细建模了识别出的主要实体,并通过简单的规则实现了该模型,以便将来可以分解。每个实体都通过服务接口进行封装,服务接口努力围绕简单的 CRUD 动词集保持一致。所有业务编排都必须在这个服务抽象层之上进行。
这些规则使得我们能够相对容易地拆解单体。当出现扩展的需求时,服务接口变成了 RPC 存根,一个执行单元逐渐演变为一个服务。最初的架构是单一的 Amazon RDS MySQL 数据库,在模式设计上同样考虑到了扩展。我们严格分离并维护实体之间的关系。当需要包含多个实体的业务编排时,必须结合服务上的 CRUD 操作来实现。虽然这很有吸引力,但在持久层中严格禁止跨域的连接。
这使得我们能够轻松地将一个 MySQL 实例拆分为多个 MySQL 实例。当需要扩展时,我们可以在每个 MySQL 实例上进一步扩展。我们可以通过读取副本进行扩展,并通过实例大小进行扩展。最终,当指数级增长变得爆炸性时,许多服务迁移到 Amazon DynamoDB,继续实现全球扩展。
正直地说,这个持久化的演进过程并不总是顺利的。指数级的增长对于人类来说,即使是工程师也很难理解。我们在 Media 服务中深刻体会到了这一点。由于 Canva 的爆炸性人气,Media 服务的 MySQL 突然变得庞大。我们知道需要迁移到 DynamoDB,但为了争取时间实现无缝迁移,我们在持久化层上设置了抽象层,同时继续使用 MySQL 直到其极限。当时的 MySQL 在数亿行的表大小下,承受着每天数千万行的增长压力,并不断遇到 Amazon RDS 的新限制。
在悉尼开发 Canva 的乐趣之一是,美国的流量高峰在悉尼时间的凌晨 1 点过后。因此,意识到这些限制是在深夜的待命警报中恍惚时发生的。为了将 MySQL 的容量推向极限,需要许多数据库的技巧。首先,删除了所有妨碍无停机时间的模式迁移的外键。接下来,由于在那个大小下,即使是 JOIN 也非常耗费成本,因此开始了模式的非规范化。最后,为了将模式管理提升到应用层,开始将数据作为 JSON 块整合到文本列中。
当我们准备切换到 DynamoDB 时,MySQL 已经被变形为一个可疑的键值存储,我们对午夜的调用感到厌烦,但我们还是设法达到了那里。目前,基于 DynamoDB 的媒体服务管理着 930 亿个项目,以每天约 9000 万项目的速度增长,展现出非常令人印象深刻的性能和可靠性。在 Canva 向规模化发展的过程中,重要的是要谨慎投资于架构内提供的抽象化。我们努力使这些抽象化变得强大、一致且可组合。我们将这种关注和谨慎延续到开发强大的 API,使任何人都能在 Canva 上构建功能。
我们提供 Apps SDK,这使得可以构建嵌入到 Canva 中的功能。通过 Connect API,您可以将 Canva 的功能集成到您的平台中。目前,来自 120 多个国家的数千名开发者正在 Canva 上进行开发,贡献了一个活跃的市场,拥有 300 多个(并且还在增加)应用程序。这些应用程序已被用户使用超过 10 亿次。
我最喜欢的一个是 Crikey 3D 动画应用程序。在 Canva 发布后的第一个月,Crikey 在试用的 Canva 用户中达到了 10%的转化率。感谢您让我介绍 Canva 工程进展的一部分。我们的创始人 Mel 希望将 Canva 带给世界上 82 亿人的手中。她常说"还只是 1%的路程"。不过,Mel 我很抱歉,目前有 2.2 亿活跃用户,根据我的计算,实际上已经达到了 2.72%。我非常期待未来 10 年以及更远的未来会有什么样的发展。我也希望大家能加入这段旅程。
系统的演变与复杂性管理
谢谢你,Brandon。这是一个很棒的故事,对吧?但是,如果我们看看背后的情况,就会发现许多我们经历的进化过程。他们拥有出色且相对简单的应用程序,但为了向客户提供更多功能,后台变得越来越复杂。我认为这已经是我们所有人都在经历的事情。
进化是根本性的。达尔文甚至已经理解,通往复杂生物的唯一途径是通过无数连续的小修正。而赫拉克利特在公元前 500 年之前就留下了"变化是永恒存在的"这句名言。认为我们可以停止世界,创造一个不变的状态的想法,只是一种幻想。现实世界始终在不断变化,数字系统也受到其影响。赫拉克利特还留下了"世界在不断流动,无法两次踏入同一条河流"这句名言。在我们的环境、需求和实施中,变化是不断发生的。
实际上,这并不是一个新的讨论。在 1960 年代,Lehman 提出了一系列关于软件演化的法则。这与分布式系统、云计算以及现代系统构建方法无关。因为他当时考虑的是大型机,但这些都是软件的法则。没有演化的软件将变得毫无意义。如果没有新功能,客户会觉得我们的系统质量在下降。因此,我们的数字系统始终面临着演化的必要性。
因此,为了帮助我们,我总结了在亚马逊学到的六个基本教训。希望这些能在考虑如何管理环境以防止复杂性失控时提供参考。然而,保持简单性需要从第一天起就有纪律。从开始设计的那一刻起,就需要开始考虑这一点。第一天可能很简单,但随着时间的推移,它会变得复杂。
以进化能力为要求的重要性
第一的教训是,像往常一样,最重要的事情。将可进化性作为一个要求。系统需要意识到,随着时间的推移,它可能需要重新审视之前做出的设计选择。我们在亚马逊 S3 初期做得非常好的一件事是,我们知道一年后或两年后不会使用相同的架构。在早期,我们认为每当规模变化一个数量级时,都需要重新审视架构。
为了应对复杂性,构建能够以可控方式进化的架构是至关重要的。我将 Evolvability 定义为软件系统在未来变更中能够轻松应对的能力。这与 Maintainability(可维护性)不同。Evolvability 指的是长期的、粗粒度的、根本性的功能和结构扩展。另一方面,Maintainability 指的是细粒度的、短期的局部变更。这些变更通常是修正
在构建可进化的系统时,我们学到了几个重要的教训。通过用细致的接口对商业概念和内部细节进行建模,可以构建聚焦的组件。然后,通过构建智能端点、利用分散化,使组件能够独立部署,从而可以单独进化这些部分。高可进化性、高可观察性以及对多种范式的支持,使我们在实际实施系统的不同部分时具备灵活性。
我经常谈论 Amazon S3,但在开头的视频中,S3 也发挥了重要作用。因为我认为这是一个很好的例子,说明简单的服务随着时间的推移在内部变得更加复杂,同时仍然保持对客户的简单性。看看 S3,这难道不是令人惊讶的事情吗?这是 18 年前,云存储的演变从简单的 API 开始的时候。最初,我们专注于耐用性、可用性和成本效益,而这些最初的原则成为了后续所有创新的基础。部分创新是由客户推动的,许多情况下需要大规模的重写,但在系统演变的过程中,仍然保持了高可用性和耐用性。
有人给我讲了一个很好的比喻。S3 的演变就像是从单发引擎的塞斯纳开始,过渡到 737,最终发展到 380 的整个机队。在此过程中,迁移是在客户未察觉的情况下进行的,同时进行空中加油。S3 的构建正是如此。每年,我们在不影响客户所提供功能的情况下,添加新功能。通过实现强一致性,我们不再需要妥协我们的核心属性。模块化使得在像 Rust 这样的语言中进行实验成为可能。
从一开始,就有针对微服务架构复杂性的明确战略。看到底层微服务的演变,实在令人惊叹。第一天从 6 个微服务开始,现在已经增长到 300 多个。我们都在不知不觉中使用 S3,而这背后的复杂性却在大幅增长。
这不仅仅是因为完全的控制使软件更容易进化。我们真正需要改变的是硬件基础设施。网络设备通常将所有功能集成在 ASIC 中,每次更改都需要更换整个网络设备。这在 2006 年是常识,但我们的客户始终在重新配置网络,而当时的网络设备并不适合这一点。我们知道 2006 年提供的网络功能在 2010 年和 2020 年会有很大不同,因此需要建立一个进化的基础。因此,我们创建了一个名为 Blackfoot 的创新基础。这基本上是一个配备大量线路卡的设备,最初将 Linux 内核放在一旁,但随着时间的推移,使网络的进化成为可能。
Amazon CloudWatch 的演变与复杂性的分割
最初的设备提供每秒 10 吉比特的线路速度,但现在已经发展到数百吉比特,同时网络安全也得到了提升。这样,像主机一样,建立了进化的基础。
在某个时刻,我们意识到我们主机的构建方式并不是进化的合适平台。像 AWS Nitro System 一样,将功能分离到带卡的独立盒子中,对于主机系统的进化至关重要。在早期,处理器变得非常快速,虚拟机的根 I/O 虚拟化成为了障碍。通过将网络移出盒子,我们能够在保持对完整线路速度的访问的同时继续进行虚拟化。实现了线路速度下的加密,并能够构建用于访问 EBS 的 PCIe 接口。
Evolvability 是一个有意识的决策。需要创造一个可以进化的环境。作为管理复杂性的前提条件,必须将 Evolvability 作为要求。我喜欢这个比喻来说明复杂性是如何随着时间增加的:把青蛙放入沸水中,它会立刻意识到不对劲并跳出来。然而,如果把青蛙放入冷水中,然后慢慢加热,青蛙会继续感到舒适。虽然有小的警告信号,但它们会被忽视。青蛙只是逐渐适应上升的温度。当它意识到危险时,已经太晚了。值得注意的是,在这个比喻中,青蛙并没有受到伤害。
这个教训是,不能忽视警告信号。最初的小变化看起来是可管理的,容易应对。然而,如果继续忽视警告信号,系统会变得更加复杂,管理和理解变得困难。要应对这一点,需要将复杂性拆分。一个好的例子是 Amazon CloudWatch。目前,我们都在使用 Amazon CloudWatch,它处理每天数百兆的指标和观测数据,每天接收近 0.5 艾字节的日志,是一个巨大的服务。这是 AWS 中的一个重要基础服务。
然而,一开始并不是这样的规模。早期的 Amazon CloudWatch 是一个非常简单的服务。启动时只是一个用于存储和接收指标数据的服务,后端服务也很少,由一个理解系统各个部分的小型工程师团队运营。然而,随着时间的推移,系统不断成长,前端成为新功能实现的场所。这随着时间的推移而成长,变得更加复杂,我称之为"巨型服务"的反模式出现了。巨型服务是一种产生需要拆分的复杂性的反模式。
现在,前端只处理基本功能,系统整体的复杂性分散在各个部分。我们反复使用的原则是:需要构建一个将系统分割开来、具有高内聚性、低耦合度并且拥有明确定义的 API 的组件。这就是现在的 Amazon CloudWatch 的状态,它拥有非常简单的前端服务。实际上,从最初开始就保留的代码,可能只是处理原始请求的部分,其他所有内容随着时间的推移被重写,并且新功能也不断添加。
我们必须更改系统的原因不仅仅是添加新功能。例如,我们面临的复杂性问题之一与工程有关。许多 Amazon CloudWatch 的大容量数据存储是用 C 语言编写的,但在这个层面上实际可操作的 C 程序员非常难以雇用。因此,我们开始考虑如何单独处理这些分散的组件。
选择哪种编程语言其实并不是那么重要。在这种情况下,我们开始使用 Rust 来实现这些大规模接口。将系统分解为更小的组成部分后,不仅在功能方面,而且在使用的库和编程语言方面,也可以随着时间的推移进行演变。
常见的问题之一是,在拆分系统时,各个服务应该有多大规模。一般来说,这个选择的余地并不多。团队的数量、成功的程度、客户的需求等外部因素往往决定了这一点。Amazon CloudWatch 有许多微服务,但每个服务的规模实际上依赖于其成长过程。因此,关于具体应该有多大规模,提供明确的建议是困难的。
在添加新功能时,有两个选择。可以扩展现有服务,或者创建新的微服务。扩展可以重用现有代码,因此在许多情况下可以更快地实现,但存在大型服务器反模式的风险。创建新服务可以将服务保持在易于管理的规模,但在初期阶段需要更多的努力。警告信号是,当微服务变得超出工程师理解范围时。一般来说,当无法在脑海中把握整体时,可以说该服务已经变得过于庞大。
S3 团队的组织和复杂性应对
在日常应对这些挑战的人物中,有 Andy Warfield。关于 Amazon S3 如何应对这种复杂性,Andy 本人将与我们分享,因此我想欢迎他。大家好!我是 S3 团队的工程师 Andy。今天我将谈谈组织和复杂性,以及我们如何构建团队和组织以应对复杂性。
正如 Werner 提到的,S3 已经存在了 18 年。这对我来说是令人惊讶的。这是我职业生涯中最了不起的项目------一个单一的分布式系统和一个单一的团队持续运营了 18 年的事实。我想指出一些关于组织如何运作的有趣和成功的方面,但首先让我从免责声明开始。我不想让大家觉得我在这里站着就解决了组织复杂性的问题。实际上,这个问题仍然没有解决。我们还有很多需要学习的东西,并且每天都在继续学习。系统持续进化,团队也在不断壮大。
但是,我认为当我们表现出色时,有几件事情做得很好。我认为这些是值得分享的。在谈论两件成功的事情之前,我想问大家一个问题。有没有人知道这是什么?有没有人见过这样的东西?你们可以看到前轮是没有的。这是我小时候在拖拉机拉力赛中使用的雪橇。我在加拿大的渥太华长大。离城镇大约一个小时的地方有亲戚,每年夏末,我们都会开车去魁北克省的肖乡村节。
那个节日有乡村节日的所有精彩内容。最大的蔬菜竞赛、游乐园,当然还有拖拉机拉力赛。在拖拉机拉力赛中,农民们会带来自己的拖拉机。他们将拖拉机连接到雪橇上,拉着卡车行驶。当下雨时尤其精彩,因为会变得泥泞。结合拖拉机的动力和驾驶员的技术,尽可能远地拉动卡车。然而,越是拉动雪橇,上面的桶里的重物就会使雪橇变得越来越重。
这是一个稍微有些不同的图,但这基本上是我对软件开发的思考方式。拖拉机的牵引力和大规模分布式系统的开发有着惊人的共同点。这个共同点就是,越是用力拉,越是变得沉重。在最初的日子里,当我盯着空的缓冲区时,那是充满可能性、负担最轻的最佳时刻。然而,越是用力拉,事情就越变得沉重。复杂性不断积累,凭借纯粹的力量和意志力只能走一段距离。
然而最终我们必须认识到雪橇的重量。作为一般观察,我们的组织必须意识到我们构建的软件同样复杂,并且需要同样的关注。
我想指出的第一个观察是,我认为 S3 团队做得相当不错,但成功的团队总是或多或少地抱有一种不安,担心自己所做的事情是否是错误的。他们总是在寻找那些并不完全正确的东西。当事情真的进展顺利时,担心自己是否遗漏了什么的情绪会变得最为强烈。感到一些不安,提出问题,并努力改进,这正是 S3 团队做得好的地方。
一例をご紹介しましょう。 约 6 年前,我们迎来了一个巨大的成长时期,许多新的工程师加入了团队。S3 在耐久性方面绝对不允许失败。耐久性是工程团队的核心,是我们方法的根本,但在迎接这些优秀但在这种环境中没有经验的新成员时,我们需要保持对耐久性的关注和投资。因此,我们与 Seth Oracle 讨论了如何应对这个挑战,并决定借鉴安全工程的想法。这就是威胁模型的概念。
我们采用了将安全威胁文档化并评估模型是否与防御措施一致的思路,开发了耐久性威胁模型的概念。我们建立了一个机制,让对 S3 进行更改的团队文档化预期的耐久性风险及其对策。同时,我们邀请了在团队中工作多年的少数工程师,他们在耐久性方面拥有丰富的运营和工程经验,担任耐久性审查员。我们采取了一种方法,围绕耐久性威胁模型进行富有成效的讨论,寻找漏洞和改进点,同时进行教育。
这只是一个例子。为了扩大团队,我们实施的机制可能有数百种。然而,重要的是,我们鼓励提出质疑,并有意识地挑战现状。借用 Grace Hopper 的话,如果你看到人们因为"我们一直以来都是这样做的"而做事情,那可能是一个警告信号,提醒我们要更加质疑和警惕。
作为第二个观察点,我想谈谈 Ownership 这个概念。Ownership 在 S3 中,以及在 AWS 的工程领域,甚至在整个亚马逊的制造过程中,都是一个非常有趣的特征。然而,解释这个 Ownership 真的很困难。让我们通过一个简单的练习来说明一下。请回想一下你们的经历和人生中,当你们投入到真正喜欢和珍视的事物时的情景。那时,你们是多么努力地工作,对质量是多么讲究,以及对交付的期待有多么兴奋。
请回想一下,当你因为被要求而必须去做某件事情时,也就是说,虽然无法理解其优点或成果,但因为被指示而不得不去做的事情。想想那时你付出了多少努力,想要多快完成,以及质量如何。这两者之间的区别正是 Ownership 这一特性。拥有 Ownership 的团队属于第一类。
当我看到与我一起工作的最有效的领导者时,我发现他们擅长推动所有权,并实践两件事。第一,他们在团队中建立一种代理感。代理感意味着团队感到自己拥有广泛的裁量权和支持,以真正取得成果。建立代理感的第一步不是指示团队该做什么和如何做。相反,是将挑战带给团队,解释其重要性,吸引团队参与,并最终信任他们,赋予他们所构建事物的所有权。通过赋予这种所有权,实际上可以让他们拥有想法,并庆祝他们的成功,因为那是他们的。当团队拥有代理感时,我与特别在 S3 中一起工作的有效领导者所做的第二件事是推动紧迫感。
即使是最优秀的团队,也会找到放慢速度的理由。它们会发现意想不到的事情,或者对发布产生担忧。而在发布时,总是面临决策和妥协。作为领导者,快速前进意味着确立所有权,退后一步将团队交给他们,但同时也要继续踩下油门,不放松确认,支持问题解决,让团队感受到在获得支持的同时取得成果的必要性。换句话说,所有权就是这种代理和紧迫感的结合。
我谈到的两件事是,应该感到一点恐惧,以及将所有权完全委托给两个披萨团队的水平,让他们真正拥有他们所提供的东西,并支持他们以便他们能够热爱自己的工作。谢谢你。关于 Andy,有很多精彩的故事,讲述 AWS 是如何从架构的角度以及组织的角度不断演变的。我们有很多这样的杰出工程师,Andy 也是其中之一。随着时间的推移,我希望能有更多能够给大家讲述精彩故事的杰出工程师。
基于细胞的架构管理运营的复杂性
在第 4 课中,我们将事物分解为更小的组成部分,并使我们的组织符合理想架构的形式。然而,我们已经到了不仅要构建它们,还必须运营的阶段。在亚马逊,我们是如何管理运营的复杂性的呢?我们通过一种称为基于单元架构的方法来组织应用程序。假设我们构建一个应用程序,前端有,中间有一些容器,后端有数据存储。当成功开始时,事物开始增长,任何运营上的障碍都会影响所有客户。
当性能受到影响、特定客户施加负载或某处发生故障时,整个服务器会宕机,所有客户都会受到影响。在这种架构中,管理这一点非常复杂。因此,需要再次将事物分解为独立运行的小构件。这里的真正目的是缩小影响范围。这在像基于单元的架构这样复杂的系统中是不可或缺的。将系统划分为独立的孤立单元,选择确定性算法(通常是哈希函数),将客户映射到特定单元中,这些单元在复杂系统中创造秩序。
Cell 是将特定单元的问题隔离开来,以防止对其他单元的影响。要实现这一点,需要一个简单的 Router。这个 Router 非常简单,只需具备将请求转发到适当 Cell 的功能即可。顺便提一下,有一种非常优秀的算法可以将客户分配到 Cell 中,称为 Shuffle Sharding,AWS Builder's Library 中有一篇很棒的文章,感兴趣的朋友可以去看看。这是一种最大化整体可用性的独特方法。至于 Cell 的外观,这取决于最初的起点,可能会有所不同。对于区域服务,Cell 仍然保持区域单位,只是成为更小的组成部分。对于基于区域的服务,将会存在更多的 Cell,但为了更好地管理复杂性,仍然会被分解成更小的块。
那么,AWS 的各种服务是如何使用 Cell 的呢?是用什么样的 ID 进行路由的呢?Amazon CloudFront 使用 Distribution ID,Amazon Route 53 使用 Hosted Zone ID,而作为后端的 Hyperplane,如 Amazon EC2 和 Load Balancer,则使用 Customer ID。这实际上是一个很好的默认选项,前提是可用。此外,为了管理不同的 Cell,Control Plane 也需要进行一些开发。当新客户入驻时,可以创建新的 Cell 或将其分散到现有的 Cell 中。
这些都是可以控制的,和服务一样。与服务的规模应该达到什么程度的问题类似,Cell 的规模应该达到什么程度的问题也存在。Cell 需要足够大,以处理预期的最大工作负载,但同时也需要足够小,以便能够在全规模工作负载下进行测试。
正确答案可能在中间。主要原因是希望利用规模效益。服务越大,出现问题时对更多客户的影响可能性就越高,而越小则规模效益减少。也就是说,像往常一样,保持平衡是重要的。引入基于单元的架构时,需要管理这些,因此复杂性增加。
这是需要事先进行的,但需要记住,系统的构建时间与其运行时间相比是非常短的。因此,提前投资于管理的便利性与对安全性和成本管理的提前投资同样重要。对 Cell 的分解有助于随着时间的推移维持客户的可靠性和安全性。AWS Well-Architected Framework 中有关于此的精彩文章,详细说明了各种细节。如果您想构建基于 Cell 的架构,强烈建议您阅读这篇文章。
消除系统中的不确定性
随着系统变得复杂,需要抑制问题发生的影响,因此单元的整理非常重要。此外,我们学到的另一个教训是,需要消除系统中的不确定性。因为不确定性非常难以处理。因此,需要在设计系统时牢记减轻复杂性。例如,假设我们被委托设计一个像超平面这样的设置系统。我们有许多负载均衡器,特别是在有数百万客户的情况下,客户不断地更改负载均衡器的设置。这是一个持续发生的事情。
一般的的做法是将设置更改保存到数据库,通过 Amazon SQS 队列发送事件,启动 AWS Lambda 函数以重新配置负载均衡器,这是一种事件驱动的架构。然而,如果这种情况持续发生,就无法预测负载均衡器需要处理多少重新配置负载。这里有一个意外的发展 - 我们并没有采用这种方法。因为在负载均衡器上的处理变得完全不可预测。
我们采用的是一种更简单的方法。所有的更改都写入文件并保存到 Amazon S3,负载均衡器以固定的循环每几秒从 S3 获取新的配置。该文件包含固定数量的条目,并始终处理所有条目。因此,重新配置是完全可预测的。这是利用简单性构建高度可预测系统的一个例子。乍一看,这似乎不是魔法般的架构变化,但简单性有助于实现可预测的处理。我们称之为"Constant Work"模式。
通过定期从 Amazon S3 获取文件,可以避免尖峰、积压和瓶颈,并且由于 Amazon S3 无与伦比的可用性,自然也可以实现自我修复。接下来,我们将介绍另一个利用常量工作的服务示例。它是 Amazon Route 53 和健康检查器。我们有一个大型健康检查器集群,用于检查节点是否可用。健康检查器不是在每次有变化时进行推送,而是定期将所有负责节点的完整配置文件推送到聚合器。
聚合器将所有这些请求合并成一个更大的表,并将其推送到 Amazon Route 53。Route 53 接收到该表后不会做任何事情。即使每隔几秒就会收到表格,也不会发生任何事情,直到 DNS 请求到达并解析为 IP 地址集。在那时,将使用该表检查主机是否可用。该系统中的处理不再由事件驱动,而是高度可预测,因此复杂性消失,保持了简单性。
然而,实现这种有意的简单性需要纪律。必须设计一个可预测的系统,这主要是为了减轻不确定性的影响。关于管理复杂性的最后一课是自动化复杂性。这在管理复杂的大规模系统时是必不可少的。AWS 利用自动化来实现耐久性、容量,实际上也用于构建新的区域,人类的参与几乎没有。如前所述的网络示例所示,客户不断重新配置网络功能,因此我们需要完全自动化这一过程。
复杂性的自动化和安全性措施
那么,应该自动化什么呢?正确的问题是,什么不应该自动化?我们需要明确只有人类真正需要介入的判断。自动化应该是标准,人类介入应该是例外。手动输入应该仅在真正需要人类判断的领域中使用。作为一种常见的方法,为了防止错误,一个人输入命令,另一个人确认其过程。
我们进行大量自动化和自动处理的领域之一是安全。AWS 中,每个人都承担着安全工作的责任。为了保护客户,我们必须将安全放在首位,因此我们每个人都是安全工程师。通过从一开始就设计安全,而不是事后添加,我们在服务创建时就将安全性融入其中。安全团队不仅支持安全服务的构建,还特别开发与自动化威胁情报相关的技术。
我们在全球拥有庞大的网络,亲眼目睹了变化的影响。我们每天处理字面上兆级的 DNS 请求,轻松识别出每天超过 10 万个恶意域名。这一切都是通过自动化的过程实现的。神经网络处理这些请求,并自动检测出恶意请求。这些信息会自动流入 GuardDuty,保护我们的客户。如果手动进行这些操作,将会不堪重负。通过这里的自动化,我们能够整理不断增加的数据,并将这些情报提供给 GuardDuty。
我在安全团队中看到的另一个领域是我们所有人都在关注的。我们每个人都有支持票,而这些支持票大多数实际上是预期由人类处理的。然而,我认为通过使用新技术,我们可以实现对这些支持票的自动处理。
为了自动化这个过程的一部分,可以实施 Agentic 工作流程,以减轻票据解决的复杂性。Agentic 工作流程是一个独立执行任务的系统。代理专注于非常明确和有限的用例,具备制定计划、迭代和使用工具的能力,以自动化这个过程。
这是我们如何使用 Agentic 票务分流系统的一个例子。代理的目标是高效地解决支持票务,这个过程包括读取票务、使用工具和反复操作。根据分析对票务进行分类、优先级排序,并判断应采取的行动。决定是自动解决还是升级以供人工确认。
您有多少次打开票证并查看需要确认的各种信息?数据库在这里,客户信息在那边,等等。当需要以人类的高度判断来解决票证时,我们应该能够提供全面的整体视图。如果您对这个 Agentic 工作流程感兴趣,请查看 Clare Liguori 发布的非常有趣的 GitHub 存储库。它提供了构建这种无服务器提示链的优秀基础。此外,还有关于使用 Amazon Bedrock 创建无服务器和 Agentic 工作流程的优秀自学课程。
这使我们能够专注于问题的自动解决。这真是太棒了。因为,特别是像票务解决这样的许多任务,老实说是无聊的。绝对必要,但如果可以的话,想要避免的麻烦工作。越早能够用高级判断判断这是完美的就越好。也就是说,自动化复杂性,自动化所有不需要高级判断的东西。
Simplexity 的教训与 AWS 英雄的介绍
这就是我在亚马逊 20 年的经历,以及我对许多教训的思考,特别是关于复杂性的教训。这些是最重要的教训。如果你听听亚马逊的杰出工程师的意见,应该能获得更多的教训。我们每个人都有关于这方面的伤痕,并且在艰难中学习。通过 Simplexity,我们可以安全地扩展系统,使其随着时间的推移变得更加复杂。因此,我们将可进化性作为要求,分解复杂性,使组织与架构相匹配,分割成单元,设计可预测的系统,并自动化复杂性。
我在这里分享了自己的教训,但大家也一定有独特的教训。我想赞扬在场的英雄们。因为英雄们是一个与世界各地的人们持续分享教训并建立社区的群体。来自 53 个国家的 257 位英雄在这里。他们是支持其他人成功的实践者。请倾听他们的故事,与他们交流。他们拥有在构建您自己系统时非常有价值的教训。
他们分享教训,但利用技术做了很多善事,特别是致力于解决全球食品浪费问题的公司是 Too Good To Go。我们想邀请工程副总裁罗伯特·克里斯滕森来谈谈他们的进展。
Too Good To Go 的成长与技术进化
大家,有谁曾经把还能吃的食物扔进垃圾桶,请举手。你并不孤单。你知道全球生产的食物有 40%被浪费吗?这就像在你最喜欢的披萨店里,从盒子里拿出四片披萨,掉在地上,然后就让它腐烂而离开。食品浪费是
食品浪费占全球温室气体排放量的 10%,是航空产业总排放量的 4 倍。在我们面临的环境问题中,食品浪费无疑是最愚蠢的问题。Too Good To Go 于 2015 年在哥本哈根成立,旨在为餐厅中被浪费的大量食品寻找解决方案的年轻企业家们创立。他们知道,人们宁愿以折扣价购买食品,也不愿意让食品被浪费。
这个简单的想法成长为世界上最大的剩余食品市场。在推出后的头几周内,数百家公司将剩余食品上传到市场应用程序,成千上万的用户收到了滞销剩余食品的组合,创造了对地球和钱包都有利的双赢关系。我们的创始人是怀着坚定信念和聪明想法开始创业的企业家,但显然并不是开发者。当时应用程序的技术基础并不算最坚固,随着快速增长出现了问题。
2018 年,注册用户数突破 100 万时,平台开始面临极限。当时的应用程序是典型的使用单个 MySQL 实例的 PHP 应用程序。系统几乎每天都处于过载状态。我们并不希望像造宇宙飞船那样。我们只有 10 名工程师,目标只是保持简单并进行扩展。那么我们做了什么呢?首先,从触手可及的简单选择开始。通过利用 Amazon Aurora 支持最多 15 个读节点的功能,我们能够确保相当大的余地。我们确定了用例,并在这些实例上使用了单独的连接池。
接下来,我决定将应用程序重写为 Spring 应用程序。我们逐个迁移端点。通过这样做,我们能够利用资源管理、连接池、缓存、消息队列的集成和处理等优势。我们不需要制造宇宙飞船。只需组合现有组件并进行一些定制,特别是在处理大量流量的结果一致性端点上,我们就能够实现可扩展和高性能的解决方案。在一百万用户的时点上接近极限,但通过这些小的改进,我们在服务启动仅四年内就达到了 1000 万注册用户。
用户数量的增长持续加速,同时业务方面也在寻求新功能,这带来了新的挑战。当开发者面临类似的扩展需求时,我们希望能够从中央工具箱中进行选择。我们记得想要保持简单。因此,我们采用了固定语言和框架的集中式架构。事务处理、缓存、消息传递、安全性、后台处理等常见模式设定了要求,并在所有服务中统一。这些情况下通过固定语言和架构,技术复杂性得以降低,工程师的负担大幅减轻。
在这一点上,我们通过结果一致性的来源、缓存和亚马逊 OpenSearch 提供了大部分流量,从而保护了事务数据库免受大量读取流量的影响。这使得增长成为可能。到 2020 年,我们在 16 个市场获得了 2400 万注册用户。然而,食品浪费在地球上的任何地方都在发生。追求更大的影响意味着进一步的增长,随后我们进军了第一个新大陆------北美。2020 年,我们面临不同的合作伙伴、不同的习惯和不同的时区。我们希望从第一天起就提供低延迟的卓越客户体验,致力于本地服务,但不想在美国构建新的应用程序或基础设施。请记住保持简单。由于利用了 AWS,我们能够利用全球范围内的区域,在北美进行本地处理。
然而,在多个区域进行部署意味着什么呢?我们希望在扩展现有内容的同时保持简单性,但这绝不是一件容易的事情。我们采用了一种方法,将客户和合作伙伴的交易固定在他们的本地区域,并分散处理大部分流量的数据。这需要集成 Amazon SNS 主题和 Amazon SQS 队列,以便在本地数据更新时将更新分发到其他区域,从而创造出开发者可以使用的新模式。
这项机制运作良好,但需要付出巨大的努力,随着地区数量的增加,情况会变得如何可想而知。今年,我们利用这一机制,继续在北美各地展开,包括凤凰城、底特律、克利夫兰等。我们使用相同的蓝图,这次在另一个大陆澳大利亚也能以最小的努力展开。目前,我们已经拥有超过 1 亿的注册用户和 175,000 个活跃的商业伙伴,形成了一个充满活力的社区。
到目前为止,Too Good To Go 已经防止了超过 4 亿份食品浪费,但我们希望产生更大的影响。今年,我们推出了新产品 Too Good To Go Parcel。通过让人们能够购买接近保质期的食品,我们可以防止生产阶段的食品浪费。对于初创公司来说,增长是理想的,但从技术角度来看,这可能是一条波折的道路。有一点是确定的,那就是 Too Good To Go 的食品救助必须保持简单。因为目前我们每秒拯救 4 份食品,但在同一秒钟内有 8 万份食品被浪费。我们地球的健康取决于此,因此我们面前有巨大的潜力。最后,我想问一个问题:您将如何减少食品浪费?谢谢。
Amazon Aurora DSQL 与同步时间的重要性
真棒呢。听到像 Robert 和 Too Good To Go 这样的客户的故事我非常喜欢。他出色地表达了在面对增长、扩展以及跨多个地区的数据处理时的复杂性。在观察这些企业及其挑战的过程中,我们在思考应该构建什么样的技术来简化这一切。当考虑到复杂性的负担时,我们希望通过构建简单的系统,能够承担更多客户所面临的复杂性负担。
目标是,例如尽可能简化处理数据库的客户体验。然而,随着时间的推移,复杂性的负担逐渐显现,回想起最初开始使用 AWS 时,客户可能自己在运用数据库,或者像现在常见的那样,将本地环境的架构提升并迁移到云端,在 Amazon EC2 上运行自己的数据库,并在其他可用区放置只读副本。复杂性的负担确实在客户一方。作为第一步,我们决定使用 Amazon RDS 来管理这些数据库。这大大减轻了运营的复杂性。补丁应用、升级、备份、读取扩展等变得不再必要,同时也能有效应对维持高可用性所带来的复杂性,恢复过程也得到了简化。
然而,在 Amazon RDS 中仍然运行着标准数据库。老实说,这并不是进行数据库工作创新的良好基础。因为大多数数据库都是作为一个难以更改的大型单体构建的。我们首先着手的工作是通过将计算与存储分离来构建 Amazon Aurora。这使得日志成为数据库,从而能够更快地推进。通过将存储与引擎分离,我们能够在不担心存储的情况下,更加无缝地进行扩展。
我们通过像 Serverless 和 Aurora Limitless 这样的创新技术,实现了从零到规模缩减的目标。周二,Meta 发布了 Amazon Aurora DSQL,这是 Aurora 的下一代产品。DSQL 使得能够提供服务给全球客户的应用程序成为可能,就像 Too Good To Go 所面临的那样。如果他们能够利用 DSQL,他们的架构会有所不同吗?DSQL 可以实现无中断的故障转移,并优化性能和合规性的两者的数据配置。它提供了构建具备为数亿客户提供服务所需的恢复力的全球分布式应用程序的工具。
我不打算详细解释 DSQL 的所有功能,但我想以 DSQL 为例,说明如何将之前提到的教训应用于 DSQL 的设计阶段,以管理复杂性,并为随着时间的推移而演变提供适当的基础。这是一个由分散且具有恢复力的独立组件构成的层次结构,使用了之前解释的相同原则。通过将系统分解为更小的构建块,每个构建块具有针对特定任务的高度内聚性和低耦合度,并通过明确定义的 API 进行通信,从而使各个组件能够独立扩展。
通过这一点,可以对系统进行细致的控制,并根据各个组件的需求进行单独扩展。此外,还可以根据各个组件的特定要求定制安全性。查看 DSQL 的主要构建模块,包括前端、负责大部分 SQL 处理的查询处理器、调整事务提交位置的裁决者、提供短期存储的日志,以及将这些内容合并到长期存储中的交叉栏。
这些组件以不同的方式进行扩展。查询处理器根据会话数量进行扩展,裁决者根据事务数量进行扩展,日志根据这些存储系统实际处理的吞吐量进行扩展,交叉开关根据日志的数量和数据库的大小进行扩展。如果将这些组件像传统数据库那样合并成一个大的单体,就必须根据其中最大扩展的组件来扩展所有组件,这将非常低效且难以管理。现在的做法允许组件独立演化。
让我们从简单的读取事务开始,看看这些组件是如何协同工作的。想象一下,一个用户正在这里拉斯维加斯的本地餐厅订购披萨。大家都写过的 SQL 查询:搜索评分在 4 以上的餐厅的 Select 语句。这时,后台发生了什么呢?应用程序连接到前端,并分配 Query Processor。Query Processor 是为每个会话构建并分配的非常小的独立组件。当 SQL 到达时,它会在事务开始时获取时间戳,读取本地时钟,然后发送请求。在此之前,它会参考称为 Shard Map 的内容。
在存储中,数据实际上是根据数据库键进行分片的,因此需要查看分片映射以了解哪个存储引擎持有这些数据。这是读取操作,因此不需要通过事务路径,可以直接访问存储以获取数据。
Amazon Aurora DSQL 的存储与一般数据库存储不同。普通数据库存储获取包含数据的页面,而 DSQL 的存储具有理解数据库的特性。也就是说,由于可以执行过滤等操作,当请求存储引擎中特定行时,它只会获取该特定行或行集,而不是包含所有所需数据的完整页面。这解决了传统数据库中的最大瓶颈之一。
结果返回并显示给用户后,用户决定购买披萨。这被称为交互式事务。如果您是 Amazon Aurora DSQL 的开发者,您可能对交互式事务很熟悉。您将开始事务,获取数据,在客户端进行操作,做出判断,编写 SQL,然后返回给客户,最终提交。这与 Amazon DynamoDB 的事务有很大不同,后者需要一次性完成所有工作。查询会变得更长,您需要选择餐厅,从菜单中选择项目,进行订单,然后执行提交。
SQL 整体作为一个包发送到查询处理器,在那里一切都被整合。DSQL 中,查询处理器作为使用快照隔离的持有库,能够在本地内存上执行数据的读取和写入。它会等待在提交时将完整的事务发送给裁决者。所有简单的 SQL 语句都在查询处理器存在的一个区域内处理,区域之间的交互仅在裁决者在不同区域执行时的提交时发生。这意味着读取、写入和更新的速度与单一区域数据库一样快。
Query Processor 在小型 Firecracker VM 内运行,并且独特之处在于它被部署在裸金属的 Query Processor 主机上。可以在裸金属主机上的 microVM 内运行数千个 Query Processor。当客户在等待判断和承诺时,如果客户端与 Query Processor 之间的连接处于休眠状态,可以暂停这个 microVM。当客户重新开始操作时,可以在毫秒级别恢复 microVM 的快照,并且它经过优化以支持长时间运行的事务。
在查询处理器中,采用了快照隔离。这使得每个事务可以对其开始时刻的数据库一致性快照进行操作。通过这种机制,读取操作可以在不阻塞写入操作的情况下进行,反之亦然。当事务开始并进入 SQL 执行阶段时,这个一致性的快照将变得可见,即使发生了插入或更新等写入操作,也不会立即应用于存储。
相应地,这些写入将被本地缓冲到专用于事务的工作区。这实现了虚拟写入功能,在同一事务内的后续读取中,可以查看所有待处理的更改。事务包括 Write Set 和完整的 Post Image(所有更新应用后数据库中行的状态)。这个 Write Set 和 Post Image 将被发送给裁决者。
Adjudicator 位于 SQL 的写入路径中,判断事务的提交可否。其角色是检测和解决事务之间的竞争,确保写入的一致性。查询处理器创建一个包含所有判断所需信息的有效负载并发送给 Adjudicator。该有效负载包括由事务修改的所有项的写入集、受事务影响的所有表行的副本的后图像集,以及对事务的提交或中止判断至关重要的事务开始时间。
让我们看看几乎同时发送到 Adjudicator 的两个并行事务。如果事务 A 比事务 B 稍早发送了提交,Adjudicator 将检查事务开始时间和写入集,并与查询处理器的有效负载进行比较。Adjudicator 会检查在 T start 之后提议的写入中,是否有对重复或匹配键的索引表行的写入。如果发现两个事务都试图更新由键 C 标识的行,则不能同时更改该行。因此,事务 A 可以提交,但事务 B 必须中止。如果事务的写入集没有交叉,Adjudicator 将允许提交,并为事务分配提交时间戳。
在传统数据库中,持久性是在存储层实现的。事务在被永久写入存储层时被视为已提交,并需要处理故障发生后的已提交事务的恢复,并将内存中的数据与存储进行同步以进行恢复。我们决定简化这一过程。在 Amazon Aurora DSQL 中,Journal 负责短期持久性。事务在写入 Journal 时被视为已提交,Journal 可以根据各个存储引擎的吞吐能力进行水平扩展。这使得在 Journal 中创建了已提交事务的完整有序流,而这种顺序在维持系统整体一致性方面发挥着重要作用。随后,Crossbar 开始从 Journal 中提取事务,并将其移动到存储引擎。
在这里,审查人员可以转到期刊,并向客户发送确认响应,以表明交易已完成。该系统的每个组件独立运行,这是遵循管理复杂性原则的架构的一个很好的例子。我们在这里简单提到过,但请记住这两个概念:start 和 commit。这是一个在保持不同组件之间简单协作的同时处理复杂性的绝佳例子。这些 start 和 commit 的时间戳读取本地时钟,但在分布式系统中使用时钟几乎是不可能的。时间的概念在我们的生活中是基本的。
大家都知道我在 8 点 30 分开始,现在也在看着钟表,想着什么时候结束吧。我们大多数人都戴着手表。对我们来说,多少有些准确的时间就足够了,大致的时间也没问题。
在分散系统中使用时间一直被认为是不可能的。为了实现所有这些由于无法使用时间而需要的事情,例如创建一致性、进行领导者选举和执行两阶段提交,随着时间的推移,惊人的优秀算法不断被开发出来。在 70 年代末,Leslie Lamport 写了一篇非常著名的论文------他因这项研究获得了图灵奖。《分布式系统中的时间、时钟和事件排序》基本上指出无法使用时间,并解释了由于实际上无法使用时间而必须考虑的其他各种功能。
分散锁、向量时钟、由于无法使用时间而发明的所有这些机制,几年后发生了非常有趣的事情。1991 年,同样是图灵奖得主的芭芭拉·利斯科夫(Barbara Liskov)写了一篇关于同步时钟的实用使用方法的论文。她开始解释所有这些算法,如果她能够访问时钟,就可以构建得简单得多。她还写道,这些分散时钟和同步时钟在系统中可用的时间非常短。
那么,"最近"指的是 30 年后的事情。30 年后,我们终于获得了高精度的同步时钟,从而能够以更简单的方式构建系统和复杂的算法。目前,数据中心内有完全不同的基础设施,第三条主干网络,它专门用于一件事:提供准确的同步时间。实际上,所做的是通过卫星进行时间同步,并通过完全隔离的这条主干网络传输到 Nitro 上,那里使用 Amazon Time Sync Service 来获取准确的时间。
时钟的精度是微秒级别的。去年,Peter O'Brady 在主题演讲中谈到了将其改进到微秒级别的时钟,并且在每个 Amazon EC2 实例中可用。其原理如下:卫星到达原子钟,并在这个完整的第三层骨干网络上,使用 FPGA 硬件和特殊的 Nitro 卡,消除了驱动程序和网络缓冲区等。这些是一直限制网络时间协议(NTP)精度的因素,NTP 的精度无法超过几十毫秒。这些时钟的精度在 1 微秒级别。
这里需要注意的是,T 提交和 T 开始是从这个硬件时钟中获取的。也就是说,可以准确比较事务何时开始和何时提交。这是一个几乎在 AWS 以外无法实现的独特环境。坦率地说,虽然可以说这些时钟非常准确,但实际上并不存在完全准确的时钟。总是存在微小的漂移、通信差异和某处的延迟,每个获取的时间都有误差范围。因此,我们构建了一个名为 ClockBound 的系统。它是一个在 EC2 实例上运行的守护进程,可以作为在 GitHub 上公开的库使用。通过这个系统,不仅可以获得准确的当前时间,还可以获得周围潜在误差的范围。
这些错误的范围可以通过考虑最早的时间是什么,以及这是否是准确的时间来加以利用。最早时间的漂移程度是多少,以及在这一点上最晚的时间是什么,从而判断并决定采用哪个时钟。这将实现具有纳秒精度的纳秒时钟。此外,还可以获得潜在错误的范围边界。这并不坏 - 利用这个错误的范围,可以考虑某个事件发生的可能最晚时间和最早时间。
由于可以利用同步的时间,这个机制变得非常简单。这是目前在提供强一致性的 Amazon DynamoDB 全球表中使用的相同技术。由于可以访问同步的时间,可以实现全球范围内的强一致性。准确的时钟大大减少了复杂性。请查看我所研究的这些算法 - 分布式事务、冲突解决、领导者选举、两阶段提交、Paxos、Raft - 如果能够简单地使用时间,这些都应该变得更加简单。
Matt 也在周二的演示中提到,他一直在考虑提供构建下一代系统的构建模块。计算、存储、数据库、网络和安全是必需的,这些本来就是构建模块。然而现在,我们在这些基础上又增加了一个更基本的构建模块,那就是"时间"。我希望大家能重新审视在应用程序中构建的算法和机制,考虑同步时间是否能大幅降低系统的复杂性。
今后 1 年的努力:支持全球难题的解决
以上就是我今天想要传达的内容。感谢大家的聆听。接下来,我想引起大家对我在接下来一年中打算努力的事情的关注。看过《Now Go Build》电视系列的人可能知道,我对年轻企业和组织致力于解决全球难题充满热情。根据联合国的可持续发展目标,到 2050 年,全球人口预计将增加 20 亿人。他们的食物该如何解决?医疗该如何提供?如何确保经济可持续的未来?
有许多年轻的企业和组织正在努力解决这些问题,我真的希望能支持他们。作为这一努力的一部分,我成立了一个名为 Tech Rescue 的组织。我们拥有一个为变革者提供的 AI 项目的团体系统,并为首席技术官设立了奖学金。我的团队正在支持他们的技术能力提升,并非常感谢那些为这些年轻企业提供如何利用技术造福世界的建议的英雄们。
根据这些组织的反馈,技术人员的数量非常少,有时只需要 2 周或 1 个月的时间就能提供很大的帮助。如果大家希望在自己的工作中做一些好的事情,并希望利用技术能力真正产生影响,请关注这些组织。请提供你们的时间。利用你们在构建大规模系统方面的经验,可以为这个世界带来真正的变化。作为技术人员,我们不仅仅是创造美丽的事物,利用技术解决世界上的难题也是我们的责任。
那么,严肃的话就到此为止。今晚我打算在这个派对上度过。期待在 Las Vegas Festival Grounds 见到大家。今晚 Weezer 和 Zedd 将在 Las Vegas Festival Grounds 演出。那我们在那里见吧。派对时间到了!