赞
踩
Aurora是一个OLTP的分布式数据库。
Amazon认为随着分布式系统的发展以及架构的演进,传统数据库的磁盘IO瓶颈已经变成了计算存储分离架构下网络IO(以及事务先关的IO处理的过程)。的Aurora通过计算节点和存储节点分离,计算节点scale up,存储节点scale out的理念将公有云的关系数据库产品推向了一个新的高度。
传统关系数据库三宗罪,扩展性、可用性和schema 变更(前两条普适各类存储系统)。经常见到的方案中,人们用分库分表/读写分离等解决扩展性,用主备策略(sync/semi-sync/async/backup|restore)解决可用性,用数据重写/提前定义多余列/json支持等解决schema变更。这些解决方案一般是业务感知的,有时也需要业务做痛苦的取舍。更不要提一些传统数据库中一些transaction操作造成的outlier
对整体性能造成的巨大拖累。
基于以上考虑,Aurora将计算与存储分离:
Aurora引入了AZ(Availability Zone
)来表示一个可用区(数据中心)。典型的Aurora数据分布采用3AZ,每个AZ2副本,也就是总共6AZ。复制采用Quorum协议,6个节点,写要至少成功4个(这里隐含的限制是Vw>N/2
,从而避免更新冲突),读要至少成功3个。通过这种配置,Aurora可以容忍1个AZ外加一个副本失效(AZ+1
)而不丢数据。
论文中提到了,一个分布式存储系统,要想提供数据的高可用性,副本的平均失效时间MTTF(Mean Time To Failure
)要远小于副本的修复时间MTTR(Mean Time To Failure
),这个很好理解,如果坏的比修的还要快,那这个集群肯定无法做到不丢数据。
Aurora对此的解法是:充分缩短修复时间
。为此,Aurora将数据存放在10G的Segment中。每个Segment以及其的六个副本构成一个PG(Protection Groups)当表刚被创建时,它只占很少的空间,随着数据量的增加,Aurora会通过增加Segment的方式无缝扩容最大到64G。考虑在万兆网卡中,10G的流量只需要10秒。这个修复时间可以说是很很短了。但是笔者认为,Aurora中肯定有类似Chunkserver的概念,一个Chunkserver中存放多个Segment,当Chunkserver出错下线后,理论上涉及到的所有PG都需要修复(增加副本),所以应该还是要有一个Master节点根据PG修复的紧急程度分配修复的优先级。
以传统关系型数据库MySQL为例,每个sql语句都会生成对应的事务log、binlog、数据,导致数据在在网络上传输多次,还会产生多次落盘。Aurora的计算与存储分离架构主要是为了优化网络IO瓶颈。Aurora中,跨网络的数据流量只有redo log
,计算节点将redo log
下发到存储节点之后(存储下推),由存储节点负责负责管控、materialization(redo log转换为data)、checkpoint(周期性的备份数据到S3)等。通过这种方式,Aurora实现了the log is the database。笔者认为这里的redo log应该比mysql包含的信息要多,才能承担起构建整个数据库的职责
下面表格将Aurora和mysql对比,说明了Aurora的强悍性能。
另外,Aurora的存储引擎层在Crash Recovery中不需要回放所有的某checkpoint
之后的redo log
,而是异步在后台回放,当读到对应stale数据的时候,再搜索redo log
进行回放。通过这种方式,加速了Crash Recovery。Nothing is required at database startup
文章继续讲了Aurora在降低延时方面做出的努力,如下图所示的读写流程,只要1、2完成就可以返回。其余全都是后台执行。注意,因为Quorum的NWR,所以存储节点需要步骤4 gossip
,将当前节点的日志同步到peer节点。
Aurora中的每个log都会有一个递增且唯一的LSN(Log Sequence Number),通过整个集群LSN的推进,Aurora避免了引入2PC等复杂的机制(笔者猜想这里LSN的作用类似于Paxos/Raft中的Commit Index)。由于集群采用NWR,所以理所应当的数据中会有一些空洞,这些空洞通过上面提到的gossip
填充。这些空洞应该只存在于checkpoint之后。
有了LSN,就有CPL(Consistency Point LSN
)来表示某个LSN已经被commit了,我们用VCL(Volume Complete LSN
),用来表示当前系统确定已经commit的最大CPL。当系统重启之后,需要truncate VCL以上的log。这种日志推进的策略就很明显了。
另外处于事务的考虑,一个事务会被拆解为多个MTR(mini-transaction
)。MTR约定了最小事务,MTR中的日志必需以原子的方式执行(比如B+Tree分裂或合并相关的数据页)。
在Aurora中,数据库实例向存储节点传递redo日志,达成多数派后将事务标记为提交状态,然后推进VDL,使数据库进入一个新的一致状态。为了避免前台事务并发执行太快,而存储服务的VDL推进不及时,我们定义了LAL(LSN Allocation Limit)来表示最大未commit的日志数量。通过这种方式,限制整个存储系统的并发程度,防止出现back-pressure(这个词在工程界出现的挺频繁的,我认为其含义指繁忙系统中的某个瓶颈点,在这里指网络带宽和磁盘存储带宽,如果LAL过大,未来的某一时刻会有很多的日志同步,占用网络和磁盘带宽)
在Aurora中,事务提交是完全异步的,存储节点将收到的事务请求放到队列中,等待redo log 持久化之后(VDL大于某事务的LSN),就可以向客户端通知事务执行成功了。这种异步机制保证了工作线程不会因为事务提交等待日志推进而阻塞。
与大多数关系型数据库一样,读数据首先从buffer pool中获得,如果数据页不存在,才会从磁盘中获取,然后buffer pool有淘汰、下刷脏页的机制。Aurora不一样的是,淘汰出去的页不会下刷磁盘,而是直接丢弃。这意味着buffer cache中页一定是up-to-date的(mysql这个不一定,因为有change buffer),buffer pool中的被淘汰的页,其LSN一定是小于VCL的。
正常情况下,读操作不需要走Quorum,VDL之前的SNL可以直接读。这个过程文中没有细讲,但是笔者认为应该会设计到一个中心化的管理PG的服务,从PG的副本中找出一个确认commit的最大的副本提供给client读。
在Aurora中,写副本实例和至多15个读副本实例共享一套分布式存储服务(3AZ 6replica),通过这种扩展方式以较低的成本扩展了读节点的数量。
Aurora的关键点在于存储和计算相分离,并且将日志下推到存储层(传统的share disk方案无法做到这点)。由于这种分离架构下,所有IO操作都是通过网络,网络将成为最大的瓶颈,因此Aurora集中精力优化网络以便提高系统吞吐能力。缺点也很明显:它适用于read intensive的场景(只有一个写节点)。但带来的读能力的优化已经满足绝大多数互联网业务的需求了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。