e-works数字化企业网  »  文章频道  »  基础信息化  »  云计算和虚拟化

Amazon Aurora:云时代的数据库 ( 上)

2017/8/13    来源:腾讯云    作者:佚名      
关键字:Amazon Aurora  云时代  
文章是 Amazon 在SIGMOD'17 上最新发表的关于 Aurora论文的翻译版本,详尽的介绍了 Aurora 设计背后的驱动和思考,以及如何在云上实现一个同时满足高并发、高吞吐量、高稳定性、高可用、高扩展的云数据库。
    Aurora是AWS服务的一部分,为OLTP业务提供关系型数据库服务。本文介绍了Aurora的系统架构以及背后设计上的考虑。我们认为,高吞吐量数据处理的核心问题已经从计算和存储移到了网络IO。为了解决这个问题,Aurora提出了一种新的关系型数据库架构,将REDO日志的处理下沉到一个专门为Aurora定制的多租户可扩展的存储服务上。我们介绍了这种架构的一些优点,包括减少了网络流量,可以实现快速的故障恢复,无损的故障切换到备机,提供容错并且自愈的存储服务。接着,我们介绍了Aurora如何使用一种高效的异步方法,在大量的存储节点上实现可持久化状态的一致性,避免使用昂贵且沟通复杂的恢复协议。最后,基于在生产环境运维Aurora 18个月的经验,我们分享了从客户上学习到一些心得:客户期望现代云服务中的数据库层是怎样的。
 
    1. 引言
 
    IT业务现在正加速向公有云迁移。这个产业级别的转变背后一个重要原因是,公有云能提供弹性的按需容量,(IT企业将这部分费用)作为经营性支出支付,而不用采用资本投入的模式。大量的IT业务需要支持OLTP的数据库,而提供与自建数据库等同甚至更高级的数据库服务,对支持这个长期转变的过程是至关重要的。
 
    在现代的分布式云服务中,弹性和可扩展性可以通过将计算和存储解耦,并在多个节点上提供存储的副本来实现。这样的结构可以让我们更容易的实现一些操作,比如替换掉异常或者不可达的主机,添加副本,主机故障后切换到副本,增加或者降低一个数据库实例的容量。在这种环境下,传统数据库所面临的IO瓶颈已经发生了变化。由于IO操作已经分布到一个多租户平台上的多个数据节点的多个数据盘上,单个数据盘或者节点不再是热点。取而代之的是,系统的瓶颈移动到发起这些IO操作的数据库层,以及真正执行这些IO的存储层之间。除了基本的PPS和带宽的瓶颈外,这里还存在着流量的放大效应,因为一个高性能的数据库必须并行的将数据写入到存储层。性能最低的节点、数据盘、网络路径决定着整体的响应时间。
 
    尽管数据库中的很多操作存在着交叉,还是有许多场景同步操作是必须的。这就导致了暂停和上下文切换。其中一个场景是,一次由于数据库缓存池未命中引起的磁盘读,这个时候读取线程在磁盘读完成之前是不能继续执行的。一次缓存未命中,可能带来额外的惩罚:将一个脏页剔除并写入到数据盘,腾出位置给新的页。另外,一些后台处理,如建立checkpoint或者刷脏页的操作,可以减少这种惩罚出现的几率,但是也会导致暂停、上下文切换以及资源竞争。
 
    事务的提交是另外一种(性能的)干扰项,一个提交的阻塞会导致后面的事务提交的无法处理。用多阶段同步提交协议,如2PC(2-phase commit),处理提交是一项极具挑战性的工作。在高度扩展的分布系统中,系统中存在着持续的软硬故障,这些协议在这种场景下的支持不够好,并且有较大的处理时延,因为分布式系统中的节点可能分布在多个数据中心。
 
    在本文中,我们介绍Amazon Aurora,一种通过将REDO日志分散在高度分布云服务环境中,来解决上述问题的新型数据库服务。Aurora使用了创新的面向服务的系统架构,使用多租户可扩展的存储服务层,来抽象虚拟化的分段REDO日志,并松散的与数据库实例层连接在一起。尽管每个数据库实例仍然包含一个传统数据库内核的大部分组件(查询处理器,事务,锁,buffer cache,访问方式以及UNDO日志的管理),一些功能(如REDO日志记录,持久化存储,故障恢复,备份以及恢复数据)都下沉交给存储层来做。
 
    相对于传统的数据库,Aurora的系统架构有三个重要的优势。首先,通过将存储构建为一个跨数据中心容错且自愈的服务,我们可以保护数据库免遭系统性能抖动以及网络或者存储层的短期或者长期的故障的影响。我们注意到,一个可持久化的故障可以认为是系统长时间不可用的事件,而系统的不可用时间又可以建模为长时间的系统性能抖动。一个设计良好的系统可以无差别地处理这些问题。其次,通过只将REDO日志写入存储层,我们可以将网络的IOPS降低一个数量级。一旦我们移除了这个瓶颈,我们可以更进一步地优化其他的竞争点,从而可以在原有的MySQL源码基础上有重大的提升。第三点,我们将一些很复杂且关键的功能(备份,REDO恢复),从原来是数据库引擎中的一次性的昂贵的操作,转变为均摊在一个大型分布式存储层上连续异步的操作。这样,我们可以实现近乎即时的不需要checkpoint的故障恢复,以及廉价的不影响前台处理的备份操作。
 
