亚马逊AWS官方博客

Amazon Aurora 冉冉升起:我们是如何设计原生云关系数据库的

关系数据库由来已久。关系数据模型可以追溯至 1970 年代 E.F.Codd 的探索。支撑当今主要关系数据库管理系统的核心技术是在 1980-1990 年代开发的。关系数据库的基本要素包括数据关系、ACID(原子性、一致性、独立性和持久性)事务、SQL 查询语言等,都经受住了时间的考验。凭借这些基本特点,关系数据库赢得了全世界用户的钟爱。它们依然是许多公司 IT 基础设施的基石之一。

但这并不是说系统管理员一定很喜欢处理关系数据库。数十年来,管理关系数据库一直都是一件对技能要求非常高的劳动密集型工作。它要求有专门的系统和数据库管理员全神贯注。对关系数据库进行扩展并同时保持容错能力、性能和爆炸半径大小(发生故障的影响),一直是管理员们面临的一个持久挑战。

此外,现代互联网工作负载的要求变得越来越高,需要基础设施具备多个关键的特性:

  1. 用户希望先从小规模起步,然后再大规模增长,基础设施不应限制他们的发展速度。
  2. 在大型系统中,故障属于常态,而非异常。发生组件故障或系统故障时,客户工作负载必须隔离。
  3. 爆炸半径要小。没有人希望单一的系统故障对他们的业务产生巨大影响。

这些问题很难处理,需要突破传统关系数据库架构才能解决。当亚马逊公司面临 Oracle 等传统关系数据库的局限性时,我们创建了一种先进的关系数据库服务 Amazon Aurora

Aurora 的设计保留了关系数据库的核心事务一致性优势。它在存储层进行了创新,构建了一个面向云的数据库,可以在不牺牲性能的前提下支持现代工作负载。客户非常喜欢这一点,因为 Aurora 提供了商业级数据库的性能和可用性,但成本只有后者的十分之一。从 Aurora 最初发布以来,它一直是 AWS 历史上增长最为快速的服务。

在本博文中,我将向大家介绍了一下我们是如何构建 Aurora 的。此外,我还将讨论为什么客户采用它的速度要比 AWS 历史上的任何其他服务都快。

关系数据库的重新构想

想想传统关系数据库的架构:

过去 30-40 年来,这种整体式的关系数据库堆栈没有太大改变。虽然在数据库扩展方面存在不同的常规方法(例如,分区、无共享或共享磁盘等),但这些方法都基于同样的基本数据库架构。这些方法无法解决大规模性能、弹性和爆炸半径问题,因为严密耦合型整体式堆栈的基本局限性依然存在。

为开始解决关系数据库的局限性,我们重新构想了堆栈的概念,将系统分解为基本的组成要素。我们认识到缓存和日志记录层非常适合创新。我们可以将这些层变为一种针对性、可扩展、可自我恢复、多租户、数据库优化的存储服务。在我们开始构建这个分布式的存储系统时,Amazon Aurora 应运而生。

我们对关系数据库中缓存和日志记录的传统理念提出挑战,重新构想了数据库 I/O 层,收获了极大的可扩展性和弹性优势。Amazon Aurora 采用了卸载恢复操作日志记录、基于元组的架构、仲裁以及快速数据库修复等理念,具有极佳的可扩展性和弹性。

卸载恢复日志记录:日志就是数据库

传统关系数据库以页面的方式来组织组织,因此在页面修改后,必须定期刷新到磁盘。为确保在发生故障时的弹性以及维护 ACID 语法的需要,页面修改也将在记录在恢复日志记录中,这些日志记录将以连续统的方式写入磁盘。虽然这种架构提供了支持关系数据库管理系统所需的基本功能,但却存在效率低下的弊端。例如,单个逻辑数据库写入会变为多个(不超过五个)物理磁盘写入,从而引发性能问题。

数据库管理员会尝试通过降低页面刷新的频率来消除写入放大的问题。但这反过来又会加剧崩溃恢复持续时间的问题。刷新间隔时间越久,意味着为了重建正确的页面映像,需要从磁盘读取并应用的恢复日志记录越多。这会导致恢复速度下降。

