Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

译者:Aceking,极数云舟技术合伙人,数据库内核研发专家,负责企业级云原生数据库ArkDB等核心数据库产品,华中科技大学计算机系数据库方向研究生毕业,原达梦数据库产品研发工程师,负责达梦数据库内核开发,长期致力于数据库原理和源码开发,精通C/C++,公司内部流传有他的名言:之后不再敢说精通C++了。前端

摘要mysql

Amazon Aurora是亚马逊网络服务(AWS)的一个组成部分,对外提供一个OLTP负载类型的关系数据库服务。本文描述这种架构设计考量。咱们确信,高吞吐量的数据处理的主要约束已经从计算、存储转移到网络中来了。Aurora给出一个新颖的架构来解决这个约束,其最显著的特色是把重作日志(redo)处理下放到专门为Aurora设计的存储服务中。文章会介绍如何既能减小网络流量,又能快速崩溃恢复(crash recovery), 还能实现复制失效不损失数据,以及存储上的容错,自愈。而后介绍Aurora在多存储节点中的保持持久状态的一致性的高效异步方案,避免了代价高昂繁复的恢复协议。最后,咱们分享了18个多月运营Aurora产品的经验,这些经验是从现代云应用的客户们对数据库层的指望中总结的。web

关键字sql

数据库; 分布式系统; 日志处理; 仲裁模型; 复制; 恢复; 性能; OLTP数据库

01 导论缓存

愈来愈多的IT负载转移到公有云中,这种全行业的转变的重大缘由包括:公有云服务商可以弹性按需提供容量,以及只需支付运营费用而不用支付资产费用的模式。许多IT负载须要一个OLTP的关系型数据库。所以,提供一种等效甚至超越的预置数据库用以支持这种转变显得相当重要。安全

愈来愈多现代分布式云服务,经过解耦计算与存储,以及跨越多节点复制的方式实现了弹性与可伸缩性。这样作,可让咱们进行某些操做,例如替换失误或者不可达的主机,添加复制节点,写入节点与复制节点的故障转移,拓展或者收缩数据库实例的大小等等。在这种环境下,传统的数据库系统所面临的IO瓶颈发生改变。由于IO能够分散到多租户集群中的多个节点和多个磁盘中,致使单个磁盘和节点不在是热点。相反,瓶颈转移到发起这些IO请求的数据库层与执行这些IO请求的存储层之间的网络中。除了每秒包数(packets per second , PPS)以及带宽这种基本瓶颈外,一个高性能的数据库会向发起存储机群并行的写入,从而放大了网络流量。某个异常存储节点的性能,磁盘或者网络通路,都会严重影响响应时间。服务器

虽然数据库大部分的操做能够互相重叠执行,可是有一些状况须要同步操做,这会致使停顿和上下文切换(多是线程上下文切换,也能够是内核态与用户态切换--译者注)。状况之一,数据库因为缓冲缓存(buffer cache)未命中,致使进行磁盘读,读线程没法继续只能等待磁盘读取完成。将脏页强制赶出而且刷盘,用以给新页腾出空间。也会致使缓存(cache)不命中。虽而后台处理,例如检查点,脏页写入线程能减小这种强制行为,可是仍然会致使停顿,上下文切换以及资源争用。网络

事务提交是另外一个干扰源。一个正在提交的事务的停顿,会阻止另外一个事务的进行。处理多节点同步协议的提交,如两阶段提交(2PC)对云级规模(cloud-scale)的分布式系统来讲更是一个挑战。这些协议不能容忍失败。可是高规模(high-scale)老是充斥着软硬件失效的“背景噪声”。同时还有高延迟,由于高规模系统老是跨多个数据中心分布的。架构

本文中,咱们介绍Amazon Aurora,一种新型的数据库服务,经过大胆激进的使用高分布性的云环境中的日志来解决上述问题。咱们给出了一种使用多租户可伸缩的存储面向服务的架构(图1),这种存储服务与数据库实例集群松耦合,而且从中提取虚拟的分段的重作日志(redo log).虽然每一个数据库实例仍然有大部分的传统的数据库内核(查询处理,事务,锁,缓冲缓存,访问方法,回滚管理), 可是一些功能(重作日志,持久存储,崩溃恢复,备份恢复)已经剥离,放到存储服务中。
Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

咱们的架构与传统方法相比,有三大明显优点:首先,把存储建为一个容错、自愈、跨多个数据中心的独立服务,咱们能够保护数据库,使得网络层或存储层的性能差别,暂时或永久的失效,对数据库再也不有影响。咱们观察到持久存储的失效能够做为一个长时-可用性事件(longlasting availability event)来建模, 而可用性事件能够做为一个长时性能变更来建模,长时性能变更已经有一个设计良好的系通通一处理。 其次,经过存储写日志的方式,能够把网络的IOPS减小一个量级。一旦咱们移除了这个瓶颈,就能够大胆的在大量的其余争用点上作优化,就能获取比咱们基于的MySQL基线源码更明显的吞吐量的提高。第三,咱们把一些最重要最复杂的功能(如:备份、日志恢复)从数据库引擎中一次性代价高昂的操做转变为分摊到大型分布式机群的连续异步的操做。从而得到近乎瞬时的崩溃恢复,无需检查点,同时,备份的代价也不高昂,不会影响前台处理。

本文介绍了咱们的三点贡献

如何分析云级规模下的持久性,如何设计具备对相关失效具备弹性的仲裁系统。(段2)

如何使用灵巧的分离存储,使获得存储的负载低于传统的1/4。(段3)

如何在存储分布式系统中消除多阶段同步,崩溃恢复,检查点。(段4)

咱们把三个创意组合在一块儿,设计出Auraro整体架构(段5),随后(段 6)检视咱们的性能结果,(段7)展现咱们的运营经验,(段8)概述相关的工做,(段9)给出结束语。

