返回 登录
1

Amazon Aurora深度探索(一)

【导语】 Amazon的Aurora自从问世,就备受关注,其性能和实现架构是被关注的热点。2017年,Amazon发表了一篇论文,披露其实现的一些技术细节。本文在此背景下,对Aurora系统的实现从整体架构、存储、事务处理三个方面进行深入探讨,并从数据库内核技术实现的角度对Aurora做了一定的推测。

2017年,Amazon在SIGMOD上发表了论文《Amazon Aurora: Design Considerations for High Throughput Cloud Native Relational Databases》

这篇论文描述了Amazon的云数据库Aurora的架构。基于MySQL的Aurora对于单点写多点读的主从架构做了进一步的发展,使得事务和存储引擎分离,为数据库架构的发展提供了具有实战意义的已实践用例。其主要特点如下:

  • 实践了“日志即数据库” 的理念。
  • 事务引擎和存储引擎分离。
    • 数据缓冲区提前预热。
    • REDO日志从事务引擎中剥离,归并到存储引擎中。
    • 储存层可以有6个副本,多个副本之间通过Gossip协议可以保障数据的“自愈”能力。
    • 主备服务的备机可达15份,提供强大的读服务能力。
  • 持续可靠的云数据库的服务能力。
    • 数据存储跨多个区:提供了多级别容灾能力。
    • 数据容灾能力:数据冗余、备份、实时恢复等多种能力集成到云服务,提高了数据的保障能力。
    • 万能数据库的概念呼之欲出。

之所以有这样的设计,是因为Amazon认为:网络I/O已经成为数据库最大的瓶颈。

Aurora的整体架构

认识Aurora的整体架构,需要先理解AWS的物理设施,而论文中对Aurora基于的物理设施着墨不多,所以我们先来掌握物理设施与整体架构的关系。

物理设施与架构

Aurora的计算节点和存储节点分离,分别位于不同的VPC(Virtual Private Cloud)中。这是Aurora架构最亮眼之处。

