暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Aurora DSQL诞生记!创新驱动云数据库技术演进

亚马逊云科技 2025-06-09
102


在re:Invent 2024大会上,亚马逊云科技发布了Amazon Aurora DSQL,其创新的技术演进路径引发了开发者社区的广泛关注。此前在公司内部DevCon开发者大会上,亚马逊云科技两位高级首席工程师Niko Matsakis和Marc Bowes就已对Aurora DSQL的构建过程进行了深入的技术分享。


Amazon Aurora DSQL是一项极具挑战性的工程项目,其研发工作覆盖了从存储系统到控制面的整个数据库架构。值得注意的是,该项目经历了从JVM到Rust的重大技术转型。本文将详细解析Amazon Aurora DSQL的核心概念、诞生背景及其设计理念,同时深入探讨Rust语言的技术特性及其在Amazon Aurora DSQL实现中的关键作用。这不仅是一个关于技术演进的故事,更向我们揭示了一个重要启示:在快速发展的技术领域,即便是经过验证的成功方案,也需要保持开放创新的态度。



01.


亚马逊云科技定制型数据库的发展历程


自创立以来,亚马逊云科技面临客户日益多元且紧迫的业务需求。2009年,Amazon RDS问世,拉开简化传统关系型数据库管理的序幕。此后逐步构建起丰富的定制化数据库矩阵:Amazon DynamoDB处理互联网级NoSQL负载,Amazon Redshift实现海量数据高速分析,Amazon Aurora以高性价比替代传统商业数据库。


这些产品并非简单的功能升级,而是深度解决客户生产环境中的架构难题。每一次创新都源于长期收集客户反馈,通过持续迭代与联合开发实现突破。例如,Amazon ElastiCache为挖掘数据库性能而生,Amazon Neptune则是应对图数据库应用需求的产物。


亚马逊云科技的数据库产品线始终与Serverless架构、边缘计算等新型计算模式同步演进。每项新服务背后,都凝聚着团队大胆探索、跨部门协作的智慧。尽管现有服务已为客户解决诸多痛点,但亚马逊云科技始终在攻克关键难题:如何打造一款无需管理基础设施、能自动扩展的关系型数据库,同时保留SQL优势,兼具Serverless扩展性、多区域部署与零运维成本?此前Amazon Aurora革新存储架构,Aurora Serverless实现自动纵向扩展,但亚马逊云科技志在重塑云数据库形态,Amazon Aurora DSQL便在此背景下应运而生。


02.


Amazon Aurora DSQL的架构初衷


Amazon Aurora DSQL的设计目标是将传统数据库拆解为更小、职责更单一的模块化组件,各组件通过明确接口与契约协同工作。这一理念源于Unix经典哲学“专注做好一件事并做到极致”,每个模块独立承担特定功能,同时通过标准化协作机制,完整实现数据库核心能力,涵盖事务支持、数据持久性、查询能力、一致性保障、故障恢复等核心需求。以下是Amazon Aurora DSQL的整体架构概览。



2021年,亚马逊云科技已解决Amazon Aurora DSQL读取请求的处理问题,但写入横向扩展仍是待解难题。传统数据库多采用“两阶段提交”(2PC)扩展写入,通过日志组件分片管理数据行,此方式在单分片事务场景下表现良好。然而,一旦事务涉及跨分片更新,便需复杂的检查、加锁与原子提交流程。实际运行中,超时处理、系统活性维持、回滚机制以及协调器故障应对等问题,大幅增加了运维复杂度。


在Amazon Aurora DSQL设计中,开发团队深知传统方案无法满足零运维、高可用与低延迟的目标,亟需探索全新写入扩展方法。


03.


日志层的扩展方案


传统设计中,数据行会预先分配给特定日志组件。但Amazon Aurora DSQL架构做出关键调整:无论事务涉及多少数据行,均完整写入单一日志,借此直接满足ACID中的原子性与持久性要求。这一设计虽简化了写入路径的扩展逻辑,却显著增加了读取复杂度——读取某行最新值需扫描所有日志,底层存储系统需与全量日志保持连接,导致随着日志实例增多以提升TPS时,网络带宽迅速成为性能瓶颈。


为此,Amazon Aurora DSQL引入核心组件Crossbar(交叉枢纽)以实现读写路径解耦扩展。Crossbar为存储节点提供订阅API,支持按特定键值范围订阅变更。事务触发时,Crossbar会将更新路由至已订阅的存储节点。这一机制概念简单但实现挑战显著:日志按事务时间排序,Crossbar需追踪所有日志并生成全局有序的更新流,以保障高效路由与顺序一致性。



04.


日志系统的复杂性持续升级