02 可伸缩的持久性

若是数据库不作其余的事情,必须知足以下要求:一旦写入,必须可读。不是全部系统能够作到这一点。在本段中咱们讨论了aurora仲裁模型背后的原理,为何咱们要将存储分段,如何将持久性与可得到性结合在一块儿,减小抖动,而且帮助解决大规模存储机群的运营问题。

2.1 复制与相关失效

实例的生命周期与存储的生命周期并无太大的相关。实例失效,用户会将其关闭,他们会根据负载状况调整其大小。这些特色有助于咱们将计算层与存储层解耦。

一旦你这样作了,这些存储节点和磁盘仍然会失效,所以须要某种形式的复制实现对失效的弹性。在规模大的云环境下,存在持续的下层背景噪声,如节点失效,磁盘、网络路径失效。每一种失效有不一样的持续时间和破坏半径。好比,某个节点暂时没法网络到达,重启致使的临时停机时间,磁盘,节点,机架,网关的叶节点或者主干节点, 甚至整个数据中心的永久失效。

在存在复制的系统中,实现容错使用一种[]基于仲裁的投票协议。若是一个复制数据项有V个副本,每一个副本能够赋予一个投票,一次读操做或者写操做分别须要Vr个投票的读仲裁,或者Vw个投票的写仲裁。那么要达到一致性,仲裁必须两个规则,首先,每次读操做必须能得到最新的修改。公式为:

Vr+Vw > V,

这个规则,保证读取的节点集与写节点集有交集,读仲裁至少能读到一个最新版本的数据副本。其次,每次写必须占副本多数,用以免写冲突。即公式:

Vw > V/2,

一个能容忍复制数据项(V=3)一个节点损失的通用的方法是,写须要2/3的投票仲裁(Vw=2),读也须要2/3投票仲裁(Vr=2)。

咱们确信2/3的投票仲裁数是不充分的,为了解释为何,先了解AWS的一个概念,可用区(AZ,availability zone)是区域(region)的子集。一个可用区与其余可用区能够低延迟的链接,可是能够隔离其余可用区的故障,例如,电源,网络,软件部署,洪水等等。跨AZ的分布复制数据能够保证典型的大规模故障模式只影响一个复制副本,这意味着,能够简单地放三个副本到在不一样的可用区,就能够支持大范围的事件容错,可是少许的个别失效事件除外。

然而,在一个大的存储机群,故障的背景噪声意味着,任意给一个时刻,都会有一个节点或者磁盘的子集可能坏掉并修复。这些故障可能在可用区A、B、C中独立分布。然而,可用区C因为火灾、洪水、屋顶倒塌等等,与此同时,可用区A或者B也有故障(故障背景噪声),就打破了任意一个副本的仲裁过程。在这一点上,2/3票数的读取仲裁模型,已经失去了两个副本,所以没法肯定第三个副本是不是最新的。换一句话说,虽然,每一个可用区的个体复制节点故障互不相关,可是一个可用区的失效,则与可用区全部的节点和磁盘都相关。仲裁系统要能容忍故障背景噪声类型的故障与整个可用区的故障同时发生的状况。

在Aurora中,咱们选用一种能够容两种错误的设计:(a)在损失整个可用区外加一个节点(AZ+1)状况下不损失数据。

(b)损失整个可用区不影响写数据的能力。

在三个可用区的状况下,咱们把一个数据项6路写入到3个可用区,每一个可用区有2个副本。使用6个投票模型(V=6),一个写仲裁要4/6票数(Vw=4),一个读仲裁须要3/6票数(Vr=3),在这种模型下,像(a)那样丢失整个可用区外加一个节点(总共3个节点失效),不会损失读取可用性,像(b)那样,损失2个节点,乃至整个可用区(2个节点),仍然保持写的可用性。保证读仲裁,使得咱们能够经过添加额外的复制副原本重建写仲裁。

2.2 分段的存储

如今咱们考虑AZ+1状况是否能提供足够的持久性问题。在这个模型中要提供足够的持久性,就必须保证两次独立故障发生的几率(平均故障时间,Mean Time to Failure, MTTF)足够低于修复这些故障的时间(Mean Time to Repair, MTTR)。若是两次故障的几率够高,咱们可能看到可用区失效,打破了冲裁。减小独立失效的MTTF,哪怕是一点点,都是很困难的。相反,咱们更关注减小MTTR,缩短两次故障的脆弱的时间窗口。咱们是这样作的:将数据库卷切分红小的固定大小的段,目前是10G大小。每份数据项以6路复制到保护组(protection Group,PG)中,每一个保护组由6个10GB的段组成,分散在3个可用区中。每一个可用区持有2个段。一个存储卷是一组相连的PG集合,在物理上的实现是,采用亚马逊弹性计算云(ES2)提供的虚拟机附加SSD,组成的大型存储节点机群。组成存储卷PG随着卷的增加而分配。目前咱们支持的卷没有开启复制的状况下可增加达64TB。

段是咱们进行背景噪声失效和修复的独立单元。咱们把监视和自动修复故障做为咱们服务的一部分。在10Gbps的网络链接状况下,一个10GB的段能够在10秒钟修复。只有在同一个10秒窗口中,出现两个段的失效,外加一个可用区的失效,而且该可用区不包含这两个段,咱们才能够看到仲裁失效。以咱们观察的失效率来看,几乎不可能,即便在为咱们客户管理的数据库数量上,也是如此。

2.3 弹性的运营优点

一旦有人设计出一种能对长时失效天然可复原(弹性)的系统,天然也对短时失效也能作到可复原。一个能处理可用区长时失效的系统,固然也能处理像电源事故这样的短时停顿,或者是因糟糕的软件部署致使的回滚。能处理多秒的仲裁成员的可用性的损失的系统,也能处理短时的网络的阻塞,单个存储节点的过载。

