S3 的简洁性是基本要求
S3 的简洁性是基本要求
2025年3月14日 • 3851 字
几个月前在 re:Invent 大会上,我谈到了Simplexity – 系统最初往往很简单,但随着它们处理客户反馈、修复 bug 和添加功能,会逐渐变得复杂。在 Amazon,我们花费了数十年的时间来抽象工程复杂性,以便我们的构建者可以专注于最重要的事情:他们独特的业务逻辑。可能没有比 S3 更好的例子来展示这一过程了。
今天,在 Pi Day (S3 的 19 岁生日) ,我将分享来自 S3 的 VP 兼 Distinguished Engineer 的 Andy Warfield 的一篇文章。Andy 将带领我们了解 S3 从简单的对象存储到复杂的数据平台的演变,并说明客户反馈如何影响该服务的每个方面。这是一个引人入胜的视角,展示了我们如何在系统扩展到处理数千亿个对象时仍保持简洁性。
我希望你喜欢阅读这篇文章,就像我一样。
–W
S3 的简洁性是基本要求
在 2006 年 3 月 14 日,NASA 的火星勘测轨道飞行器在从地球出发的七个月旅程后成功进入火星轨道,Linux kernel 2.6.16 发布,我正在准备一个工作面试,S3 作为第一个公共 AWS 服务发布。
回顾过去,思考事物是如何变化的,这很有趣:那次工作面试是在 University of Toronto,是我在完成博士学位并准备成为一名教授时参加的约十个大学面试中的一个。我之前在英国 Cambridge 生活了四年,致力于 hypervisors、存储和 I/O virtualization,这些技术最终将被大量用于构建云。但在那天,当我即将结束研究生生涯,开始拥有家庭和事业时,第一批外部客户对象开始进入 S3。
到我 2017 年加入 S3 团队时,S3 刚刚超过了一万亿个对象。今天,S3 在全球 36 个地区存储着数千亿个对象,并且被地球上几乎所有行业和应用领域的客户用作主要存储。今天是 Pi Day —— 也是 S3 的 19 岁生日。在其近二十年的运营中,S3 已经发展成为地球上最有趣的分布式系统之一。在我为团队工作的这段时间里,我开始将我们构建的软件、构建它的组织以及客户对 S3 的产品期望视为不可分割的整体。在这三个方面,S3 作为一个有机体不断发展和改进,并从在其之上构建的开发人员那里学习。
倾听(并响应)我们的构建者
当我大约 8 年前加入 Amazon 时,我知道 S3 被我每天使用的各种应用程序和服务所使用。我看到过来自 Netflix、Pinterest、Smugmug 和 Snowflake 等公司关于基于 S3 构建的讨论、博客文章,甚至研究论文。我真正没有意识到的是,我们的工程团队花费大量时间与使用 S3 构建的客户的工程师交谈,以及外部构建者对我们优先考虑的功能有多大的影响。我们所做的一切,当然包括我们推出的大部分最受欢迎的功能,都是对 S3 客户请求的直接回应。过去一年,S3 推出了一些非常有趣的功能,例如 S3 Tables——我将在稍后详细讨论——但对我而言,我认为对整个团队而言,我们最有价值的发布是一些事情,例如 consistency、conditional operations 和 increasing per-account bucket limits。这些事情非常重要,因为它们消除了限制,并且实际上使 S3 更简单。
保持简洁性这个想法非常重要,而且在构建和运营 S3 的近 20 年中,我们的想法也在不断发展。很多人将“简单”一词与 API 本身联系起来——一个基于 HTTP 的不可变对象存储系统,具有四个核心动词(PUT、GET、DELETE 和 LIST),很容易理解。但是,看看我们的 API 如何响应构建者今天在 S3 上所做的各种各样的事情而发展,我不确定这是否是我们真正用“简单”来描述的 S3 的方面。相反,我们开始认为使 S3 简单是一件非常棘手的问题——我们希望 S3 专注于处理你的数据,而不必考虑其他任何事情。当我们的系统某些方面需要开发人员付出额外努力时,缺乏简单性会分散他们的注意力并浪费他们的时间。在存储服务中,这些干扰有很多形式——S3 简单性可能最重要的一个方面是弹性。在 S3 上,你永远不必预先配置容量或性能,也不必担心空间不足。开发人员认为理所当然的属性背后有很多工作:弹性扩展、非常高的持久性和可用性,只有当这些事情可以被认为是理所当然的时候,我们才能成功,因为这意味着它们不会分散注意力。
当我们将 S3 迁移到强一致性模型时,客户的反应比我们任何人预期的都要强烈(我认为我们认为人们会非常高兴!)。我们知道它会很受欢迎,但在一次又一次的会议中,构建者谈论删除代码并简化他们的系统。在过去的一年里,当我们开始推出 conditional operations 时,我们也得到了非常相似的反应。
作为 S3 团队的工程师,我最喜欢的事情之一是有机会了解我们的客户构建的系统。我特别喜欢了解直接在 S3 上构建 databases、file systems 和其他 infrastructure services 的初创公司,因为通常是这些客户在有趣的新领域经历了早期增长,并且对我们如何改进有深刻的见解。这些客户也是我们新 S3 功能一发布就最热衷的消费者(当然不是唯一的)。我最近与 Turbopuffer 的 CEO Simon Hørup Eskildsen 聊天——Turbopuffer 是一个构建在 S3 之上的设计精美的 serverless vector database ——他提到他有一个脚本可以监控并每小时向他发送有关 S3 “What’s new”文章的通知。我见过其他例子,客户猜测他们希望 S3 推出的新 API,并且有脚本在后台运行,多年来一直探测它们!当我们推出引入新的 REST 动词的新功能时,我们通常会有一个仪表板来报告对其的请求调用频率,而且通常情况下,团队会惊讶地发现仪表板一启动就开始发布流量,甚至在功能推出之前,他们发现这正是这些客户的探测,猜测新功能。
我们在去年 re:Invent 大会上发布的 bucket limit 公告是构建者感到兴奋的一个类似例子,尽管它并不引人注目。从历史上看,S3 中每个帐户的 bucket 数量限制为 100 个,事后看来这有点奇怪。我们疯狂地专注于扩展对象和容量计数,对单个 bucket 的对象数量或容量没有任何限制,但从未真正担心客户扩展到大量的 bucket。但是近年来,客户开始将其称为一个明显的缺点,我们开始注意到人们对 bucket 和对象的看法之间存在有趣的差异。对象是一个 programmatic 构造:通常由其他软件创建、访问并最终完全删除。但是,对 bucket 总数的低限制使它们成为一个非常 human 的构造:通常是由一个人在控制台或 CLI 中创建 bucket,并且通常是由一个人跟踪组织中使用的所有 bucket。客户告诉我们的是,他们喜欢 bucket 抽象,因为它是一种对对象进行分组、将安全策略等与之关联,然后将它们视为数据集合的方式。在许多情况下,我们的客户希望使用 bucket 作为一种与他们自己的客户共享数据集的方式。他们希望 bucket 成为一种 programmatic 构造。
因此,我们齐心协力扩展了 bucket 限制,这是一个有趣的例子,说明我们的限制和明显缺点不仅仅是让客户感到沮丧的事情,而且在大规模情况下也很难解决。在 S3 中,bucket 元数据系统的工作方式与跟踪 S3 中对象元数据的更大的命名空间不同。即使在每个帐户 100 个 bucket 的限制下,我们称之为 “Metabucket” 的系统过去也已经多次重写以进行扩展。显然需要做一些工作来进一步扩展 Metabucket,以预期客户创建数百万个 bucket。但是解决这种规模还有更微妙的方面:我们必须认真思考更大数量的 bucket 名称的影响、应用程序设计中 programmatic bucket 创建的安全后果,甚至性能和 UI 问题。一个有趣的例子是,在 AWS console 中有很多地方,其他服务会弹出一个小部件,允许客户浏览他们的 S3 bucket。例如,Athena 会这样做,以允许你指定查询结果的位置。根据用例,有几种形式的此小部件,它们通过列出帐户中的所有 bucket 来填充自己,然后通常通过对每个 bucket 调用 HeadBucket
来收集其他元数据。当团队开始关注扩展时,他们创建了一个包含大量 bucket 的测试帐户,并开始测试 AWS Console 中的渲染时间——在几个地方,渲染 S3 bucket 列表可能需要花费数十分钟才能完成。当我们更广泛地关注 bucket 扩展的用户体验时,我们不得不跨数十个服务来解决此渲染问题。我们还推出了一个新分页版本的 ListBuckets
API 调用,并引入了 10K bucket 的限制,直到客户选择更高的资源限制,以便我们有一个安全措施来防止他们遇到我们在控制台渲染中看到的相同类型的问题。即使在发布后,团队也会仔细跟踪客户对 ListBuckets
调用的行为,以便我们可以主动联系,如果我们认为新限制产生了意外的影响。
性能至关重要
多年来,随着 S3 从主要用于通过相对较慢的互联网链接进行存档数据的系统发展成为更强大的系统,客户自然希望使用他们的数据做更多的事情。这创建了一个引人入胜的飞轮,性能的提高推动了对更高性能的需求,任何限制都成为另一种摩擦来源,分散了开发人员的核心工作。
我们处理性能的方法最终反映了我们对容量的理念——它必须是完全弹性的。我们认为,只要不干扰他人,任何客户都有权使用 S3 的全部性能能力。这推动我们朝着两个重要的方向发展:首先,主动思考如何帮助客户从他们的数据中获得巨大的性能,而无需像配置那样的复杂性,其次,构建复杂的自动化和安全措施,使客户可以努力工作,同时仍然与他人相处融洽。我们首先对 S3 的设计保持透明,记录从请求并行化到重试策略的所有内容,然后将这些最佳实践构建到我们的 Common Runtime (CRT) 库中。今天,我们看到单个 GPU 实例使用 CRT 来驱动每秒数百千兆位的进出 S3 的数据。
虽然我们最初的重点是吞吐量,但客户越来越要求更快地访问他们的数据。这促使我们在 2023 年推出了 S3 Express One Zone,这是我们的第一个 SSD 存储类,我们将其设计为单 AZ 产品,以最大限度地减少延迟。对性能的需求持续增长——我们有像 Anthropic 这样的机器学习客户驱动着每秒数十 TB 的数据,而娱乐公司直接从 S3 流式传输媒体。如果有什么区别的话,我预计随着客户将使用 S3 的体验拉得更接近他们的应用程序并要求我们支持越来越多的交互式工作负载,这种趋势将会加速。这是另一个例子,说明如何消除限制(在这种情况下,是性能限制)使开发人员能够专注于构建,而不是解决明显的缺点。
简单性与速度之间的张力
在过去的二十年中,对简单性的追求将我们带入了各种有趣的方向。我上面提到所有例子,从扩展 bucket 限制到增强性能,以及围绕跨区域复制、对象锁定和版本控制等功能的无数其他改进,这些都为数据保护和持久性提供了非常谨慎的安全措施。凭借 S3 演变的丰富历史,可以很容易地浏览一长串功能和改进,并谈论每个功能和改进如何使处理你的对象变得更简单。
但是现在我想对简单性做一个自我批评的观察:在我到目前为止提到的几乎每个例子中,我们为实现简单性所做的改进实际上都是针对最初不够简单的功能的改进。换句话说,我们发布的东西随着时间的推移需要变得更简单。有时我们意识到差距,有时我们稍后才会了解到。我想在这里指出的是,简单性和速度之间实际上存在一种非常重要的张力,这种张力以两种方式运行。一方面,对简单性的追求有点像“追求完美”,因为你永远无法完全实现它,因此存在过度设计和反复猜测的风险,这会阻止你发布任何东西。但另一方面,急于发布一些存在痛苦差距的东西可能会让早期客户感到沮丧,更糟糕的是,它可能会让你陷入一种需要后期简化工作的情况,而后期简化的成本更高。简单性和速度之间的这种张力是我在 S3 中看到的一些最激烈的 product discussions 的来源,我认为团队实际上在这方面做得非常谨慎。但是,当你专注于你的注意力时,你永远不会感到满意,因为你总是觉得你要么移动得太慢,要么没有保持足够高的标准。对我来说,这种悖论完美地描述了我们团队在每个产品发布时感到的焦虑。
S3 Tables:一切都是对象,但对象并非一切
人们已经在 S3 中存储 tables 超过十年了。Apache Parquet 格式于 2013 年推出,旨在有效地表示表格数据,并且已成为 S3 中各种数据集的事实上的表示形式,并且是数百万个数据湖的基础。S3 存储着 exabytes 的 parquet 数据,并且每天提供数百 petabytes 的 Parquet 数据。随着时间的推移,parquet 不断发展,以支持与 Apache Hadoop 和 Spark 等流行的分析工具的连接器,并与 Hive 集成,以允许将大量 parquet 文件合并到单个表中。
parquet 变得越受欢迎,并且分析工作负载越来越多地发展为与基于 parquet 的 tables 一起工作,与 parquet 一起工作的明显缺点就越突出。开发人员喜欢能够在 parquet 上构建数据湖,但他们想要更丰富的 table 抽象:支持更细粒度的 mutations,例如插入或更新单个行,以及通过添加或删除新列来发展 table 模式,这很难实现,尤其是在不可变对象存储之上。2017 年,最初启动了 Apache Iceberg 项目,以便在 parquet 之上定义更丰富的 table 抽象。
对象是简单且不可变的,但 tables 都不是。因此,Iceberg 引入了一个元数据层,以及一种组织表格数据的方法,这种方法真正创新地构建了一个可以由 S3 对象组成的 table 构造。它将 table 表示为一系列基于快照的更新,其中每个快照总结了来自 table 上一个版本的一系列 mutations。这种方法的结果是,小的更新不需要重写整个 table,而且该 table 实际上是版本化的。很容易前进和后退以查看旧状态,并且快照适合数据库需要原子更新多个项目的事务性 mutations。
Iceberg 和其他类似它的开源 table 格式实际上是它们自己的存储系统,但由于它们的结构是外部化的——客户代码管理 iceberg 数据和元数据对象之间的关系,并执行诸如垃圾回收之类的任务——因此出现了一些挑战。一个是小的基于快照的更新有产生大量碎片化的趋势,这会损害 table 性能,因此有必要 compact 和 garbage collect tables,以清理这种碎片化,回收已删除的空间,并帮助提高性能。另一个复杂之处在于,由于这些 tables 实际上由许多(通常是数千个)对象组成,并且使用非常特定于应用程序的模式进行访问,因此许多现有的 S3 功能(例如 Intelligent-Tiering 和跨区域复制)无法完全按预期在它们上运行。
当我们与已经开始在 Iceberg 上运行高度扩展(通常是 multi-petabyte)数据库的客户交谈时,我们听到的是,他们对与 table 数据类型而不是对象数据类型进行交互的更丰富的一组功能感到兴奋。但是,我们也听到了一些令人沮丧的事情,以及一些艰难的教训,即客户代码负责诸如 compaction、垃圾回收和 tiering 之类的事情,而我们内部对对象执行所有这些事情。这些精通 Iceberg 的客户非常明确地指出,使用 Iceberg,他们实际上正在 S3 对象之上构建他们自己的 table 原始类型,并且他们问我们为什么 S3 无法做更多的工作来使这种体验变得简单。正是这种声音促使我们真正开始探索 S3 中一流的 table 抽象,并最终促成了我们推出 S3 Tables。
构建 tables 的工作不仅仅是在 S3 之上提供“托管 Iceberg”产品。Tables 是 S3 上最受欢迎的数据类型之一,与视频、图像或 PDF 不同,它们涉及复杂的跨对象结构,并且需要支持 conditional operations、后台维护以及与其他存储级功能的集成。因此,在决定推出 S3 Tables 时,我们对 Iceberg 作为 OTF 以及它在 S3 上实现 table 抽象的方式感到兴奋,但我们希望像对待一流的 S3 构造(就像对象一样)那样来处理该抽象。我们在 2024 年 re:Invent 大会上推出的 tables 确实以几种方式将 Iceberg 与 S3 集成:首先,每个 table 都显示在其自己的 endpoint 之后,并且从策略的角度来看是一种资源——这使得通过设置 table 本身的策略(而不是构成它的单个对象)来控制和共享访问变得容易得多。其次,我们构建了 API 来帮助简化 table 创建和快照提交操作。第三,通过了解 Iceberg 如何布局对象,我们能够在内部进行性能优化以提高性能。
我们知道我们正在做出一个关于简单性和速度的决定。我们已经向自己和预览客户证明,相对于 S3 中客户管理的 Iceberg,S3 Tables 有所改进,但我们也知道我们还有很多简化和改进工作要做。自从它们推出以来的 14 周内,很高兴看到这种速度形成,因为 Tables 已经引入了对 Iceberg REST Catalog (IRC) API 的完整支持,以及在控制台中直接查询的能力。但是我们仍然有很多工作要做。
从历史上看,我们总是将 S3 称为对象存储,然后继续讨论对象的所有属性——安全性、弹性、可用性、持久性、性能——我们努力在对象 API 中交付这些属性。我认为我们从 Tables 的工作中了解到的一件事是,真正定义 S3 的是这些存储属性,而不是对象 API 本身。
客户一致的反应是,这种抽象与他们产生了共鸣——它在直观上是“S3 为对象所做的一切,但为 table 所做的一切。”我们需要努力确保 Tables 符合这种期望。确保它们本身就像对象一样,是一种简单的、通用的、面向开发人员的原始类型。
通过努力真正概括 S3 上的 table 抽象,我希望我们已经在分析引擎和更广泛的一组通用应用程序数据之间架起了一座桥梁。我们已经投资与 DuckDB 合作,以加速 Duck 中对 Iceberg 的支持,并且我预计我们将重点关注其他机会,以真正简化开发人员和表格数据之间的桥梁,例如许多以表格格式存储内部数据的应用程序,通常嵌入诸如 SQLite 之类的库式数据库。我的感觉是,当我们开始看到客户为来自 spark 等工具的直接分析使用以及与他们自己的应用程序和数据提取管道的直接交互使用相同的数据来回移动时,我们就知道我们在 S3 Tables 方面取得了成功。
展望未来
随着 S3 接近其第二个十年的尾声,我对我们对 S3 是什么这一概念的理解发生了根本性的演变感到震惊。我们的客户不断推动我们重新构想什么是可能的,从扩展到处理数千亿个对象到引入全新的数据类型(如 S3 Tables)。
今天,在 Pi Day,S3 的 19 岁生日,我希望你看到的是一个仍然对我们正在构建的系统感到非常兴奋并投入其中的团队。展望未来,我感到兴奋的是,我们的构建者将继续找到新颖的方法来推动存储可能性的界限。S3 演变的故事远未结束,我迫不及待地想看看我们的客户将带我们走向何方。同时,我们将继续作为一个团队构建你可以认为理所当然的存储。
正如 Werner 会说的那样:“现在,去构建吧!”