Amazon Aurora:云时代的数据库 ( 上)
 
    在本文中,我们首先介绍三个主要贡献:
 
    1、如何在云规模上实现可持久性,如何设计一个多数派系统以应对关联故障(第二节)
 
    2、如何将传统数据库最下面的一部分下沉到存储层来实现智能的存储(第三节)
 
    3、如何在分布式存储中移除多阶段的同步,如何故障恢复以及建立checkpoint(第四节)
 
    我们接着在第五节展示如何将这三个想法结合起来设计Aurora的整体架构,紧接的第六节是我们的性能测试结果。第七节讲的是我们在这个过程中学习到的心得。最后,我们在第八节简要地介绍了相关的工作。第九节是结束语。
 
    2. 大规模系统中的可持久性
 
    数据库专门设计来满足一种协议,一旦数据写入,就可以被读出来。不过,不是所有的系统都是这样的。我们在这一节介绍我们的多数派模型以及对数据分段背后的理念,将这两者结合起来,如何既能实现可持久性、可用性、减少抖动,又能帮助我们解决大规模存储层的运维问题。
 
    2.1 复制以及关联故障
 
    实例的生命周期与存储的生命周期不是强耦合的。实例可以挂掉,用户也可以将他们停掉,也可以根据负载升级或者降级实例。基于这些原因,将存储层和计算层分开是有实际意义的。
 
    一旦分离了计算和存储,存储节点本身和数据盘也会故障挂掉。因而,他们必须以某种形式复制来应对故障。在大规模的云环境中,长期存在着低频的节点、数据盘、网络路径故障的背景噪音。每一个故障可能具有不同的持续时间和影响范围。举个例子,可能某一个节点会存在短暂的网络不可用的情况,由于重启引起的短暂的停服,或者也存在着一个数据盘、节点、机架、网络交换设备的叶子或者主干,甚至整个数据中心的永久性故障。
 
    在一个复制系统的里面应对故障的一个方案是,使用基于多数派投票的协议。如果V个副本每个都有一个投票权,那么一个读或者写操作必须分别获得读多数派Vr票,以及写多数派Vw票。为了保证一致性,这些多数派必须满足两个规则。首先,为了读到最新的数据,必须满足Vr+Vw > V。这个规则保证最近的一次写多数派和读多数派至少包含相同的一个节点,从而保证读到最新的数据。其次,为了避免写冲突,感知到最新的写入操作,写操作的涉及的副本数必须满足Vw > V/2。
 
    通常的为了避免一个节点故障的方式是将数据复制三份,设置V为3,读多数派为Vr=2,写多数派为Vw=2。
 
    但是我们认为设计2/3为多数派是不够的。为了理解这是为什么,我们必须先理解AWS中可用区的概念。一个可用区是一个地域的子集,与该区域的其他可用区通过低延时的链路连接。可用区之间对很多故障是隔离的,包括供电、网络、软件、洪灾等。将数据副本存放在不同的可用区中,可以保证通常的故障模式只会影响到一个副本。这也就意味着,用户只要将三个副本存放在不同的可用区中,就可以应对大规模的事件和小范围内个别的故障。
 
    我们将Aurora设计为能容忍(a)挂掉整个可用区以及一个额外的节点而不影响读取数据,(b)挂掉一整个可用区而不影响写入数据。我们通过将数据复制为6个副本,存放在3个可用区中,每个可用区2个。我们将大多数派模型中的V值设为6,这样写多数派为Vw=4,读多数派为Vr=3。通过这个模型,我们在挂掉一个可用区加一个节点仍然提供读服务,挂掉一个可用区仍然提供写服务。确保读多数派,能使我们添加一个副本就可以重建写多数派。
 
    2.2 分段存储
 
    我们考虑一下AZ+1的方案是否能提供足够的可持久性。为了在这个模型中保持足够的可持久性,必须保证两个不相关故障成对出现的概率(平均故障间隔),要比平均修复时间小得多。如果成对故障出现的概率非常高,可能会导致一个AZ故障,从而形成不了多数派。过了某个点之后,很难去进一步降低独立事件的平均故障时间。因而,我们将重点放在通过降低平均修复时间来降低成对故障的影响。我们采用的具体做法是,将数据库的总容量划分为固定大小的数据段,大小为10G。每个数据段有6个副本,组成一个Protect Group(PG),分布在3个AZ中,每个AZ 2个。一个Aurora数据卷通过一组PGs连接而成,物理上由一组挂载本地SSD的EC2主机作为一个存储节点,每个存储节点有多个存储单元。通过分配更多的PG,可以线性的扩展数据卷的容量,Aurora支持的最大数据卷(一个副本)容量为64T。
 
    数据段是系统中最小的故障和恢复单元,自动的监控和修复故障是整个服务的一个部分。之所以选择10G,是因为在万兆网络条件下,恢复一个数据段只需要10秒钟。在这种情况,如果要打破多数派,那么必须同时出现两个数据段同时故障加上一个AZ故障,同时AZ故障不包含之前两个数据段故障的独立事件。通过我们对故障率的观察,这种情况出现的概率足够低,即使是在我们现在为客户服务的数据库量级上。
 
    2.3 韧性的运维优势
 
    一旦我们设计了一个能对长时间故障保持韧性的系统,那么这个系统就能轻松处理短时间的故障了。一个存储系统如果能应对一个AZ的长时间故障,也能应对由于停电或者软件故障引起的短时间服务不可用。同理,如果能应对一个多数派中的成员数秒钟的失联,当然也能处理短时间的网络拥塞或者存储节点的高负载。
 
    由于Aurora系统对故障有着高度的忍耐性,我们可以通过这一点来处理导致数据段不可用的运维操作。举个例子,热点管理可以变得很直观。我们可以直接将一个热点数据盘或者节点标记为故障,通过将数据迁移到冷存储节点上来迅速地修复多数派。而操作系统和安全漏洞修复对于存储节点来说,就是一个短时间的不可用事件。甚至,存储层的软件升级也可以类似的处理。我 每次处理一个AZ,同时保证同一个PG内没有两个副本所在的节点同时被处理。基于这些,我们在存储服务上可以使用敏捷方法和快速部署。
 
    3. 日志即数据库
 
    在这一节,我们阐释了为什么传统的数据库使用分段冗余的存储系统,会引起不能承受的网络IO和同步阻塞等性能负担。接着,介绍Aurora采用的将日志处理交给存储服务层来做的方案,并且用实验数据说明了该方案能显著地降低网络IOs。最后,介绍了Aurora存储服务中使用的一些技巧,用于将同步阻塞和不必要的写操作最小化。
 
    3.1 成倍放大的写负担
 
    我们的模型中将数据整体容量分段,并将分段复制为6个副本形成4/6写多数派,给整个系统带来了韧性。不过,这个模型会让传统的数据库如MySQL对单次应用层的写入产生过多的真实IO操作,使得整个系统的性能无法接受。高IO被复制操作成倍的放大,产生的高包量PPS让系统负担很重。同时,这些IO操作也产生一些同步点,导致数据管道阻塞、延时被放大。虽然链式复制及其变种可以减少网络开销,但是仍然受困于同步阻塞以及延时放大。
 
    我们来审视一下写操作如何在传统的数据库中执行的。数据库系统如MySQL将数据页写到数据对象中(如堆文件、B树等),同时将REDO日志写入Write-Ahead日志WAL。每一条REDO日志包含着一个数据页的前镜像和后镜像的差异。将REDO日志应用到前镜像上可以得到数据页的后镜像。
 