如图1,用户的应用通过Customer VPC接入,然后可以读写位于不同AZ(Availability Zone)的数据库。而不同的AZ分布于全球不同的Region中(如图2,截止到2017年初,AWS全球有16个区域即Region,有42个可用区即AZ,每个Region至少有2个AZ。而每个AZ由两到多个数据中心组成,数据中心不跨AZ,每个AZ内部的数据延迟低于0.25ms。AWS文档称,AZ之间的延迟低于2ms,通常小于1ms。

图1  Aurora整体架构

图1 Aurora整体架构

图2  Aurora的Region分布

图2 Aurora的Region分布

数据库的部署,是一主多从的集群架构,图1的Primary RW DB是写数据的节点,只能有一个(这点说明Aurora还是传统的数据库架构,不是真正的对等分布式架构,这点也是一些批评者认为Aurora缺乏真正创新之处的缘由)。而Secondary RO DB是只读的从节点,由零到多个备节点组成,最多可以有15个。主从节点可以位于不同的AZ(最多位于3个VPC,需要3个AZ),但需要位于同一个Region内,节点通过RDS(Relational Database Service)来交互。

RDS是由位于每个节点上称为(HM(Host Manager)的agent来提供主从集群的状态监控,以应对主节点fail over的问题以便进行HA调度,以及某个从节点fail over需要被替换等问题。这样的监控服务,称为Control Plane。

数据库的计算服务和存储分离,数据缓冲区和持久化的“数据”(对于Aurora实则是日志和由日志转化来的以page为单位的数据,而不是直接由数据缓冲区刷出的page存储的数据)位于Storage VPC中,这样和计算节点在物理层面隔离。一个主从实例,其物理存储需要位于同一个Region中,这样的存储称为EC2 VMs集群,其是由一个个使用了SSD的Storage Node组成。

核心技术与架构

Aurora提倡“the log is the database”,这是其设计的核心。围绕这个观点,传统数据库的组件架构发生了一些变化。

对于Aurora,每一个存储节点,如图3,由两部分构成。

图3  存储结构

图3 存储结构

第一部分:Caching

这是一个重要的关键点,可惜论文没有描述其细节。

如同传统数据库架构的数据缓冲区,向事务层提供数据。传统数据库架构的数据缓冲区,向上起着消耗存储I/O的I加载数据到内存供计算层读写数据的作用;向下起着消耗I/O的O写出脏数据到存储层以实现数据持久存储的作用。对于一个写密集的OLTP系统,大量随机写花费了很多时间,系统的性能因此经常表现为存储层的I/O瓶颈。 尽管checkpoint技术缓解了每个写操作刷出脏数据的冲动,尽管SSD的使用缓解了存储层的瓶颈,但是,毕竟存储层的I与O的时间消耗还是巨大的,尤其是对于随机写密集的OLTP系统。

Aurora的设计消除了脏数据刷出的过程,数据缓冲区的作用只是加载数据供上层使用,而脏数据不必从数据缓冲区刷出到物理存储上,这对于随机写密集的OLTP系统而言,是一个福音,性能的瓶颈点被去掉了一个(如图3,在“Caching”和“Logging+Storage”之间,竖线的箭头应该是指向“Caching”的,以表示数据只是加载到Caching中,不存在脏数据的刷出操作)。

但是,观察图3,“Caching”是位于了存储层内还是计算层内?论文没有明示。

从图3观察,似乎“Caching”是存储层和计算层所共用的一个组件,那么就可能存在这样的一个两层设计:位于存储层和计算层各有一部分“Caching”,这两部分“Caching”组合成为一个逻辑上的“Caching”,而逻辑意义上的“Caching”在AWS似乎认为其更像是属于计算层的。如下文引自论文原文:

Although each instance still includes most of the components of a traditional kernel (query processor, transactions, locking, buffer cache, access methods and undo management) several functions (redo logging, durable storage, crash recovery,and backup/restore) are off-loaded to the storage service.

位于存储层内的“Caching”,更像是一个分布式的共享文件系统,为了提高性能也许是一个分布式内存型的共享文件系统,为主从架构的数据库提供高速读服务,此点妙处,论文没有点出,这里权做推测。存储层如果能为所有的主备节点提供一致的缓冲数据,则有更为积极的意义,可以对比参考的如Oracle的RAC。

而位于计算层内的“Caching”,是单个数据库实例读数据的场所,独立使用。

Aurora提供了一个“自动恢复”缓存预热的功能,其官方宣称如下:

“自动恢复”缓存预热

当数据库在关闭后启动或在发生故障后重启时,Aurora将对缓冲池缓存进行“预热”。即,Aurora会用内存页面缓存中存储的已知常用查询页面预加载缓冲池。这样,缓冲池便无需从正常的数据库使用“预热”,从而提高性能。

Aurora页面缓存将通过数据库中的单独过程进行管理,这将允许页面缓存独立于数据库进行“自动恢复”。在出现极少发生的数据库故障时,页面缓存将保留在内存中,这将确保在数据库重新启动时,使用最新状态预热缓冲池。

源自:http://docs.amazonaws.cn/AmazonRDS/latest/UserGuide/Aurora.Overview.html

“在出现极少发生的数据库故障时,页面缓存将保留在内存中”,这句话很重要,一是其表明数据不用很耗时地重新加载了;二是数据库实例崩溃前的数据内存状态被保留着;三是数据库崩溃重启不必再执行“故障恢复”的过程即使用REDO日志重新回放以保障数据的一致性了(事务的ACID中的C特性)。

那么,页面缓冲是一直保留在哪个节点的内存中?是存储节点还是计算节点?如果是位于计算节点,那么备机节点发生数据库故障时,这样的机制不会对备机节点起到保护作用。如果是位于存储节点,则存储作为一个服务,服务了一主多备的多个节点,则能更好地发挥“自动恢复”缓冲预热的功效(存储节点的Caching一直存在,向上层计算节点的Caching提供数据批量加载服务,但也许不是这样,而是提供一个接口,能够向计算层的Caching提供高速读数据的服务,论文没有更多的重要细节披露,权做推测)。由此看来,“Caching”层的两层设计,当是有价值的(价值点是“自动恢复”缓冲预热,由存储层提供此项服务),与预写日志功能从事务层剥离是关联的设计。

这就又回到前面引用的论文中的那段英文,其表明 :Aurora的设计,把REDO日志、持久化存储、系统故障恢复、物理备份与物理恢复这些功能模块,归属到了存储层。由此就引出了Aurora的另外一个重要话题——存储层的设计(如下的第二部分和下一节内容)。

对于计算层的“Caching”,其实现将被大为简化。脏数据不再被写出,脏页面不再需要复杂的淘汰策略进行管理,消除了后台的写任务线程,同时也消除了checkpoint线程的工作,数据缓冲区的管理大为简化,既降低了系统的复杂度又减少了时间的消耗,还避免了因执行后台写等任务带来的性能抖动,解耦带来的功效确实宜人。Aurora额外需要做的一项新工作是:only pages with a long chain of modifications need to be rematerialized。而计算层的“Caching”变成单向的读入,此时需要解决的仅仅是什么样的数据可以(从存储层的Caching)被读入的问题,而论文原文描述:

The guarantee is implemented by evicting a page from the cache only if its “page LSN” (identifying the log record associated with the latest change to the page) is greater than or equal to the VDL.

VDL是存储层的最小一致点(参见下文“Aurora的事务处理-持久性”这一节的内容),标识了可用日志的最低范围,比VDL还老的数据页不再可用,所以显然如上的论文原文是错误的。如果有比当前数据页还新的数据页被从日志中恢复,则其LSN一定更大,所以页面换入的条件是:存储层Caching中存在页面的LSN值更大;页面被换出的条件是:Caching中页面的LSN小于等于VDL。而且,这一定是发生在备机需要更新其计算层Caching的时刻,而不是主机需要更新其计算层Caching的时刻。存在此种情况,其原因已经很明显,主机修改数据,形成脏页,这样的脏页(数据的后像)才能作为REDO日志的一部分被主机刷出。而主机不会刷出脏页,所以被修改后的数据页应该一直在内存中,而被修改过的数据页如果反复被修改,则意味着主机Caching中的相应脏页数据一定是最新的,没有必要从存储层的Caching中读入“绕道恢复后的数据页”。如果以上猜想不成立,除非Aurora生成REDO日志时,存于REDO日志中的数据页部分采取先复制然后其上的数据项被修改这样的方式。可是多做一次复制,又有何必要呢?

另外,如果“Caching”确实存在两层(另外一个证据,参见图4 ),而如“存储层的工作”一节所述,存储层也在处理日志,并依据日志生成页数据,则存储节点也存在处理数据的能力,就类似于Oracle的ExaData。这样可能导致两层的“Caching”还是存在差别的。存储层的“Caching”能够帮助做谓词下推的工作,然后把较少的数据传回计算层的“Caching”,由此实现类似Oracle ExaData的智能扫描(Smart Scan)功能。是否如此,或者Aorora的体系结构和功能模块在未来继续演变的时候,是否会在存储层内的“Caching”做足文章,可以拭目以待。不过,目前制约存储层内“Caching”起更大作用的因素,主要在于分布式事务机制的选取和InnoDB自身的事务实现机制。详细讨论参见下文“事务与数据分布”一节的内容。

图4  存储层的“Shared storage column”与计算层的“Caching”构成的两层数据缓冲结构

图4 存储层的“Shared storage column”与计算层的“Caching”构成的两层数据缓冲结构

第二部分:Logging+Storage

这部分是日志和持久化存储。日志与传统数据库对于预写日志(WAL)的利用方式与MySQL不同,这点是Aurora实现计算与存储分离的核心(下一节详述存储层实现细节)。

如图5所示,对于日志数据,从Primary RW DB写出到一个存储节点,每个AZ至少有2份数据,写出的日志数据会自动复制到3个AZ中的6个存储节点,当其中的多数节点回应写日志成功,则向上层返回写成功的ACK。这表明写日志信息采用了多数派协议(Quorum)。

MySQL的事务模型符合SS2PL协议 ,当日志成功写出,就可以在内存中标识事务提交成功 ,而写日志信息是一个批量的、有序的I/O操作,再加上Aurora去除了大量的缓冲区脏数据的随机写操作,因此Aurora的整体性能得到大幅提升。

借用官方论文的一组对比数据,可以感性认识存储和计算分离所带来的巨大好处,如图6所示,MySQL的每个事务的I/O花费是Aurora的7.79倍,而事务处理量Aurora是MySQL的35倍,相差明显。

对于主备系统之间,如图5所示,主备之间有事务日志(LOG)和元数据(FRM FILES)的传递。也就是说备机的数据是源自主机的。如图5所示的主备之间的紫色箭头,表示主机向备机传输的是更新了的元数据,绿色箭头表示日志作为数据流被发送给了备机(这个复制应该是异步的,相关内容请参考下文“存储层的工作”一节)。所以备机的数据更新,应该是应用了主机传输来的事务日志所致。这是论文中表述的内容,原文如下:

In this model, the primary only writes log records to the storage service and streams those log records as well as metadata updates to the replica instances.

图5  主从复制日志存储图

图5 主从复制日志存储图

图6  Aurora与MySQL主从复制架构性能数据对比

图6 Aurora与MySQL主从复制架构性能数据对比

但是,日志的应用功能是被放到了存储层实现的,如原文描述:

Instead, the log applicator is pushed to the storage tier where it can be used to generate database pages in background or on demand.

而官方的网站用图7描述了备机的数据,是从存储节点读入的。

图7  Aurora主备机数据流

图7 Aurora主备机数据流

鉴于以上几点,备机数据获取和更新的这个细节,算是个谜。

“Caching”如果确实分为两层,在存储层提供从日志中恢复成为数据页的形式而被缓冲,则主备系统之间应该没有必要再传输日志数据,对于备机而言,直接从统一的存储层的“Caching”中获取数据即可。

与此相关的一个问题是:为什么备机节点可以多达15个呢?难道仅仅是应对读负载吗? 或者,作为故障转移的目标,需要这么多备机做备选吗?这又是一个谜。

其他组件

从图1中可以看到,物理备份和恢复的数据,是直接存储在Amazon S3,即Simple Storage Service上。物理备份和恢复的模块功能被从事务引擎中剥离到了存储层。

从图3和4中可以看到,日志信息的持久化存储也是落在了S3上。

S3是AWS提供的对象存储服务。S3提供了高耐久性、高可扩展性以及安全的解决方案来备份和归档用户的关键数据。在云服务中,数据库提供商业逻辑的支撑,S3提供了数据的持久存储支撑。其作用不可小视。

另外,论文提及了heat management、OS and security patching 、software upgrades等特性,对于创造极高的云数据库服务能力很有帮助,本文不再展开讨论,请参阅论文和相关资料。

作者:李海翔,网名那海蓝蓝,熟悉PostgreSQL、MySQL、Informix等数据库内核技术。腾讯金融云数据库技术专家。著有《数据库查询优化器的艺术》,被誉为查询优化技术领域的里程碑之作。2017年出版《数据库事务处理的艺术 事务管理与并发控制》,被再次誉为事务处理领域的新里程碑。
责编:仲培艺,纠错、寻求报道或者投稿请致邮zhongpy@csdn.net。
本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请订阅2017年《程序员》

评论