更复杂的是,每层系统需支持大规模并发分发能力以最大化利用硬件资源,但订阅节点可能因各种原因“掉队”,需引入大量缓冲机制。这使开发团队尤其担忧垃圾回收(GC)问题,尤其是GC暂停带来的不确定性。


分布式系统的残酷现实在此凸显:当需从所有日志节点读取数据维护全局有序性时,“任一节点延迟”的概率趋近于100%。亚马逊云科技工程副总裁Marc Brooker曾深入探讨过这一挑战。


为验证担忧,开发团队搭建模拟测试环境,对Crossbar架构在大规模扩展下的表现建模,重点评估偶发“1秒阻塞”场景的吞吐与延迟,测绘结果却不尽人意:在40台主机模拟环境中,预期Crossbar支撑每秒百万级TPS,实际仅约6000TPS;更严重的是尾部延迟从可接受的1秒飙升至10秒。这并非偶发,而是架构根本性问题——每个事务需跨多主机读操作,任一主机遭遇GC暂停都会拖累整个事务。换言之,规模扩展后,几乎每个事务都会被“最慢节点”拖慢。


05.


以Rust破局:从小模块开启技术转型


GC引发的吞吐瓶颈与系统阻塞,是摆在开发团队面前的真实工程难题。在深度优化JVM(团队有不少工程师对此颇有经验)、转向C/C++(会失去内存安全保障)、探索Rust三条路径中,开发团队选择了Rust——其具备的GC可预测性能、内存安全性、零成本抽象特性,恰好匹配对“高性能与底层控制力兼具”的需求。


切换编程语言并非轻率之举,它是一扇“单向门”,一旦形成大规模代码库,几乎很难回头重来。开发团队决定从相对简单的组件——小模块仲裁器(Adjudicator)切入验证:由于是首次尝试Rust,开发者之所以选择Adjudicator,原因在于其业务逻辑简单,降低转型门槛;日志系统已有Rust客户端可复用;存在JVM(Kotlin)参考实现,便于对照测试。


开发团队选择了两名无C/C++/Rust经验的工程师从零开始,以“小步试错+数据验证”践行团队一贯的工程哲学——正如Rust社区所言:“用Rust编程,如同先解酒再写代码”,编译器频繁的“拒绝”成为日常挑战。这种“短痛换长效”的决策,既规避了大规模重构风险,又通过最小化验证单元积累Rust工程经验,为后续核心组件(如Crossbar)的技术转型奠定基础。



几周后,Rust版本成功编译,运行结果令人大吃一惊:这段未经刻意优化的代码,性能竟比精心优化的Kotlin实现快了10倍——为了将Kotlin版本性能从2000TPS提升到3000TPS,开发团队曾耗时数年,进行了无数次微调,而Java工程师编写的Rust版本首次测试就达到30000TPS。


这一结果彻底扭转了开发团队的思路:与JVM实现同等性能所需的时间相比,“几周的学习成本”显得微不足道。开发团队不再纠结“是否使用Rust”,而是思考“Rust还能解决哪些系统瓶颈”,最终团队决定将整个数据平面用Rust重写,并保留Kotlin作为控制平面实现语言——以高级语言处理高层逻辑,用Rust掌控延迟敏感的底层逻辑。然而这一看似“鱼与熊掌兼得”的方案,实际落地过程却远非预期顺利。


06.


解决核心难题,胜过无休止修补内存安全漏洞


决定用Rust重写数据平面只是起点。经深入讨论,开发团队选择基于PostgreSQL构建系统架构——复用其查询处理能力(解析器、查询规划器),同时替换复制、并发控制、存储引擎等核心组件。但挑战随之而来:该如何修改这个始于1986年、拥有百万行C代码、拥有数千名贡献者,至今仍在高速演进的开源项目?


最简单的方法是直接分叉(fork)一个版本来修改,但这样虽能快速改动,但会切断与社区特性、性能优化的同步,最终与主线版本越走越远、彻底脱节。于是开发团队决定充分利用Postgres的扩展机制(extension points),通过公共API接口修改系统行为,扩展代码与Postgres同进程运行却独立维护。这种方式既保持结构清晰,又能在Postgres升级时轻松适配,避免“硬分叉”的长期隐患。


紧接着,一个新的抉择摆在开发团队面前:这些扩展该用C语言编写,还是Rust?


在设计Postgres扩展时,团队起初倾向于C语言——既便于理解Postgres原生C代码,又可规避语言差异带来的适配问题。但随着开发深入,一个核心矛盾逐渐凸显:Postgres的C代码经数十年实战检验,稳定性毋庸置疑,但新编写的C扩展代码却暗藏隐患。每一行C代码都可能引入use-after-free、缓冲区溢出等内存安全漏洞。而当团队在审查一个简单数据结构的C实现时,竟发现多处内存安全隐患。而改用Rust只需从crates.io引入成熟安全的开源库,即可规避这类问题。