因为咱们系统有高容错性,能够利用这点,经过引发段不可用,作一些维护工做。好比 热管理就直截了当,咱们直接把把热点磁盘或节点的一个段标记为坏段,而后仲裁系统经过在机群中迁移到较冷的节点来修复。操做系统或者安全打补丁在打补丁时,也是一个短时不可用事件。甚至,软件升级也能够经过这个方式作。在某个时刻,咱们升级一个AZ,可是确保PG中不超过一个成员(段或者节点)也在进行补丁。所以,咱们的系统能够用敏捷的方法论在咱们的存储服务中进行快速部署。

03 日志即数据库

本段解释为何传统的数据库系统在采用段2所述的分段的复制存储系统,仍难以承受网络IO和同步停顿致使的性能负担。咱们解释了将日志处理剥离到存储服务的方法,以及实验证明这种方法如何显著的下降网络IO,最后讲述了最小化同步停顿和没必要要写操做的各类技术。

3.1 写放大的负担

咱们的存储卷的分段存储,以及6路写入4/6仲裁票数的模型,具备高弹性。可是不幸的是,这种模型会让传统的数据库如MYSQL没法承受这种性能负担,由于应用往存储作一次写入,会产生存储中许多不一样的真实IO。高量的IO又被复制操做放大,产生沉重的PPS(每秒包数,packets per second)负担。同时,IO也会致使同步点流水线停滞,以及延迟放大。虽然链复制[8]以及其余替代方案能减小网络代价,可是仍然有同步停顿以及额外的延迟。让咱们看看传统的数据库写操做是怎么进行的把。像Mysql这样的系统,一边要向已知对象(例如,heap文件,B-数等等)写入页,一边要像先写日志系统(write-ahead log, WAL)写入日志。日志要记录页的前像到后像的修改的差别。应用日志,可让页由前像变为后像。
而实际上,还有其余的数据也要写入。举个例子,考虑一下如图2所示以主从方式实现的同步镜像mysql配置, 用以实现跨数据中心的高可用性。AZ1是主mysql实例,使用亚马逊弹性块存储(EBS),AZ2是从Mysql实例,也用EBS存储,往主EBS的写入,均要经过软件镜像的方法同步到从EBS卷中。

Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

图2所示,引擎须要写入的数据类型:重作日志,binlog日志,修改过数据页,double-write写,还有元数据FRM文件。图中给了以下的实际IO顺序:

步骤1步骤2,像EBS发起写操做,而且会像本地可用区的EBS镜像写入,以及操做完成的响应消息。

步骤3,使用块级镜像软件将写入转入到从节点。

步骤4和5从节点将传入的写入操做执行到从节点的EBS和它的EBS镜像中。

上述MySQL的镜像模型不可取,不只仅由于它如何写入的,也由于它写进了什么数据。首先,1,3,5步骤是顺序同步的过程,因为有许多顺序写入,产生了额外的延迟。抖动也被放大,哪怕是在异步写的时候,由于它必须等待最慢的操做,让系统任凭异常节点的摆布。按照分布式视角来看,这个模型能够视做4/4的写仲裁,在失效或者异常节点的异常性能面前,显得十分脆弱。其次,OLTP应用产生的用户操做,能够产生许多不一样类型的写入,而这些数据写入方式虽然都不一样,可是却表示一样的信息。例如,往双写缓冲的写入,主要是为了防止存储层页的损坏,(可是内容与页的普通写入是同样的)

3.2 日志处理分离到存储

传统数据库修改一个页,就会产生日志记录。调用日志应用器,应用日志到内存中该页数据的前像,获得该页的后像。事务提交前日志必须已经写入,而数据页则可推后写入。在Aurora中,经过网络的写入只有重作日志。数据库层不写入数据页,也没有后台写入,没有检查点,没有Cache替换。相反,日志应用器已经放置到存储层,存储层在后台生成,或者按需生成数据库的数据页。固然,从头开始应用全部修改的,代价让人难以承受,所以,咱们在后台持续生成数据页以免每次抓取时在从新生成这些页。从正确性的角度来看,后台生成是彻底可选的:就引擎而言,日志即数据库 ,存储全部具体化出来的页,均可以视做日志应用的缓存。与检查点不一样,只有那些很长修改链的页才会要从新具化 ,检查点由整个日志链长度控制,而Aurora由给定页的日志链长度来控制。

尽管复制会致使的写放大,咱们的方法明显能减小网络负载,并且保证性能与持久性。在使人尴尬的并行写入,存储能超载IO而不影响数据库引擎的写吞吐能力。举个例子,图3所示,是一个一主多从Aurora集群,部署在多个可用区中。在这个模型中,主库往存储写入日志,而且把这些日志与元数据更新发送给从节点。基于公共存储目标(一个逻辑段,好比,PG)的一批批日志都是彻底有序的。把每一个批次以6路传输给复制节点,存储引擎等待4个或4个多的回应,用以知足写仲裁条件,来判断日志在持久上或硬化上是否有问题。复制节点应用这些日志记录来更新自身的缓存缓冲。

Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

为了测量网络IO的情况,咱们在以上所述的两种mysql配置下,进行了100GB的数据集只写负载的sysbench[9]测试:一种是跨多个可用区的mysql镜像配置,另外一个是Aurora的RDS,在r3.8large EC2的实例中运行了30分钟。

实验结果如表1所示。在30分钟的时间里,在事务上,Aurora比mysql镜像多35倍,虽然Aurora的写放大了6倍,可是每一个事务写IO仍然比mysql镜像小7.7倍。这里并无记录EBS的链式复制与mysql跨可用区的写。每一个存储节点,只是6个复制节点中的一个,所以看不到写放大。这里的IO数量减小了46倍。写入了更少的数据,省下的网络能力,能够激进地经过复制实现持久性与可用性,并在发起并行请求时,最小化抖动的影响。
Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