在 Amazon Aurora,日志就是数据库。数据库实例将恢复日志记录写入分布式的存储层,由存储负责按需利用日志记录构建页面映像。数据库实例无需帅新脏页面,因为存储层始终知道页面的具体内容。这从多个方面提高了数据库的性能和可靠性。由于消除了写入放大,并且使用了扩展存储队列,写入性能得到极大的提高。

例如,按照 SysBench 基准,Amazon Aurora MySQL 兼容版的 IOPS 写入速度 是在类似硬件上运行的 Amazon RDS for MySQL 的 5 倍。由于数据库实例不再需要执行恢复日志流重放,数据库崩溃恢复时间显著降低。存储层负责在页面读取上执行恢复日志,从而形成一个不受传统数据库架构限制的新存储服务,让您可以更进一步创新。

基于元组的架构

我曾经说过,一切都会出现故障。组件会出现故障,在大型系统中更会经常出现故障。整个实例会出现故障。网络故障可能导致基础设施被大面积隔离。在极少数情况下,整个数据中心可能因自然灾害被隔离或丢失。在 AWS,我们采取面向故障的设计方法,利用基于元组的架构以在问题发生前解决问题。

AWS 拥有多个地理区域(20 多个),并且在每个区域内又设了多个可用区。借助这种多区域和多可用区优势,这种设计良好的服务可在发生普通组件故障时以及更大型的灾难时正常运行,不影响服务的可用性。Amazon Aurora 将所有写入复制到三个可用区,提供优异的数据持久性和可用性。事实上,Aurora 可以承受整个可用区的丢失而不会失去数据可用性,并且可以在更大型的故障发生后快速恢复。

但众所周知,复制是非常耗费资源的,那么 Aurora 是如何在提供稳健的数据复制功能的同时,确保高性能的? 答案是仲裁。

仲裁之美

一切都会出现故障。系统越大,某个东西发生故障的概率越大:网络链接、SSD、整个实例、软件组件等等。即使软件组件没有漏洞,它仍然需要定期重启以进行升级。

传统方法是在执行故障转移前阻止 I/O 处理,以及在出现故障组件时以“降级模式”运行,这种方法在大规模环境下存在很多问题。应用程序往往也不能很好地承受 I/O“打嗝”。借助略微复杂的数学,可以证明在大型系统中,随着系统规模的增长,以降级模式运行的概率会接近 1。此外还存在一个真正潜藏的“灰色故障”问题。 这是指组件并未完全失效,而是变得运行缓慢。如果系统设计没有预计延迟的问题,则这一短板可能导致整个系统的性能下降。

Amazon Aurora 使用了仲裁机制来解决组件故障和性能降级的问题。写入仲裁的基本原理十分简单:写入尽可能多的副本以确保仲裁读取始终可以找到最新的数据。最基本的仲裁例子是“三分之二”:

Vw+Vr > V

Vw > V / 2

V=3

Vw=Vr=2

例如,您可能需要执行 3 个物理写入,写入仲裁为 2。您无需等待所有三个写入都完成,即可宣布逻辑写操作已经成功。如果有一个写入失败或缓慢是可以接受的,因为总体操作结果和延迟不会受到此异常值的影响。这一点非常重要:即使某个东西发生故障,仍可以成功快速完成写入。

这种简单的 2/3 仲裁机制可让您容忍某个可用区完全丢失。当然这还不够。虽然丢失整个可用区是一种稀有事件,但不会降低其他可用区发生组件故障的可能性。对于 Aurora,我们的目标是可用区+1:我们希望能够容忍一个可用区丢失,并且同时再发生一个故障时不发生任何数据持久性损失,同时对数据可用性的影响也极低。我们使用 4/6 的仲裁机制来实现这一目标:

Vw+Vr > V

Vw > V / 2

V=6

Vw=4

Vr=3

对于每个逻辑日志写入,我们发出六项物理复制写入指令,在其中四项写入指令完成时视为写入操作成功。每个可用区有两个副本,如果整个可用区丢失,写入操作仍将会完成。如果一个可用区丢失,同时又发生了一个故障,您仍可达到读取仲裁要求,然后执行快速修复以快速恢复写入能力。

快速修复和弥补