Amazon Aurora:云时代的数据库 ( 上)
 
    在实际中,一些其他的数据也必须被写入。比如,考虑一对同步镜像的MySQL实例,通过部署在不同的数据中心形成主从结构来获取高可用性。在AZ1中有一个MySQL实例,通过EBS挂载带网络的存储。在AZ2中有一个从机,同样通过EBS挂载带网络的存储。写入到主EBS的数据会通过软件镜像同步到一个从EBS上。
 
    图2展示了数据库引擎需要写入的不同类型的数据,包括REDO日志,为支持任意时间回档归档到S3上的二进制日志,被修改的数据页,为了防止页损坏而双写的数据,还有元数据FRM文件。图中同样描述了IO流的顺序。在步骤1和2中,会写入数据到主EBS上,同时同步到在同一个AZ中的从EBS上,当两个都写完了才回复确认。接着,在步骤3中,写入数据会使用块级别的软件镜像同步到MySQL从机上。最后,在步骤4和5中,数据会被写到MySQL从机上挂载的一对主从EBS上。
 
    上面描述的MySQL镜像模型在现实中是不可取的,不仅是因为数据是如何写入的,同时也因为有哪些数据被写入。首先,步骤1、3、5是顺序且同步的。延时会因为同步写而累积。抖动会被放大,主要是因为即使是异步写,也必须等待最慢的一次操作,系统的性能由最坏的操作结果决定。从分布式系统的角度看,这个模型可以看作一个4/4写多数派模型,在故障和最坏操作的性能限制条件下很脆弱。其实,由OLTP应用产生的用户操作可能导致多种不同的类型的写入,而实际上代表的是同样的信息—比如,为了防止存储基础设施中的页损坏而设计的双写操作。
 
    3.2 REDO日志处理下沉到存储
 
    当一个传统数据库修改一个数据页,会产生一个REDO日志记录,并调用Log Applicator将其应用在内存中的页的前镜像上产生页的后镜像。事务的提交要求首先必须写入日志,数据页的刷盘可能会滞后。
 