把日志处理放到存储服务中,也能够最小化崩溃恢复的时间,来提升可用性,而且消除检查点、后台写、备份等后台处理所带来的抖动。咱们再看崩溃恢复。传统数据库在崩溃以后,必须从最近的检查点开始,将日志重演并要报保证全部的保存的日志都被应用。在Aurora中,持续的日志应用发生在存储中,而且它是持续的,异步的,分散在机群中的。任何一次读页请求,若是该页不是当前版本,则要应用一些重作日志。所以,崩溃恢复过程已经分散到全部的正常的前台处理中,不须要在数据库启动的时候执行。

3.3 存储服务的设计点

咱们的存储服务设计的核心原则是最小化前台写请求的延迟。咱们把大部分的存储处理移到了后台。鉴于存储层前台的请求峰值与平均值之间的天然变更,咱们有足够的时间在前台请求以外进行这些处理。并且咱们也有机会用cpu换磁盘。好比说,当存储节点忙于前端请求处理,就没有必要进行旧页的垃圾回收(GC),除非该磁盘快满了。Aurora中,后台处理与前台处理负相关。与传统的数据库不同,传统数据库的后台写页、检查点处理与系统的的前台的请求量正相关。若是系统中有积压请求,咱们会抑制前端请求,避免造成长队列。由于系统中的段以高熵的形式(高混乱程度)分布在不一样的存储节点中,限制一个存储节点在4/6仲裁系统中天然的做为慢节点处理。

如今以更多的细节来观察存储节点各类活动。如图4所示,包含了以下若干步骤:

一、接收日志记录而且放入内存队列中。

二、保存到磁盘而且返回应答。

三、组织日志记录,而且识别日志中的空白,由于有些批次的日志会丢失。

四、与对等节点交流(gossip)填住空白。

五、合入日志到新的数据页。

六、周期地将新数据页和日志备份到S3。

七、周期的回收旧版本。

八、校验页中的CRC编码。

注意,以上各个步骤不全是异步的,1 和 2 在前台路径中有潜在的延迟影响。

Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

04 日志前行

本段中,咱们介绍数据库引擎生成的日志如何在持久状态,运行状态,复制状态始终保持一致。特别是,如何不须要2PC协议高效地实现一致性。首先,咱们展现了如何在崩溃恢复中避免使用高昂的重作处理。咱们解释正常操做中如何维护运行状态与复制状态。最后,咱们披露崩溃恢复的细节。

4.1 解决方案草图:异步处理

由于咱们把数据库建模为一个日志流,事实上日志做为有序的修改序列,这点咱们也要利用。实际上,每一个日志都有与之关联的日志序列号(Log Sequence Number, LSN), LSN是由数据库产生的单调递增的值。

这就可使咱们简化共识协议,咱们采用异步方式协议而不是2PC协议。2PC协议交互繁复而且不容错。在高层次上,咱们维护一致性与持久化的点,在接收还未完成的存储请求的响应时推动这些点,而且是持续的推动。因为单独的存储节点可能会丢失一个或者多个日志记录,它们能够与同PG的其余成员进行交流,寻找空白并填补空洞。运行状态是由数据库维护的,咱们能够直接从存储段读取而不须要读仲裁。但在崩溃恢复时候,运行状态已丢失须要重建的时候除外,这时候仍须要读仲裁。

数据库可能有不少独立的未完成的事务,这些事务能够与发起时顺序彻底不一样的顺序完成(达到持久化的结束状态)。假设数据库奔溃或重启,这些独立事务是否回滚的决策是分开的。则跟踪并撤销部分完成事务的逻辑保留在数据库引擎中,就像写入简单磁盘同样。在重启过程当中,在容许数据库访问存储卷以前,存储进行自身的崩溃恢复,而不关心用户层的事务。固然,要保证数据库看到的存储系统是单一的视图,尽管实际上存储是分布式的。

存储服务决定一个最高的LSN,保证在在这个LSN以前的日志记录都是能够读取的。(VCL,volume Complete LSN)。在恢复过程当中,对应LSN任何高于VCL的重作日志都要丢弃。可是,数据库还能够进一步约束条件,取其子集,用以标记哪些日志能够丢弃。这个子集就是CPL集(Consistency Point LSNs)。所以,咱们还能够定义一个VDL(volume durable LSN)为小与VCL的最大CPL。举个例子,虽然数据完成到LSN为1007,可是数据库标记的CPL集为900,1000,1100。在这种状况下,咱们丢弃的位置为1000(1000之后的都要丢弃),也就是咱们完成到1007(VCL),可是持久到1000(VDL)。

所以,完成性与持久性是不一样的。CPL能够看做存储事务必须有序接受的某种限制形式。若是客户端没有对此加以区别,咱们把每一个日志记录标记为一个CPL。实际上,数据库与存储以以下方式交互:

每一个数据库层面的事务,都被打断为多个迷你事务(mini-transactions,MTRs),迷你事务是有序的,并且是原子的方式执行(就是不可分割地执行)。

一个迷你事务由多条连续的日志记录组成(要多少有多少)。

迷你事务的最后一条日志记录就是一个CPL

在崩溃恢复过程当中,数据库告诉存储层为每一个PG创建一个持久点,用来创建一个VDL,而后发起丢弃高于VDL日志的命令。

4.2 正常操做

咱们如今描述数据库的“正常操做”, 依次关注写、读、提交与复制。

4.2.1 写