恰好Android团队2024年9月的研究进一步佐证了开发团队的观点:大多数新内存安全问题源于新编写的代码。这让开发团队确信:彻底解决内存安全问题的最佳路径,是从源头避免编写“不安全”代码。最终,Rust凭借其内存安全特性,成为Postgres扩展开发的唯一选择。


注:据Android团队研究表明,大多数新漏洞源于新编写的代码。因此,选择具备内存安全特性的编程语言,可从源头规避内存安全问题。


最终,开发团队决定转向用Rust来编写这些扩展模块。


起初很多人可能认为,既然Rust代码必须频繁与Postgres的C语言API交互,Rust提供的内存安全优势或许会打折扣,但实际情况恰恰相反。团队通过构建抽象层,有效强制执行安全的内存访问模式。例如,在C语言中,常见两个需协同安全使用的字段(如char指针和len字段),此时程序员只能依赖注释或编码规范说明两者关系并提醒避免越界访问,而在Rust中,这些隐含规则被封装进标准String类型,从语言层面确保访问安全性。


开发团队在Postgres代码库中发现许多类似案例——头文件中不得不用文字解释结构体的“正确”使用方式,而借助Rust的类型系统,可以将这些“规则”直接嵌入类型定义,在编译时就防止潜在违规操作。当然,创建这些抽象需要高度谨慎,但一旦设计完成,后续代码就能在保障安全的前提下高效复用。这一经验再次提醒团队:在系统设计中,可扩展性、安全性与稳定性的考量应当优先,即便这些决策短期内看似“困难”,因为从长远看,投入时间学习新语言的代价,远低于反复修复内存安全漏洞的成本。


07.


关于控制平面


最初开发选择用Kotlin编写控制平面,这是基于JVM语言在Amazon Aurora、Amazon RDS等服务中构建控制面的成熟经验,且当时团队对Kotlin更熟悉、部分内部库缺乏Rust实现,而控制平面对高吞吐低延迟的需求不如数据平面迫切。初期数据平面与控制平面独立运行良好,但集成后问题凸显:作为“免运维”能力核心的控制平面需与数据平面共享核心逻辑(如集群热点识别、拓扑变更协调、自动扩缩容等),但双语言架构导致逻辑无法复用,引发实现差异、测试平台割裂、调试修复成本高昂等连锁问题。


面对“支持双语言模拟器”或“控制面重写Rust”的抉择,开发团队结合技术演进与团队能力做出转向:Rust 2021版已解决早期痛点,内部Rust库(如亚马逊云科技的认证运行时客户端)性能超越Java实现,且架构通过API Gateway与Lambda集成更趋简洁。更关键的是,团队从“被动接受”转为“主动拥抱”——内部文档《DSQL Book》系统性沉淀设计哲学,每周技术学习、论文研读与架构讨论形成常态化机制,Rust专家“反向设计”介入提前规避复杂问题,这些投入构建了知识传承体系与技术信心。


最终,统一为Rust架构的优势清晰显现:核心逻辑可在同一平台模拟运行,彻底消除双语言维护负担;Rust在Crossbar场景的吞吐性能与全系统Rust化后的延迟稳定性(p99与p50几乎重合)达到生产级标准,使控制平面与数据平面在可靠性、可观测性上实现深度协同。这次技术转向不仅是语言选择,更是通过架构统一与团队能力升级,为“免运维”数据库的终极目标奠定了坚实基础。


08.


Amazon Aurora DSQL的价值远超编程语言本身


Amazon Aurora DSQL是一次精准的技术契合,凭借系统控制力在核心路径中规避尾延迟问题,保障低延迟与稳定性;以集成灵活性无缝衔接Postgres等C语言代码库,实现模块化扩展;从开发效率看,其不仅能支撑控制平面开发,团队熟悉后效率与Java持平,甚至通过WebAssembly在运维工具中实现跨场景应用。


起初开发团队假设Rust生产力低于Java,但实践证明,尽管存在学习曲线,团队掌握后开发效率并未下降。当然,Rust并非“万能解”——现代Java(如JDK 21)已能满足多数场景性能需求。关键在于基于项目需求、团队能力与运维环境做架构决策:若构建对尾延迟极度敏感的服务,Rust是优选;若组织以Java为标准化语言,需权衡单一团队引入Rust的隔离成本。


真正重要的是赋予团队决策空间,支持其学习、尝试甚至推翻既往选择。唯有如此,才能打造面向未来、经得住时间考验的系统。






期待你的分享  收藏  在看  点赞

亚马逊的一小步,云计算的一大步!


点击阅读原文,获取更多精彩内容!

文章转载自亚马逊云科技,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论