赞
踩
SIGMOD 2017
Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases.
Amazon Aurora是亚马逊开发的云原生分布式数据库,相关的文章于2017年发表于SIGMOD。亚马逊开发的云上服务一直是大家关注的重点之一,正是通过这篇paper才让我们能一窥这款云原生数据库的架构设计,本篇文章主要内容也是依据这篇paper而得到的。
从传统的单机数据库转变为RAC的数据库集群或者其他的分布式数据库,在这个过程中,最重要的改变是对性能的影响瓶颈,原先的单机数据库,计算节点和存储节点在物理上同属于一个节点,因此在requet I/O上并不会遇到太大的瓶颈,影响系统性能的主要问题是节点的计算能力和存储能力。而在分布式数据库系统中,由于存储节点和计算节点被解耦,底层的存储节点可以无限扩展,分布式数据库中的主要问题集中在网络对requset I/O的影响以及整体系统改如何高效地保证一致性上。
Aurora 是亚马逊云服务AWS中的关系型数据库服务,主要面向OLTP场景。其基本设计理念是云上环境下,数据库的最大瓶颈不再是计算或者存储资源,而是网络,因此Aurora在原本MYSQL基本架构的基础上,通过构造一套存储与功能层分离架构,在数据交互层上仅将日志处理下推到存储层,进而解决网络瓶颈。
Aurora的整体架构如上图所示,主要分为3层:
Aurora独立的存储层按照Quorum模型进行设计,基础的架构是如下图所示的3AZ,6副本架构。关于Aurora为什么选择这样的设计架构,需要首先解释Quorum是如何保证整体数据的一致性的。
关于Quorum模型机制:假设复制拓扑中有V个节点,每个节点有一个投票权,读请求或写请求必须拿到 V r V_r Vr 或 V w V_w Vw个投票才能返回。为了满足一致性,需要满足两个条件,首先 V r + V w > V V_r + V_w > V Vr+Vw>V ,这个保证了每次读都能读到拥有最新数据的节点;第二, V w > V / 2 V_w > V/2 Vw>V/2,每次写都要保证能获取到上次写的最新数据,避免写冲突。比如 V = 3 V=3 V=3,那么为了满足上述两个条件, V r = 2 V_r=2 Vr=2, V w = 2 V_w=2 Vw=2。
Aurora认为简单的2/3 Quorums是不够的。要理解为什么,需要首先了解AWS中可用区(AZ)的概念。AZ是通过低延迟链路连接到该区域其他AZ的区域的子集,但对于大多数故障(包括电源,网络,软件部署,洪水等)都是隔离的。跨AZ分发数据副本可确保典型的故障规模模式仅影响一个数据复制品。这意味着可以简单地将三个副本中的每个副本放置在不同的AZ中,并且除了较小的单个故障外,还可以容忍大规模事件。
3AZ的架构中,单一AZ上副本节点出现故障的概率通常是很高的,两AZ上副本节点同时故障的概率虽然低,但是并不意味着不会发生,而且,在单AZ单副本的架构下,这样的单一副本出错,就代表整个AZ的故障,因此这样的AZ设计并不符合Aurora的需求。
为了保证各种异常情况下的系统高可用,Aurora的数据库实例部署在3个不同AZ(Availablity Zone),这样保证最小的异地子集需求,每个AZ包含了2个副本,总共6个副本,每个A是一个独立的容错单元,包含独立的电源系统,网络,软件部署等。结合Quorum模型以及前面提到的两条规则, V = 6 V=6 V=6, V w = 4 Vw=4 Vw=4, V r = 3 Vr=3 Vr=3,Aurora可以容忍任何一个AZ出现故障,而不会影响写服务;任何一个AZ出现故障,以及另外一个AZ中的一个节点出现故障,不会影响读服务且不会丢失数据。
基于Quorum机制的读写一致性模型会导致每一个节点上接受到的log日志并不完备,如下图所示:
关于AZ+1是否能够提供足够的可持久性。为了在该模型中提供足够的可持久性,必须确保在修复其中一种故障所需的时间内(MTTR - 平均修复时间),发生不相关故障的双重故障概率(MTTF - 平均故障时间)足够低。如果出现双重故障的概率较高,可能会出现一个AZ故障,以致打破Quorum。到目前为止,很难降低MTTF在独立故障上的概率。因此Aurora将重点放在降低MTTR上,从而降低双重故障的影响。为此,Aurora将数据库卷分为固定大小的小段,目前设置的大小是10G。这些数据段每个都会复制6份,组成一个PG,因此每个PG包含6个10GB的数据段,分布在3个AZ中,每个AZ包含两个数据段。存储卷由一组PG组成,在物理上使用Amazon EC2挂载SSD作为单个节点,由大量存储节点构成。构成存储卷的PG集合随着存储卷的增长而增大。我们目前最大支持单个存储卷达64T。
存储节点构成。构成存储卷的PG集合随着存储卷的增长而增大。我们目前最大支持单个存储卷达64T。
现在,数据段是背景噪声最小的故障和修复单元。监控是自动修复故障是服务的一部分。在10Gbps的网络连接条件下,一个10G的数据段可以在10秒内被修复。因此,想要打破Quorum,就必须在10秒的时间窗口中同时发生两个数据段故障加上一个AC故障,并且AZ故障不包含此前故障的两个数据段。即使在我们为客户管理的数据库量级上,通过我们观察到的故障率,这种情况出现的概率非常低。
Aurora的模型将存储卷进行分段,并将每个段复制6份形成[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g747lQjx-1638100312542)(https://www.zhihu.com/equation?tex=4%2F6)]的Quorum,给系统带来了更高的可恢复性。然而,该模型会导致诸如MySQL这类的传统数据库性能无法接收,因为这些数据库对于每一个应用写入都会生成许多不同的实际的I/O。高I/O通过复制被放大,从而增加了沉重的PPS负担。同时,大量的I/O产生了同步点,导致数据管道停顿以及延迟增大。尽管链式复制以及变种可以降低网络开销,但是仍然无法避免同步停顿和延迟放大。
关于MYSQL是如何完成写入请求的:所有的SQL流程都包含在事务中完成,例如在InnoDB引擎中,事务的完成主要由WAL系统完成。常规的事务写流程中,包括redo-log、undo-log,以及MYSQL server中的binlog等等都将在写流程中生成,因此,如果使用传统数据库中的读写流程完成写请求,将会带来过重的写放大开销。此外,许多日志的写步骤要求同步完成,这也极大地影响了数据库地写入效率。
这样写日志要求显然是Aurora无法接受的,因此Aurora摒弃了复杂的日志写系统,仅保留redo-log,完成所有的写入过程。Aurora之所以能够选择仅使用redo-log作为唯一的日志,这与Quorum机制提供的保障相关,由于在写入层面扩大了需要写入的副本数,在另一方面可以减少单一副本写入时需要的日志数,从总体上看,提供了较好的性能和可持久性,存储服务可以通过并行的方式来扩展I/O,且不影响数据库引擎的写吞吐量。而Quorum机制造成的单一节点上的日志Gap可以通过Gossip协议完成填补,这一过程是在数据存储层异步完成,不影响数据库层的计算服务效率。具体的写入过程大致由下面的步骤完成:
通过以上的转换设计,Aurora相较于传统的MYSQL数据库所提供的事务读写能力得到了显著的提高,具体的实验数据可以在文中找到。
基于Aurora在文中提出的“日志即数据库”这个概念,可以看到,目前在Aurora中传递的唯一数据流就是redo-log了,那么基于redo-log,作为一个支持OLTP的数据库,Aurora是如何基于日志驱动各种事务流程的,这一点值得深入的探讨。
首先,引入一些基本概念
LSN(Log Sequence Number)
每个日志记录都有一条关联的日志序号,LSN由数据库生成,单调递增。
VCL(Volume Complete LSN)
表示存储服务拥有VCL之前的所有完整的日志,即六个副本中至少有四个拥有序号VCL的日志,故障恢复时,VCL之后的日志都要被截断。
CPL(Consistency Point LSN)
每个事务被拆分为多个有序的微事务(MTR,mini-transaction),这些微事务必须被完整的执行。每个微事务又是由多个日志记录组成,每个微事务最后一条日志记录被标记为CPL。
VDL(Volume Durable LSM)
所有副本中最大的CPL,并且必须小于或者等于VCL。(如果大于VCL说明还没有写成功,数据库服务器不需要更新数据)。也是Aurora中已同步的最大日志号。
LSN的概念是在MYSQL中就存在的,其他三个则是Aurora自己提出的概念,VCL和VDL的概念很好理解。而CPL概念的提出联系到了MYSQL中的MTR这个概念,这里对MTR不做赘述,这个概念是日志在物理层执行的原子标志,但并非一定是逻辑层日志结束的标志位,这里给出一些参考:
参考:https://blog.csdn.net/liang_0609/article/details/77334911
在Aurora中,数据库实例向存储节点传递redo日志,建立写Quorum后推进VDL,使数据库进入一个新的一致性状态。在任何给定时刻,数据库中可以有大量的并发事务处于活动状态,每个事务都会生成自己的redo-log记录,数据库会为每个日志记录分配一个唯一且有序的LSN。为了避免前台事务并发执行太快,Aurora定义了LSN Allocation Limit(LAL),规定分配的任何一个LSN都不大于当前的VDL和LAL常数的和,保证了数据库不会比存储系统领先太多,避免当存储或网络无法跟上时,后台压力过大导致写请求被堵塞。
在分段存储的部分,我们提到了PG对于写流程的作用,但是并没有展开。Aurora为了填补不同数据节点上的log Gap,需要用到Gossip协议,所谓Gossip协议,就是不同数据节点通过通讯来补充自己reldo-log链上缺失的redo-log。但具体来说,数据节点并不是直接完成一个redo-log的检查,因为redo-log可能很多,并且这个过程效率很低,但借助于PGs,Aurora可以提高Gossip协议的效率。每个PG的每个段仅看到卷中的一部分redo-log日志记录,这些记录都是只会影响本段上的页面。每个日志记录都包含一个反向链接,用于标识该PG先前的日志记录。这些反向链接可以用于追踪已到达的每个段的日志记录的完整性点,从而建立SCL(Segment Complete LSN)链表,当存储节点为了查找和交换它们丢失的记录而相互通信时(即Gossip协议执行中),就会使用SCL。同一PG中的独立数据段根据他们所拥有的日志数据链进行比对,对缺失的日内容进行补充。
在Aurora中,与大多数数据库一样,数据页的请求一般都从缓冲池中获得,当缓冲池中对应的数据页不存在时,才会从磁盘中获取。
在正常情况下,当数据库读数据时,不需要使用Quorum协议来读,只需访问redo日志全的存储节点即可(存储节点确认收到后数据库服务器会记录每个存储副本接收多少日志,所以其知道哪些存储副本有最新的数据)。但是在数据库服务器故障恢复的过程中,需要根据Quorum协议来读,以重建数据库运行时的一致状态。
如果缓存满了,系统会从缓存中逐出一个数据页。传统数据库系统中,如果该页是脏页,那么在替换前,会将其刷新到磁盘中。而Aurora数据库不会在逐出数据页时写磁盘,只有当页面的LSN小于等于VDL(持久化点)时,才将其从缓存中逐出。该协议确保:
在Aurora中,事务提交是异步完成的。每个事务由若干个日志组成,并包含有一个唯一的“commit LSN”,当VDL的位点大于事务的commit LSN时,表示这个事务redo-log都已经持久化,可以向客户端回包,通知事务已经成功执行。在Aurora中,有一个独立的线程处理事务成功执行的回包工作,而工作线程不会等待事务提交完成,它们会继续处理其它的事务。
关于Aurora的容灾策略:对于一个PG来说,它能在挂掉2个节点后依旧提供读写服务;再极端点挂掉3个节点时依旧能提供读服务(read quorum是3/6,write quorum是4/6)。注意按照每个节点存储10G数据的量来估算,一次恢复大概为10s,也就是上述情况潜台词都是在10s内同时挂掉的情况,而且3个AZ往往至少有一个是异地的,所以算是比较极端的情况了。
简单来说,全局一致的已完成LSN点VCL是恢复时的定标位置,所有大于VCL的LSN都要被放弃掉。
Aurora每一个Volume都要保证至少能满足read quorum(即至少有3个节点可以提供读服务),所以容灾恢复回退到VCL点后,也能保证与其它同组节点同步一致性点(consistency point)后能重新得到新的PGCL点和VCL点(说通俗点也就是能重新满足数据一致性)。
若真的同时挂掉了3个节点,也就是只满足read quorum但不满足write quorum时,这时候会通过read quorum修复错误分区。直到能重新提供读写服务时,Aurora会增加epoch,这个是针对整个protection group维护的值,它代表了当前组的状态,一旦组发生了改变都会导致该值自增。所有请求都需要带上epoch,如果epoch不符则存储节点不会接受该请求。相较之下,一些系统的方式是等待原有的连接过期,如此延迟必定是较高的。
此外,得益于Aurora的架构特点,回放日志的工作可以一直在存储节点后台做,任何一次读磁盘I/O操作,如果数据页不是最新版本,都会触发存储节点回放日志,得到新版本的的数据页。这与传统数据库的故障恢复操作本质是一样的,所以Aurora在故障恢复时不需要做太多的事,甚至可以等到需要访问某个数据页时再对这个页进行“故障恢复”,如此大大减少了故障恢复的时间。
参考:
https://zhuanlan.zhihu.com/p/102282192
https://www.jianshu.com/p/dd6aa53c3af5
https://zhuanlan.zhihu.com/p/319806107
https://blog.csdn.net/weixin_43827934/article/details/101392184
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。