在Aurora中,数据库不停地与存储服务交互,而且维护状态以创建仲裁,推动卷持久性(volume durablity),而且在提交时注册事务。举个例子,在正常/前转路径中,当数据库接收到响应消息为每批日志创建写仲裁,则推动当前VDL。在任意给定时刻,数据库可能有大量的并发事务活动。每一个事务均产生日志,数据库又为每条日志分配惟一的有序的LSN,可是分配受限于一条原则,LSN不能大于当前VDL与一个常量值的和。这个常量称为LSN分配限值(LAL, LSN Allocation limit)(目前设置为一千万)。这个限值保证数据库的LSN不超出存储系统太远,在存储或者网络跟不上时,提供一个反压,用以调节来入的写请求。

注意,每一个PG的每一个段仅仅能看到存储卷日志记录的子集,该子集的日志仅仅影响驻留于该段的页。每一个日志记录包含一个指向PG中前一个日志记录的反向连接。这些反向连接能够跟踪达到每一个段的日志记录的完成点,用以创建段完成LSN(Segment Complete LSN, SCL), SCL是表示一个最大LSN,在该LSN之下全部的日志记录均已经被该PG接收到。存储节点使用SCL相互交流,用来查找并交换得到本身确实的那部分日志。

4.2.2 提交

在Aurora中,事务提交是异步完成的。当客户端提交一个事务,处理事务提交的线程记录一个“提交LSN”(commit lsn)到一个单独的等待提交的事务列表中,而后将事务搁置去处理其余任务。这至关于WAL协议是基于事务提交的完成,也即当且仅当最新的VDL大于或等于事务的提交LSN的时候。随着VDL的推动,数据库在正在等待提交事务识别符合条件的,由专有线程向正在等待的客户端发送提交响应消息。工做线程不会由于提交事务而暂停,它们仅是把其余的挂起的请求拉起继续处理。

4.2.3 读

在Aurora中,和大部分的数据库同样,页由buf和cache提供。只有页是否在Cache中还存疑的时候,才会致使存储IO请求。

若是buf cache已满,系统找出受害页,将其赶出cache。在传统数据库中,若是受害页是“脏页“,替换前要刷盘。这就保证后续的页的提取,老是最新数据。然而Aurora在驱逐页的时候并无将页写出,但它强制执行一个保证:在buff或cache中的页始终是最新的版本。实现这一保证的措施是,仅仅将“页LSN(Page LSN)“(与页相关最新的日志LSN)大于或者等于VDL。这个协议保证了:(a)页的全部修改在日志中都已硬化, (b)在cache没命中的时候,得到最近的持久化版本的页,请求当前VDL版本页就足够了。

数据库在正常状况下不须要使用读仲裁来创建一致性。当从磁盘读取一个页,数据库创建一个读取点(read-point)

表示读取发起时候的VDL。数据库能够选择一个相对于读取点数据完整存储节点,从而取获得最新版本的数据。存储节点返回的数据页必定与数据库的迷你事务(mtr)的预期语义一致。由于,数据库管理着往存储节点日志的馈送,跟踪这个过程(好比,每一个端的SCL),所以它知道那些段知足读取要求(那些SCL大与读取点的段)。而后直接向有足额数据的段发起读请求。

考虑到数据库知道那些读操做没有完成,能够在任什么时候间在每一个PG的基础上计算出最小的读取点LSN(Minimum Read Point LSN). 若是有存储节点交流写的可读副本,用来创建全部节点每一个PG的最小读取点,那么这个值就叫作保护组最小读取点LSN(Protection Group Min Read Point LSN, PGMRPL). PGMRPL用来标识“低位水线”,低于这个“低位水线”的PG日志记录都是没必要要的。换句话说,每一个存储段必须保证没有低于PGMRPL的读取点的页读请求。每一个存储节点能够从数据库了解到PGMRPL,所以,能够收集旧日志,物化这些页到磁盘中,再安全回收日志垃圾。

实际上,在数据库执行的并发控制协议,其数据库页和回滚段组织方式,与使用本地存储的传统数据库的组织方式并没有二致。

4.2.4 复制节点(replicas)

在Aurora中,在同一个存储卷中,一次单独的写最多有15个读复制。所以,读复制在消耗存储方面,并无增长额外的代价,也没有增长额外的磁盘写。为了最小化延迟,写生成的日志流除了发送给存储节点,也发送全部的读复制节点。在读节点中,数据库依次检查日志流的每一天日志记录。若是日志引用的页在读节点的缓冲缓存中,则日志应用器应用指定的日志记录到缓存中的页。不然,简单丢弃日志记录。注意从写节点的视角来看,复制节点是异步消费日志流的,而写节点独立于复制节点响应用户的提交。复制节点在应用日志的时候必须听从以下两个重要规则:(a)只有LSN小于或等于VDL的日志记录才能应用。(b)只有属于单个迷你事务(MTR)的日志记录才能被应用(mtr不完整的日志记录不能应用),用以保证复制节点看到的是全部数据库对象的一致视图。实际上,每一个复制节点仅仅在写节点后面延迟一个很短的时间(20ms甚至更少)。

4.3 恢复

大部分数据库(如ARIES)采用的恢复协议,取决因而否采用了能表示提交事务全部的精确的内容的先写日志(write ahead log,WAL)。这些系统周期的作数据库检查点,经过刷脏页到磁盘,并在日志中写入检查点记录,粗粒度地创建持久点。在重启的时候,任何指定的页均可能丢失数据,丢失的数据多是已经提交了的,也可能包含未提交的。所以,在崩溃恢复的过程当中,系统从最近的检查点开始处理日志,使用日志应用器将这些日志应用日志到相关数据库页中。经过执行相应的回滚日志记录,能够回滚崩溃时正在运行的事务,这个过程给数据库带来了故障点时候的一致性状态。崩溃恢复是一个代价高昂的操做,减小检查点间隔能够减小代价,可是这会带来影响前端事务的代价。传统数据库须要在二者作权衡,而Aurora则不须要这样的权衡。