Amazon Aurora:云时代的数据库 ( 上)
 
    在Aurora中,需要通过网络传输的写数据只有REDO日志。数据库层不会因为后台操作或者建立检查点而写入其他数据。取而代之的是,Log Applicator被下推到了存储层,用来在后台或者按需产生数据页。诚然,从头开始按每页修改的完整路径来生成每个数据页是相当昂贵的操作。因而,我们在后台不断地使用REDO日志来生成数据页,来避免每次都按需从头生成。注意到,后台的数据生成从正确性的角度来看完全是可选的:因为从存储引擎的角度出来,日志就是数据库,所有生成的数据页不过是日志的缓存。同时,不像建立检查点,只有有一连串修改记录的数据页需要重新生成。建立检查点,与完整REDO日志链的有关,而Aurora的数据页生成只与这个页的日志链有关。
 
    我们的方案即使是在由于复制引起的放大写的条件下,不仅减少了网络负载,而且还提供了可观的性能和可持久性。存储服务可以以并行独立任务的方式来扩展IO,并且不影响数据库引擎的吞吐量。举个例子,图3展示了一个Aurora集群,包括一个主实例和多个副本,部署在多个不同的可用区中。在这个模型中,主实例将REDO日志写入存储层,并将日志以及元数据的更新一起发送给副本实例。IO流根据目的地来将日志顺序打成batch,并将每个batch传给数据的6个副本并持久化到数据盘上。数据库引擎只要收到6个中的4个回复就形成了一个写多数派,此时可认为这些日志文件被持久化了。每个数据副本使用这些REDO日志将数据页的变更应用在他们的buffer cache中。
 
    为了测试网络IO,我们用SysBench跑了一个写压力测试,100G的数据量写入两个不同配置的数据库:一个是之前介绍的部署在不同可用的区的MySQL同步镜像,另外一个是Aurora(副本在不同的可用区)。对两个数据库实例,在r3.8xlarge EC2实例上运行测试30分钟。
 
    我们的测试结果归纳在表1中。在30分钟的测试过程中,Aurora可以负载比MySQL镜像多35倍的事务。每个事务所需的IO次比MySQL镜像少7.7倍。我们通过将更少的数据通过网络写,使得我们可以更激进地复制数据获得持久性和可用性,可以并发的请求来最小化性能的抖动。
 
