本文来自 www.allthingsdistributed.com/2025/05/jus…
在 re:Invent 大会上,我们发布了 Aurora DSQL,自那以后,我与众多开发者探讨了这对数据库工程意味着什么。有趣的不只是技术本身,而是我们如何走到今天的这段旅程。我一直希望能更深入地讲述这个故事,不仅讲述 DSQL 的“是什么”,还要讲“怎么做的”和“为什么这么做”。
几周前,在我们内部的开发者大会 DevCon 上,我观看了两位高级首席工程师(PE)的演讲,讲述了构建 DSQL 的过程(该项目从最初 100% 使用 JVM,最终转向了 100% Rust 实现)。演讲结束后,我邀请 Niko Matsakis 和 Marc Bowes 与我一起,把他们的洞见整理为一篇深入探讨 DSQL 开发过程的文章。他们不仅同意了,还主动提出帮助解释其中一些技术细节较为复杂的部分。
在下面的博文中,Niko 和 Marc 将深入解析我们是如何用 Rust 构建 DSQL 的。这是一个关于工程效率追求的故事,也告诉我们为何质疑既有决策——即使它们曾经表现优异——仍然十分重要。
作者注
在正式进入正文前,有一件事需要说明。这是一个充满野心的项目(并且仍在进行中),它需要涵盖从存储到控制平面工程的深厚专业知识。本文整合了许多将 DSQL 变为现实的首席和高级首席工程师的经验与智慧。希望你阅读本文时,能像我一样享受其中的过程。
特别感谢:Marc Brooker、Marc Bowes、Niko Matsakis、James Morle、Mike Hershey、Zak van der Merwe、Gourav Roy、Matthys Strydom。
AWS 专用数据库的简史
自 AWS 成立初期以来,客户的需求不断演变——而且越来越紧迫。从 2009 年推出 Amazon RDS,简化传统关系型数据库的管理开始,我们迅速扩展出了一套专用数据库组合:
- DynamoDB,用于支持互联网规模的 NoSQL 工作负载;
- Redshift,用于对大规模数据集进行快速分析查询;
- Aurora,则为那些希望摆脱传统商用数据库成本与复杂性但又不想牺牲性能的用户提供了解决方案。
这些不仅是渐进的技术进步,更是客户在生产环境中遇到的真实限制的回应。一次又一次,解锁正确方案的关键往往不是灵光一现,而是反复倾听客户需求、进行迭代开发,常常让客户直接参与其中。
当然,速度和扩展性并非唯一的驱动力。随着开发者试图从关系数据库中榨取更多性能,ElastiCache 也应运而生,用于内存缓存。Neptune 也随后推出,因图数据工作负载和关系密集型应用超出了传统数据库的处理能力。
回顾这些年,令人惊叹的不仅是产品线的增长,更是它与新计算模式——如 Serverless、边缘计算、实时分析等——同步演进。每一次产品发布的背后,都是一个愿意尝试、挑战旧有假设,并与 Amazon 内部产品团队紧密协作的工程团队。外部往往看不到的是:创新几乎从来不是一夜之间完成的,它往往是建立在逐步积累、成功经验和从失败中学习(而非畏惧)的基础上。
虽然我们推出的每一个数据库服务都解决了关键问题,但仍有一个难题持续存在:
如何构建一个无需基础设施管理、能根据负载自动扩展的关系数据库?
一个结合了 SQL 的熟悉与强大、具备真正的无服务器扩展能力、可无缝部署于多区域,并具备“零运维负担”的数据库?我们过去的尝试都朝着这个目标迈进了一步。Aurora 引入了为云优化的存储与简化的操作;Aurora Serverless 实现了自动垂直扩展;但我们知道还不够。这不仅仅是添加功能或提升性能的问题,而是对“云原生数据库”应有形态的根本性重新思考。
于是,Aurora DSQL 诞生了。
Aurora DSQL
Aurora DSQL 的设计目标,是将数据库拆分成小而明确的组件,通过清晰的接口和显式契约进行协作。每个组件秉承 Unix 的理念 —— “做好一件事”,但通过协同工作,它们能提供用户所期望的所有数据库功能(事务、持久性、查询、隔离、一致性、恢复、并发、性能、日志记录等)。
架构概览
我们在 2021 年已完成读取路径的设计,剩下的核心问题是如何实现写入的水平扩展。传统解决方案是两段提交(2PC):事务管理器协调多份日志(journal)写入,按原子性提交。然而,当涉及多日志的跨分区更新时,这种方案会变得异常复杂,需要处理锁、超时、协调器失败等问题,运维复杂性急剧增加。我们需要新的方法来保障高可用和低延迟。
日志层的扩展
我们做出了一个关键设计决策:不再将行数据预分配到固定日志,而是将整个事务提交写入单个日志中,不论它修改了多少行。这样能满足 ACID 中的原子性和持久性。不过,读取路径变得更复杂,因为要获取最新值,必须查询所有日志。所以存储层需要和每个日志保持连接。随着日志数量上升,网络带宽成为瓶颈。
引入 Crossbar
我们发明了 Crossbar,分离读写路径的扩展逻辑。存储节点可订阅特定键范围。当事务写入日志后,Crossbar 将更新路由到订阅节点。核心挑战是如何高效地跟踪多个日志流,以形成全局总序。
同时,每层要支持高 fan‑out,但订阅节点可能因各种原因落后,需要排队缓存。这就带来 GC 停顿问题。
我们通过仿真实验发现,随着节点数增多,每笔事务最终都会遇到尾部延迟:例如,40 节点配置下,预期每秒达百万 TPS,但实际只有六千;p99 延迟从 1 秒跳升至 10 秒。原因是事务必须读取多个节点,任何一个延迟都会拖慢整体。GC 暂停成了隐形杀手。
短期痛苦,长期收益
面对垃圾收集、吞吐和延迟问题,我们面临选择:继续深耕 JVM 优化、转用 C/C++(但失去内存安全保障)、还是尝试 Rust。我们选择了后者,因为它无需 GC,且有内存安全保障和零成本抽象。
当然,大规模从 JVM 转向 Rust 并非小事。但我们决定从边缘模块入手:先用 Rust 重写 Adjudicator(事务冲突仲裁器),它逻辑相对简单,可快速测试,并与已有 Kotlin 实现性能对比。
两名从未接触过 Rust 的工程师参与开发,最初编译器频繁“说不”。但几周后,结果令人惊讶 —— Rust 版 Adjudicator 性能达到 Kotlin 版的 10 倍。Kotlin 版经过多年优化,从 2000 提升到 3000 TPS,Rust 初版即到 30000 TPS。
这让我们彻底改变对 Rust 的看法:此前那些花时间适应语言的论据瞬间变得微不足道。我们从“是否该用 Rust?”转向“还有什么可以用 Rust 优化?”。
结果是,我们决定将整个数据面迁移到 Rust,而控制 plane 保留 Kotlin —— 用 Kotlin 做高层逻辑,用 Rust 实现延迟敏感部分。不过这只是暂定方案,后面章节还有更多。
一次修补胜过无数内存安全漏洞
我们还决定基于 PostgreSQL 构建查询引擎:用其 parser 和 planner,替换其复制、并发控制、持久化、存储等机制。
绕过直接 hard‑fork 的方法是使用 PostgreSQL 的扩展机制:向其插件系统注册扩展,而不是改动核心代码,从而让我们可兼容主流 PostgreSQL 发展路径。
语言选择上,起初我们认为 C 更合适(因 PostgreSQL 本身就是 C),但运行发现新增 C 代码带来许多潜在内存安全问题:比如 use‑after‑free、越界写。恰好 Android 团队在 2024 年发表文章,指出绝大多数 bug 来自“新代码”,由此印证我们:新增 C 代码容易失败。
转向 Rust 后,我们能用已有的安全类型(如 String 替代 char*+len),把诸多在 C 中依赖注释或约定的内容,变为编译器强制检查。尽管编写这些抽象层需谨慎,但后续逻辑可借此避免错误,大幅减少内存安全缺陷风险。
控制 Plane 的语言选择错误
最初,将控制 Plane 写在 Kotlin 是基于既有经验:Amazon RDS 和 Aurora 多用 JVM 语言,控制平面更易用。Rust 带来的性能优势在控制平面里不那么关键,且 Rust 库还不够成熟。然而随着数据面迁移完成,我们在集成时发现,数据面控制面分属 Rust 和 Kotlin,导致逻辑无法重用,测试与文档维护复杂,每次小改都要 debug‑fix‑deploy ——代价高昂。
要么重写模拟平台以支持双语言,要么将控制面也改写为 Rust。
经过考量,第二条是唯一可行方案。Rust 的生态与语言本身的痛点已有极大缓解,内部库不但成熟,还有一些远超 Java 版本。团队内部对 Rust 的态度也已趋于热情。Marc Brooker 撰写的《DSQL 之书》,每周深度学习支持,也让大家更加自信。最终决定是不再犹豫:用 Rust 完整统一系统架构。
迁移后,我们看到端到端的 p99 延迟非常稳定,接近 p50,即使最慢操作也保持一致的高质量表现。
不只是写代码
最终 Rust 成为 DSQL 的通用语言:提供低延迟控制、C 兼容扩展能力、高层生产力。我们甚至用 Rust(配合 WebAssembly)构建内部运维 Web 页面。
最令人惊讶的是,团队认为 Rust 的生产力并不低于 Java。虽然学习曲线存在,但一旦成熟,他们的开发速度恢复如初。
当然,这不意味 Rust 适合所有项目。JDK 21 等现代 JVM 性能已足够应对多数服务;关键是按项目需求、团队能力和运营环境选语言。若系统对尾延迟非常敏感,Rust 是优选。但如果团队偏向 Java,就需慎重衡量社区隔离成本。核心是让团队基于 thoughtful choice,并支持他们学习、试错、偶尔回顾决策。
总结一句:现在,去构建吧!
推荐阅读
如需深入了解 DSQL 的思考,可阅读 Marc Brooker 的 DSQL 相关系列文章:
- Aurora DSQL, and A Personal Story
- Reads and Compute
- Transactions and Durability
- Wait! Isn’t That Impossible?