传统数据库有一个重要的简化原则是,使用同一个日志应用器,推动数据库状态与进行同步的日志恢复。而且此时从前端看来,数据库库是脱机的。Aurora设计也遵循一样的原则。可是日志应用器已经从数据库中解耦出来,直接在存储服务中在老是在后台并行运行。一旦数据库开始执行卷恢复,它会与存储服务合做来进行。结果是,Aurora数据库恢复速度很是快(一般在10秒如下),哪怕是每秒运行100,000条语句时候崩溃后状况下作恢复。

Aurora数据库不须要在崩溃以后重建运行状态。在崩溃的状况下,它会联系每一个PG,只要数据知足了写仲裁的条件,段的读仲裁彻底能够保证能发现这些数据。一旦数据库为每一个PG创建了读仲裁,则能够从新计算VDL,生成高于该VDL的丢弃范围(truncation range),保证这个范围后的日志都要丢弃,丢弃范围是这是数据库能能够证实的最大可能保证未完成的日志(不是完整MTR的日志)可见的结束LSN。而后,数据库能够推断出LSN分配的上限,即LSN的上限能超过VDL多少(前面描述的一千万)。丢弃范围使用时间数字(epoch number)做为版本,写在存储服务中,不管崩溃恢复仍是重启,都不会弄混丢弃日志的持久化。

数据库仍然须要作undo恢复,用来回滚崩溃时正在进行的事务。可是,回滚事务能够在数据库联机的时候进行,在此以前,数据库已经经过回滚段的信息,创建了未完成事务列表。

05 放在一块儿

本段中,咱们描述如图5所示Aurora的各个模块。

Aurora库是从社区版的mysql/innodb数据库fork出来的分支,与之不一样的主要在读写磁盘数据这块。在社区版的Innodb中,数据的写操做修改buffer中的页,相应的日志也按LSN顺序写入WAL的缓存中。在事务提交的时候,WAL协议只要求事务的日志记录写入到磁盘。最终,这个被修改的缓存页使用双写技术写入到磁盘中,使用双写的目的是为了不不完整的页写(partial page writes)。这些写操做在后台执行,或者从cache驱逐页出去时候执行,或者在检查点时候执行。除IO子系统外,innodb还包含事务子系统,锁管理器,B+树的实现,以及“迷你事务”(MTR)。innodb的将一组不可分割执行(executed atomically)的操做称为一个迷你事务(例如,分裂/合并B+树页)。

Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

在Aurora的Innodb变种中,mtr中原先那些不可分割执行日志,按照批次组织,分片并写入到其所属的PG中,将mtr最后一个日志记录标记为一致点(consistency point), Aurora在写方面支持与社区mysql同样的隔离等级(ANSI标准级别,Snapshot 隔离级,一致性读)。Aurora读复制节点能够从写节点获取事务开始和提交的持续信息,利用这些信息支持本地只读事务的snapshot隔离级。注意,并发控制是彻底有数据库实现的,对存储服务没有任何影响。存储服务为下层数据提供了展示了一个统一视图,与社区版本innodb往本地存储写数据的方式,在逻辑上彻底等效。

Aurora使用亚马逊关系数据库服务(RDS)做为控制平台。RDS包含一个数据库实例上的代理,监控集群的健康状况,用以决定是否进行故障切换,或者是否进行实例替换。而实例能够是集群中的一个,集群则由一个写节点,0个或者多个读复制节点组成。集群中全部的示例,均在同一个地理区域内(例如,us-east-1,us-west-2等)。可是放置在不一样的可用区内。链接同一个区域的存储机群。为了安全起见,数据库、应用、存储之间的链接都被隔离。实际上,每一个数据库实例均可以经过三个亚马逊虚拟私有云(VPC)进行通讯:用户VPC,用户应用程序能够经过它与数据库实例进行交互。RDS VPC,经过它数据库节点之间,数据库与控制平台之间进行交互。存储VPC,用以进行数据库与存储之间的交互。

存储服务部署在EC2虚拟机集群中。这些虚拟机在一个区域内至少跨三个可用区,共同负责多用户提供存储卷、以及这些卷的读写、备份、恢复。存储节点负责操做本地SSD,与对等节点或数据库实例进行交互,以及备份恢复服务。备份恢复服务持续的将数据的改变备份到S3,而且依据需求从S3恢复。存储控制平台使用亚马逊DynamoDB服务存储集群的永久数据、存储卷配置、卷的元数据、备份到S3的数据的描述信息。为了协调长时间的操做,如数据库卷恢复操做,修复(从新复制)存储节点失效等,存储控制平台使用亚马逊简单工做流服务(Amazon Simple Workflow Service)。维持高可用性,须要在影响用户以前,主动地、自动地、尽早地检测出真正的或潜在的问题。存储操做各个关键的方面都要经过metric收集服务进行持续监控,metric会在关键性能、可用性参数值指示出现异常进行报警。

06 性能结果

在本段中,咱们分享了自2015五月Aurora达到“GA”(基本可用)以后的运营经验。咱们给出工业标准的基准测试结果,以及从咱们客户得到的性能结果。

标准基准测试结果

这里展现了在不一样的试验下,Aurora与mysql的性能对比结果。实验使用的是工业标准的基准测试,如sysbench和TPC-C的变种。咱们运行的mysql实例,附加的存储是有30K IPOS的EBS存储卷,除非另有说明,这些mysql运行在有32 vCPU和244G内存的r3.8xlarge EC2实例中,配备了Intel Xeon E5-2670 v2 (Ivy Bridge) 处理器,r3.8xlarge的缓存缓冲设置到170GB。

07 学到的经验

咱们能够看到客户运行的大量的各类应用,客户有小型的互联网公司,也有运行大量Aurora集群的复杂组织。然而许多应用的都是标准的用例,咱们关注于这些云服务应用中共同的场景和指望,以引领咱们走向新方向。