Amazon Aurora:云时代的数据库 ( 上)
 
    将日志处理放在存储层可以通过一系列手段来提升可用性,包括减少故障恢复时间,消除由于后台操作如建立检查点、数据页写入以及备份等引起的性能抖动。
 
    我们来对比一下故障恢复。在传统的数据库中,系统必须从最近的一个检查点开始恢复,重放日志确保所有REDO日志都被应用。在Aurora中,可持久化REDO日志不断地、异步的应用在存储层,分布在各个数据节点上。如果数据页还没被生成,一个读请求可能会应用一些REDO日志来生成数据页。这样一来,故障恢复的过程被分散在所有的正常的前台操作中。在数据库启动的时候不需要做任何事情。
 
    3.3 存储服务的设计点
 
    存储服务的一个核心设计点是尽可能减少前台写操作的延时。我们将大部分的存储处理操作移到了后台。考虑到存储层从峰值到平均请求的巨大差异,我们有足够的时间在前台操作路径之外处理这些任务。我们也可以使用计算来换存储。举个例子,如果存储节点在忙着处理前台写请求的时候,没有必要运行GC来回收老的数据页版本,除非是数据盘快满了。在Aurora中,后台处理和前台处理是负相关的。这与传统的数据库不同,传统数据库后台的脏页刷盘和建立检查点与前台的负载是正相关的。在这样的系统中,如果后台积累了许多未处理的任务,那么必须扼制前台正常的处理流程才能防止后台任务越积累越多。由于在Aurora中数据段被分散在不同的存储节点上,一个存储节点卡死可以轻易被4/6写多数派处理,卡死的存储节点会被看作一个慢节点(不影响整体的流程)。
 
Amazon Aurora:云时代的数据库 ( 上)
 
    我们来进一步看看存储节点的处理流程。如图4所示,它包括以下的步骤:(1)收到日志记录并将其加入内存的队列,(2)持久化记录并确认写入,(3)整理日志记录并确认日志中有哪些缺失,因为有些包可能丢了,(4)与其他数据节点交互填补空缺,(5)用日志记录生成新的数据页,(6)不断的将数据页和REDO日志持久化到S3,(7)周期性的回收旧的版本,(8)最后周期性的对数据页进行CRC校验。
 
    注意上面的步骤都是异步的,只有步骤(1)和(2)是在前台操作的路径中,可能会影响延时。
 
责任编辑:李欢
本文为授权转载文章,任何人未经原授权方同意,不得复制、转载、摘编等任何方式进行使用,e-works不承担由此而产生的任何法律责任! 如有异议请及时告之,以便进行及时处理。联系方式:editor@e-works.net.cn tel:027-87592219/20/21。
e-works
官方微信
掌上
信息化
编辑推荐
新闻推荐
博客推荐
视频推荐