数据复制的方式有多种。在传统存储系统中,数据镜像或纠删编码在整个物理存储单元进行,在多个单元组合在一个 RAID 阵列中。这种方法导致修复十分缓慢。RAID 阵列的重建性能受到阵列中少数设备的能力限制。随着存储设备变得越来越大,重建期间需要复制的数据量越多。

Amazon Aurora 使用完全不同的复制方法,这种方法基于分区和扩展架构。Aurora 数据库卷在逻辑上分为 10-GiB 大小的逻辑单位(保护组),每个保护组以六种方式复制到物理单位(分段)中。各个分段都分散在庞大的分布式存储队列中。如果发生某个故障,导致一个分段丢失,单个保护组的修复只需要移动大约 10GiB 的数据,几秒钟即可完成。

此外,需要修复多个保护组时,整个存储队列都将参与修复进程。这提供了极大的带宽,从而可以快速完成整个批次的修复操作。因此,如果某个可用区丢失并且又发生了另一个组件故障,Aurora 可能在几秒钟内失去给定保护组的写入仲裁。但自动启动的修复操作会以极快的速度恢复可写入性。换言之,Aurora 存储会快速自我恢复。

是如何实现以六种方式复制数据并保持高性能的写入的? 传统数据库架构会将完整的页面或磁盘扇区写入存储,导致网络疲于应付,因此无法实现这一目标。与此相反,Aurora 中的实例仅将恢复日志记录写入存储。这些记录要小很多(一般为几十或几百字节),可以在不造成网络过载的情况下实现 4/6 写入仲裁。

根据写入仲裁的基本原理,一些片段最初可能不会始终收到所有写入。这些片段是如何处理恢复日志流中的缺口的? Aurora 存储节点会在相互之间持续“闲谈”以填补空白(并执行修复)。日志流的推进会通过日志序列号 (LSN) 管理来紧密编排。我们使用一组 LSN 标志来维护各个独立片段的状态。

读取方面是怎么操作的? 仲裁读取十分昂贵,最好能够避免。客户端 Aurora 存储驱动器会跟踪哪些片段的哪些写入操作已经成功。由于它始终知道在哪里取得最新的页面副本,因此不需要为例行页面读取执行仲裁读取。此外,驱动器会跟踪读取延迟,始终尝试从过去延迟最低的存储节点读取。唯一需要仲裁读取的情形是在数据库实例重启期间的恢复。必须通过询问存储节点的方式重建初始的 LSN 标志集。

创新的基石

Aurora 的许多重要新功能都直接受益于分布式的自我恢复型存储架构。例如:

  • 读取可扩展性:除主数据库实例外,在 Aurora 中最多可以预置 15 个只读副本,确保了读取可扩展性和更高的可用性。只读副本与主实例使用相同的分区存储卷。
  • 连续备份和时间点还原:Aurora 存储层以连续、透明的方式将恢复日志流备份到 Amazon S3。借助时间点还原功能,您可以还原到配置的备份期内的任何时间戳。无需计划创建快照,距离需要的时间点最近的快照极远时也不会发生事务丢失的问题。
  • 快速克隆:Aurora 存储层可以快速创建卷的物理副本,无需复制所有页面。页面最初在父子卷之间共享,页面修改时将完成复制并写入操作。复制卷时不会发生重复的费用。
  • 回溯:快速将数据库还原至特定的时间点,但无需从备份执行完整的还原操作。错误丢弃了表时怎么办? 您可以使用 Aurora 的回溯功能还原。

依托 Aurora 的存储引擎,更多的关系数据库创新将会很快出炉。我们已经进入一个全新的关系数据库时代,Aurora 仅仅是开始。客户也异口同声表示支持。Capital One、道琼斯、Netflix 和 Verizon 等行业领先企业正在将他们的关系数据库工作负载迁移到 Aurora,包括 MySQL 兼容版和 PostgreSQL 兼容版。

希望进一步了解 Amazon Aurora 的设计?

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases。参见 SIGMOD 2017

Amazon Aurora: On Avoiding Distributed Consensus for I/Os, Commits, and Membership Changes。参见 SIGMOD 2018

本篇作者

Werner Vogels

亚马逊全球首席技术官(CTO)兼副总裁,负责推动亚马逊全球包括云计算在内的技术创新。