7.1 多租户与数据库整合

咱们许多客户运营软件即服务(SaaS)的业务,有专有云业务客户,也有将企业部署的遗留系统转向SaaS的客户。咱们发现这些客户每每依赖于一种不能轻易修改应用。所以,他们一般把他们本身的不一样用户经过一个租户一个库或模式的方式整合到一个单实例中。这种风格能够减小成本:他们避免为每一个用户购买一个专有实例,由于他们的用户不可能同时活跃。举个例子,有些SaaS的客户说他们有50,000多用户。

这种模式与咱们众所周知的多租户应用如Saleforce.com不一样,saleFore.com采用的多租户模式是,全部用户都在同一模式统一的表中,在行级记录中标记租户。结果是,咱们看到进行数据库整合的用户拥有巨量的表。生产实例中拥有超过150,000个表的小数据库很是的广泛。这就给管理如字典缓存等元数据的组件带来了压力。这些用户须要(a)维持高水平的吞吐量以及并发的多用户链接,(b)用多少数据就购买提供多少的模式,很难进一步预测使用多少存储空间,(c)减小抖动,最小化单个用户负载尖峰对其余租户的影响。Aurora支持这些属性,对这些SaaS的应用适配得很好。

7.2 高并发自动伸缩负载

互联网负载须要处理由于突发事件致使的尖峰流量。咱们的主要客户之一,在一次高度受欢迎的全国性电视节目中出现特殊情况,经历了远高于正常吞吐量峰值的尖峰,可是并无给数据库带来压力。要支持这样的尖峰,数据库处理大量并发用户链接显得十分的重要。这在Aurora是可行的,由于下层存储系统伸缩性如此之好。咱们有些用户运行时都达到了每秒8000个链接。

7.3 模式升级

现代web应用框架,如Ruby on Rails,都整合了ORM(object-relational mapping, 对象-关系映射)。致使对于应用开发人员来讲,数据库模式改变很是容易,可是对DBA来讲,如何升级数据库模式就成为一种挑战。在Rail应用中,咱们有关于DBA的第一手资料:“数据库迁移“,对于DBA来讲是"一周作大把的迁移“,或者是采用一些回避策略,用以保证未来迁移的不那么痛苦。而Mysql提供了自由的模式升级语义,大部分的修改的实现方式是全表复制。频繁的DDL操做是一个务实的现实,咱们实现了一种高效的在线DDL,使用(a)基于每页的数据库版本化模式,使用模式历史信息,按需对单独页进行解码。(b)使用写时修改(modify-on-write)原语,实现对单页的最新模式懒更新。

7.4 可用性与软件升级

咱们的客户对云原生数据库如何解决运行机群和对服务器的补丁升级的矛盾,有着强烈的指望。客户持有某种应用,而这些应用有主要使用Aurora做为OLTP服务支持。对这些客户来讲,任何中断都是痛苦的。因此,咱们许多客户对数据库软件的更新的容忍度很低,哪怕是至关于6周30秒的停机时间。所以,咱们最近发布了零停机补丁(zero downtime patch)特性。这个特性容许给用户补丁可是不影响运行的数据库链接。

如图5所示,ZDP经过寻找没有活动事务的瞬间,在这瞬间将应用假脱机到本地临时存储,给数据库引擎打补丁而后更新应用状态。在这个过程当中,用户会话仍然是活动的,并不知道数据库引擎已经发生了改变。
Amazon Aurora:高吞吐量的云原生关系数据库的设计考量

08 相关工做

在本段中,咱们讨论其余人的贡献,这些贡献与Aurora采用的方法相关。

存储与引擎解耦虽然传统的数据都被设计成单内核的守护进程。可是仍有相关的工做,将数据库内核解耦成不一样的组成部分,例如,Deuteronomy就是这样的系统,将系统分为事务组件(Tranascation Component, TC)和数据组件(Data Componet ,DC).事务组件提供并发控制功能已经从DC中作崩溃恢复的功能,DC提供了一个LLAMA之上的访问方法。LLAMA是一个无闩锁的基于日志结构的缓存和存储管理系统。Sinfonia 和 Hyder则从可拓展的服务抽出了事务性访问方法,实现了能够抽象使用这些方法的数据库系统。Yesquel系统实现了一个多版本分布式平衡树,而且把将并发控制从查询处理中独立出来。Aurora解耦存储比Deuteronomy、Hyder、Sinfonia更为底层。在Aurora中,查询处理,事务,并发,缓存缓冲以及访问方法都和日志存储解耦,而且崩溃恢复做为一个可拓展的服务实现的。

分布式系统 在分区面前,正确性与可用性的权衡早已为人所知,在网络分区去状况下,一个副本的可串行化是不可能的,最近Brewer的CAP理论已经证实了在高可用系统在网络分区的状况下,不可能提供“强”一致性。这些结果以及咱们在云级规模的经验,激发了咱们的一致性目标,即便在AZ失效引发分区的状况下也要一致性。

Brailis等人研究了高可用事务系统(Highly Available Transactions, HATs)的问题。他们已经证实分区或者高网络延迟,并不会致使不可用性。可串行化、snapshot隔离级、可重复读隔离级与HAT不兼容。然而其余的隔离级能够达到高可用性。Aurora提供了全部的隔离级,它给了一个简单的假设,即任什么时候候都只有一个写节点产生日志,日志更新的LSN,是在单个有序域中分配的。

Google的Spanner提供了读和写的外部一致性,而且提供了跨数据在同一个时间戳上全球一致性读。这些特性使得spanner能够一致性备份,一致性分布式查询处理以及原子模式更新,这些特性都是全球规模的,哪怕是正在运行的事务的时候,也支持。正如Bailis解释的同样,Spanner是为google的重-读(read-heavy)负载高度定制的。采用两阶段提交,读/写事务采用两阶段锁。

并发控制弱一致性与弱隔离级模型在分布式数据库中已为人所知。由此产生了乐观复制技术以及最终一致性系统在中心化的系统中,采用的其余的方法,有基于锁的悲观模式(),采用如多版本并发控制乐观模式,如Hekaton,如VoltDB采用分片方法,Deuteronomy和Hyper采用时间戳排序的方法。而Aurora给数据库系统提供了一个持久存在的抽象的本地磁盘,容许引擎本身处理隔离级与并发控制。

日志结构存储1992年LFS就引入了日志结构存储系统。最近Deuteronomy和LLMA的相关工做,以及Bw-tree以多种方式在存储引擎栈中采用日志结构技术,和Aurora同样,经过写入差量而不是写整页减小写放大。Aurora与Deuteronomy都实现了纯重作日志系统,而且跟踪最高持久化的LSN,用以确认提交。

崩溃恢复 传统数据库依赖于ARIES的恢复协议,如今有些数据库为了性能而采用了其余途径。例如,Hekaton与VoltDB采用某种形式的更新日志来重建崩溃后的内存状态。像Sinfonia系统采用如进程对以及复制状态机的技术来避免崩溃恢复。Graefe描述一种使用每页日志记录链的系统,能够按需逐页重作。能够提升崩溃恢复的速度。Aurora与Deuteronomy不须要重作崩溃恢复的过程。这是由于Deuteronomy会将事务延迟,以便只有提交的事务的修改才会写入到持久存储中。于是,与Aurora不一样,Deuteronomy能处理的事务大小必然受到限制。

09 结论

咱们设计出了高吞吐量的OLTP数据库Aurora,在云级规模环境下,并无损失其可用性与持久性。主要思想是,没有采用传统数据库的单核架构,而是从计算节点中把存储解耦出来。实际上,咱们只从数据库内核中移走了低于1/4的部分到独立的弹性伸缩的分布式服务中,用以管理日志与存储。因为全部的IO写都要经过网络,如今咱们的基本的约束就在于网络。因此咱们关注于能缓解网络压力并能提升吞吐量的技术。咱们使用仲裁模型来处理大规模的云环境下的相关的复杂失效。而且避免了特殊节点带来的性能惩罚。使用日志处理减小总的IO负担。采用异步共识方法消除多阶段提交同步协议的繁复交互与高昂的代价,以及离线的崩溃恢复,和分布式存储的检查点。咱们的方法致使了简化系统架构,能下降复杂性,易于伸缩能为将来的发展打下基础。

文章我的解读

图1中所示的Data Plane中,数据库引擎那个框包住了Caching,而存储服务那个框,也包住了Caching。回味无穷。能够猜想,Cache是共享内存,数据库引擎和存储服务均可以访问。并且Cache不光是数据库引擎的一部分,并且也是存储服务的一部分。全文大部分篇幅讲如何优化写请求带来的网络负担,而只字未提读请求是否带来网络负担。所以,能够大胆猜想,一次写操做,不光是把日志应用到磁盘存储中,同时也把已经在cache的页也应用了,所以大部分的读请求不须要真实的存储IO,除非该页在cache中没有命中。从段6的Aurora测试能够看到,Aurora实例拥有的缓存至关大,通常的应用软件的数据库数据都能放在内存中。从图3也能够看到,AZ1主实例,也会向AZ2,AZ3数据库从实例发送日志与元数据,既然已经将日志处理从数据库引擎解耦出来了,发送的日志由谁应用呢?我的认为,AZ2与AZ3接收到日志,仍然是有存储服务应用的,只不过Cache是存储服务与数据库引擎共享的。

并且有这个猜想,咱们进一步解读零停机补丁的实现方式。ZDP如何保持用户链接实现假脱机?我的认为,应用到数据库之间有proxy。使用proxy来保持应用程序的链接,并从新与更新后数据库实例创建链接,并且要作到用户会话无感受,Cache是共享内存是必要的。由于Cache保存了大部分数据库运行状态,重启后的数据库实例仍然继续进行用户会话的查询。这个和使用共享内存的postgre有点像,你随意杀死postgre某些进程,并不影响用户会话的查询。

此外,我的认为,Aurora的计算节点,有可能也是存储节点。虽然逻辑上是存储与计算分离的,可是也能够享受不分离带来的好处,好比,存储服务能够直接往Cache应用日志,使得数据库引擎在大部分读请求不须要真实的IO。

Aurora有没有checkpoint?

Aurora的PGMRPL能够认为是检查点。LSN小于这个点的数据页已经刷盘,而大于这个点的页能够刷盘,也能够不刷盘,LSN小于这个点的日志均可以丢弃。PGMRPL在逻辑上与存储引擎的检查点是等效的。能够认为是PG上的检查点。

Aurora与polardb

Aurora与Polardb都是存储与计算分离、一些多读、共享分布式存储的架构。很显然,polardb在写放大上面没有作优化。从节点须要同步主库的脏页,共享的存储,私有的脏页,是个很难解决的矛盾。所以polardb的读节点会影响写节点。而Aurora能够认为没有脏页。日志即数据库,能够把日志应用器看做更慢的存储,存储服务与缓存均可以认为是日志应用器的Cache。

从节点与主节点之间有延迟,而aurora存储的数据页有多版本,文中明确指出存储有旧页回收处理。从节点依据读取点LSN读取到指定版本的页,文中段4.2.4指出,写节点到读复制节点之间的延迟小于20ms,所以,不可回收的旧版本页应该不会太多。

临时表

每一个读节点虽然只有查询操做,可是查询操做会生成临时表用以保存查询的中间结果。生成临时表数据是不会产生日志的。可是这里仍有写IO,我的认为,Aurora有可能直接写在本地存储,这样不会产生网络上的负担。

相关文章
相关标签/搜索