欢迎您光临本站,如有问题请及时联系我们。

换个角度看Aurora:缘何“万能”?对比TiDB有何不同?

  首先,Aurora是依托于AWS云高度融合MySQL定位于OLTP的企业级关系数据库,Aurora的设计者认为,当前海量数据处理的主要瓶颈已经从计算、存储IO转移到了网络IO(1),为了解决这个问题,Aurora通过将重做日志推到多租户的大规模存储服务当中(Aurora的设计哲学是logisdatabase)(2),从而不仅大幅度的减少了网络IO,而且可以数据的多副本进行快速故障恢复,以及容错损失,建立起一套自愈存储(Aurora将恢复子系统委托给底层可靠的存储系统,依赖这个来保障系统服务层级(ServiceLevelAgreement,SLA))。


  Keyword:OLTP、logisdatabase、logprocessing、quorummodels


  当前数据库在M-S的基础上,产生有很多优化变种,比如Sharding、扩容,这样单个节点的存储IO可以有效进行控制,但由于节点间的不经济的复制、交互导致网络IO扩大。

  下文有专门阐述章节。

  背景介绍

  Aurora介绍

  现在IT逐渐在公有云迁移,其中大多需要OLTP数据库系统,能够提供一个等同或者更优的数据库是加速这种转型的关键。在现在分布式云服务中,通过对计算与存储的解耦,以及对存储的多节点复制,增加系统的弹性及扩展性,这样我们也能够进行删除问题节点、增加副本、写节点切换等。但在这种环境下,传统数据库系统所面临的I/O瓶颈发生变化。由于I/O可以在多个节点和多个磁盘传播,单个磁盘和节点瓶颈不在。瓶颈转移到数据库与多节点存储直接的网络交互,尤其在对存储进行并行写的时候,网络带宽的带宽或者流量(PPS)可能导致响应时间变慢。


  同时传统(包括分布式)数据库需要复杂的脏数据刷机机制,事务提交也需要很多交互,事务提交普遍采用2-phasecommit机制(2PC),这些在云环境下都需要进行改变(优化)(Aurora的设计哲学是logisdatabase,对数据的更改只写日志,也即write-once,大大简化了传统数据库的写入机制)。


  Aurora使用了一种新的体系结构(见下图)它将logging和存储从数据库引擎中剥离到分布式的云存储环境中,虽然实例还包括大部分的传统内核组件(查询处理器、事务、锁、缓存、访问机制和回滚管理),但其中的几个功能(重做日志、数据持久化、闪崩恢复、备份/还原)则被下传到了存储服务层。


  Aurora


  Keyword:2-phasecommit、write-once


  Aurora主要优势

  通过将存储构建为跨多个数据中心的独立容错和自愈的服务,使存储层中数据不受性能差异和单节点瞬态或永久故障的影响。

  只写重做日志记录存储,我们可以减少一个数量级的网络IOPS。一旦消除了这个瓶颈,就可以积极地优化许多其它争点,在MySQL基础代码基础上获得显著的吞吐量改进。

  Aurora用存储层持续异步复制操作,替代数据库引擎中最复杂、最关键、最昂贵的备份和重做恢复功能(backupandredorecovery),这是不需进行保存检查点(checkpoint)、不对数据库进程处理进行干扰的低成本的备份恢复机制。

  Aurora三个问题

  但同时同样引入三个问题:


  如何在云存储上进行数据持久性,以及如何对节点故障进行选举仲裁。

  如何使用这个自动同步存储系统,并发挥其最大作用。

  如何消除分布式存储中的多阶段同步、崩溃恢复和检查点。

  Keyword:checkpoint、crashrecovery、redolog、redorecovery、quorummodel


  传统数据体系背景知识

  在解决上面三个问题前,我们先回顾下传统关系数据库体系结构,DB在处理事务的过程通常被视为一种分层的行为。系统在顶层对SQL语句进行解析,然后将得到的语法树传递给查询优化器层。查询优化器通过统计信息进行最优路径选择(CBO),这个阶段产生的物理执行计划与逻辑存储层交互,完成相应的操作。


  在本文中,将事务处理引擎简化为两层模型:SQL解析、SQL执行以及优化视为查询处理引擎(ProcessEngine,PE);逻辑层存储和物理层存储统称存储引擎(StorageEngine,SE)。这对应MySQL可插拔存储的两层架构。


  数据库


  定义数据库服务器集群的架构决策的关键点在于集群共享、复制发生的阶段,协调动作发生在什么层以及哪个层。这不仅确定了系统在可扩展性和灵活性上的权衡,而且关系到每一种架构在现成的数据库服务器上的适用性。以下是具有代表性的架构:


  数据库


  数据库架构分类整理

  我们对这四种主要的架构进行下整理汇总(除此之外还有sharednothing纯分布式架构):


  架构


  Aurora的架构可谓独树一帜,它的体系架构类似SDP,但是它将更新限制在一个DB实例上,避免了分布式并发控制协议。Aurora设计者们认为,传统的数据库实现可扩展不管是做Sharding、还是分布式、或者共享存储(OracleRAC),本质上都是在数据库的不同层面耦合(SNA在应用层连接层,SDP是在进程和缓存层),扩展后的每个实例的程序栈仍然是原来的多层结构。Aurora认为从成本、部署灵活性及可用性等因素考虑,应该考虑把数据库的各层打开,然后在每个层单独做扩展。传统的数据库系统,例如MySQL、PostgreSQL以及Oracle,将所有的功能模块封装成一个整体,而Aurora则是将数据库的缓冲区管理、恢复系统(recovery)从这个整体剥离出来,单独定制扩展。


  Keyword:SharedNothing、Shareddisk、Garela、GroupReplicatio、dbproxy、OracleRAC


  Aurora多节点持久化

  Aurora复制及相关失败处理:


  磁盘、网络、机柜、机房等都有一定的故障率,并且日常维护重启、升级等操作会增加这类临时不可用时间。一般采用多副本选举来解决该问题:例如3个副本的情况下,写必须要有2个以上(Majority)副本写成功才可以,在读的情况下也必须保证有2个:V-Read+V-Write>V-Copy。Aruora设计中使用了3AZ(可用区),每个AZ下2Copy的设置,共计6个副本。AWS传统的服务,例如S3、Kinesis等都是3副本设计。这样的设置主要从实际观察情况出发:


  在同一个AZ下下硬盘、节点损坏、维修等是常态。在同一个AZ下设置2Copy可以尽可能减少这类操作对用可用性影响。

  机房级AZ问题不常发生,例如水灾、火灾、地震等故障。

  在写入场景下6Copy需要保证4个副本完成,在读场景下只要7-4=3个副本一致就可以了。


  段存储(segmentedstorage)

  Aurora下存储最小单位叫Segment,每个Segment大小为10GB,ProtectionGroup(PG)是一个逻辑单位,代表的是6个Segment,Segment是维护与调度的最小单元,一个Segment故障时,同AZ的万兆网络可以在10秒内就完成修复(这也是选择10G的原因),存储节点Segement这种设计对于日常运维比较重要,例如在热点问题上(HeatManagement),我们可以在一个热磁盘或节点上标记一个片段是坏的,冲裁系统(quorum)可以通过快速迁移到另一个较冷的节点,操作系统和安全修补程序甚至存储机群升级都是该节点的一个简短的不可用事件,一次执行一次,确保各PG的成员不同时被修补。实现了在存储服务中使用敏捷和快速部署。


  Logisdatabase

  传统数据库中的在写入页面对象(如堆表、B-TREE等)之前,都需要先写入重做日志,(wirite-aheadlogWAL机制)。并且每个重做日志记录还包含修改前、修改后的数据。除此之外,其它数据也需要写入,在下面基于多数据中心的热备架构(active-standy)(构建在EBS上)(下图)中,一个简单的写入被放大很多倍(自己算吧)。


  RedoLog

  Binlog(临时存储最后N个)

  Data(最新数据库结构数据,内存中定时Dump)

  FRMFiles(这个数据量一般比较小)

  Double-Write(MySQL数据刷新的一个优化,将dirtypage拷贝到doublewritebuffer内存区域,便于脏数据刷新过程中失败恢复。)


  keywords:DOUBLE-WRITE、Binlog


  为了解决这个写入放大的问题,Aurora提出的思路是,唯一的写入只包括通过网络写入Redolog(不在有后台的写入、checkpoint、脏数据),直接将Redolog下推到存储节点,每个存储节点根据Redolog来构成本地(LocalSegment)存储状态,多个存储可靠性通过副本数来保障。每个存储节点也可以将Segment定时同步到S3上增加可靠性。


  下图显示了一个主多从的集群。主库只将日志记录写入存储服务,并将这些日志记录发送到从实例。IO批量流入一个共同的目的地(逻辑段,即一个PG)来完全记录日志记录,并将每个批次传给所有6个副本继续持久化,数据库引擎从从4处(4/6)等待确认,从库使用重做日志将更改应用到其缓冲区缓存。


  日志


  闪崩处理,在传统数据库中,闪崩后数据库需要从最近的一次checkpoint开始应用所有重做日志进行恢复,在Aurora中,持久化重做日志会多节点连续的、异步同步,任何一个读请求都有可触发重做日志对旧的页进行应用(重做日志随时应用),所以正常情况下,数据启动后基本上不用做什么恢复操作。


  在Aurora的设计思路中,存储服务的核心设计原则是最小化前台写请求的延迟(快速响应),将大多数存储处理移到后台,同时他也有机制去避免后台存储高峰导致前台延迟,例如,当存储节忙于处理前台写请求时,不需要运行旧版本数据页的收集(GC),除非磁盘接近容量,在Aurora后台处理与前台处理是负相关的(传统数据库往往是正相关),后台页面和检查点的后台写入与系统上的前台负载正相关,如果在存储系统上建立了一个积压,将关闭前台活动,以防止长时间的队列堆积(这个思想,类似单实例的刷新机制,会自动选择空闲情况下进行脏数据持久化)。由于在系统中的各个存储节点上放置了高熵值,所以在一个存储节点上的节流很容易被4/6次仲裁写入处理,以慢节点的形式出现。(有点类似传统的M-S架构中,从库延迟后,摘掉读流量)(这里面隐含另外一个问题,如果是整体负载导致的存储积压,关闭一个节点可能导致雪崩,在论文里没有发现相关的解读,可能Aurora的设计者认为这种情况概率很低,或者认为存储处理能力应用远大于计算节点。)


  让我们更详细地看下存储节点上的各步骤。如上图所示,它包括以下步骤:


  接收日志记录并添加到内存中的队列

  记录在磁盘上并进行ack返回

  对于批量Redolog进行整理,确定没有批次丢失

  与其它副本存储节点进行对齐(通过Gossip协议,它们可以知道集群中有哪些节点,以及这些节点的状态,每一条Gossip消息上都有一个版本号,节点可以对接收到的消息进行版本比对,确保二者得到的信息相同)

  日志记录合并到新的数据页

  定期将日志和新的页面复制到S3

  定期进行过期版本垃圾数据回收

  定期验证CRC码页,如果发现损坏数据块,与相邻节点进行通信获取完好的数据块。这是Aurora实现可自主修复损坏数据块的关键技术

  注意,只有步骤1和2完成,前台即完成(commit),其它操作都是异步进行。


  Aurora读写处理过程

  术语解释

  这块内容比较烧脑,有些地方原论文写得也很模糊,需要加上一些猜测和理解,其中里面也有很多术语,我们先把主要的进行简单解释:


  LSN:LogSequenceNumber日志顺序号,每个日志都有一个自动增长顺序号(传统的MySQL也有这个概念,类似Oracle的SCN)

  VCL:VolumeCompleteLSN存储可以保障的可用的最高LSN

  CPLs:ConsistencyPointLSNs一致点的LSN

  VDL:VolumeDurableLSN已经持久化最大的LSN,最大的CPLs

  SCL:SegmentCompleteLSN已经完成的段的日志号

  Aurora写操作

  在Aurora中,数据库不断与存储服务交互,并维护节点状态以便进行仲裁、更新最新的持久化的卷(VDL)。在正常情况下,数据库根据每个节点的重做日志记录,来更新目前VDL。数据库随时都会有大量并发事务,它们各自生成自己的重做日志记录。数据库为每个节点分配唯一顺序的LSN对其进行约束,该LSN等于当前VDL和定量LSNAllocationLimit(LAL)之和(目前为1000万)(VDL+LAL)。此限制确保数据库不会超前于存储系统很多,如果存储或网络无法跟上,则可降低传入写入的压力。注意,每个PG片段只看到已影响该卷中日志记录的一个子集(affectthepagesresidingonthatsegment)。每个日志记录包含一个链接,指向之前的记录,通过这个反向链接,可用于跟踪已经在每个段建都完成的日志点,并根据这个确定已经完成的LSN终点(SCL),最终知道哪些记录已经到达该节点上。每个存储节点互相通信(gossip),通过SCL用于查找和交换丢失的日志记录。


  Aurora


  Aurora提交

  在Aurora中,事务提交是异步完成的。当用户提交一个事务,线程将事务移动到提交列表,写下该事务的commitLSN,然后线程继续执行其它工作(和传统不一样)。这相当于在WAL的基础上,完成一个承诺,当且仅当新的VDL大于或等于事务提交LSN(commitLSN≤VDL)。等待提交会使用专用线程发送到存储节点,收到对应批次的日志记录的4个ACK。


  在传统MySQL数据库中,为了减少磁盘IO采用成组提交技术。第一个写日志缓冲区的线程需要等待预先设定的时间,然后再执行磁盘IO操作;或者等到日志缓冲区页写满以后再执行IO操作。这样子做的结果是第一个写日志缓冲区的线程需要挂起等待,耗费时间。这是一个同步操作,在持久层存储的ACK返回之前不能进行其它工作。


  下图提交队列里面有3个挂起的提交请求,分别是Pendingcommitgroup1,Pendingcommitgroup2,以及Pendingcommitgroup3。主实例收到针对Pendinggroupcommit1的4个以上的日志持久化ACK以后,将系统VDL前移至22,第一组的状态从pending变成committed。此时后台线程检查提交队列,然后成批提交LSN小于等于22的事务T1,T2,T3。


  注意,即使后面Pendingcommitgroup3先于Pendingcommitgroup2收集4个以上的存储节点返回的持久化ACK,那么也不能移动数据库持久化位点。因为,这个数据库持久化位点是Aurora崩溃恢复以后决定开始重做的位点。跳过前面的成组提交的LSN会导致数据库丢失某些数据。在系统崩溃恢复的时候,系统检索最新的数据快照与相应的日志记录(其LSN大于数据库持久化位点),即可将数据库恢复到最新的一致性状态。


  Aurora读操作

  和大多数数据库一样,Aurora读操作首先会在缓存区里进行扫描,如果缓存里没有对应数据,才会将请求下发到存储系统,如果缓存区满了,则需要将一些页面进行挤出(部分页面是受保护的),如果缓存区里有脏数据,则需要像将脏数据持久化到磁盘,这样才能确保其他的事务可以读到最新的数据。


  只有当缓存区页面的LSN(标记该页面的最新版本日志号)大于或等于VDL时,该页面才可能被刷出,在这之前这个页面是受保护的,这个机制确保了:


  所有页面的更新都已经持久化到日志。

  在缓存区没有该数据页的情况下,可以根据VDL获取最新版本数据。

  正常情况下,数据库读操作并不需要采用多数投票的方式,当从磁盘读取数据时,会分配一个读位点(readpoint),这个点代表产生时刻的VDL,同时数据库在存储节点维护SCL,系统根据读位点和SCL来确定从哪个存储节点进行读取,只有当故障恢复或者必要的时候,才会根据多数投票的方式来确定系统的VDL。


  Aurora复制

  在Aurora中,多达15个副本都可以装入单个共享存储卷。因此,读副本不会在写操作方面增加额外的开销。为了最小化延迟,写入同时传送到所有副本上。


  Aurora恢复

  Aurora将数据库文件切分成10GB大小的块,每个块都有专属的日志记录。在崩溃恢复的时候,系统利用多数派读,确定运行时的一致性状态。恢复模块首先确定最大VCL(最大的顺序LSN),截断此后日志。进一步,可以将需要重放的日志限制在其LSN≤CPL。VDL取最大的CPL,LSN大于VDL的日志记录都可以安全地截断。例如,最大已完成的日志记录的LSN为1007(尚未提交),但是系统的CPL为990,1000,1100。系统可以确定LSN大于1000的日志记录都可以忽略。确定完重放日志的LSN最大值以后,Redo操作就可以并行在不同Segment上执行了。


  部署

  在原论文里,该章节的标题直接写成了PUTTINGITALLTOGETHER,笔者的理解是它想表达Aurora和AWS现有框架下的多种云产品进行集成,形成了一个整体数据库云服务。先来幅鸟瞰图:


  如图,应用通过VPC(私有虚拟网络)接入,然后可以读写位于不同AZ(可用区)的数据库。数据库的部署是一主多从的集群架构,一个写入的主节点(Aurora不属于完全的分布式架构),多个只读的从节点,从节点+备节点最多可以有15个(为啥?AWS共享存储系统可以让15个副本装入单个共享存储卷)。节点之间通过RDS(RelationalDatabaseService)来交互,RDS在这里充当HM(HostManager)角色,它通过Agent来获取主从集群的状态监控、当主节点Failover的时候,它会进行HA调度、或者某个从节点Failover它会进行下线替换。这个负责监控、管理的节点,称为Controlplane。


  基准测试

  这个直接转载自论文的数据,目前还无从实测,不过从上面的设计理念上,有可能是会大幅度提升的,不做过多评论,我们只关心写入性能。


  AuroravsTiDB(Spanner&F1)

  回顾数据库的发展历史,我们可以简单分成三个阶段,从上实际70年代E.F.Codd提出关系模型到本世纪初,基本上是以单机关系型数据库为主,如Oracle、MySQL、PostgreSQL,但随着互联网搜索、社交的发展、数据量爆发增长,传统数据库高成本,无法线性扩展问题日益凸显,分布式NoSQL快速发展,如HBase、MongoDB,近几年来,大家发现很多的业务并没有办法直接使用NoSQL的模型,应用需要很复杂的开发才能实现SQL和事务能很简单实现的功能,所以新一轮的数据库开始了SQL回归,这种可扩展、高性能、支持SQL和事务的数据库被大家泛称为NewSQL。


  NewSQL又分为不同的方向,除了本文提到的云计算RDS,还有分布式关系数据库,其中的代表产品如谷歌的Spanner&F1,还有国内人气厂商PingCAP的TiDB,TiDB具有支持SQL、水平弹性扩展、数据强一致性的高可用等特性,篇幅限制,这里不展开,详情可参考TiDB的官方文档(https://pingcap.com)。


  虽然Aurora、TiDB是两个不同方向的产物,但作为两个引领潮流的数据库产品,笔者按照体系结构、数据格式、存储引擎、容量能力、扩展性、兼容性、移植性、灵活性、读写性能、内存机制、异地多活等若干维度进行下对比:


  Aurora

  架构:属于Shareddisk架构(存储可以简单理解为共享,实际上是一套多副本复制存储)。

  存储:数据格式属于传统的表结构(InnoDB引擎)。

  写扩展:单点写入,系统写入性能有上限(虽然正在计划中的Multimaster理论上也会提升写吞吐,但是仍然受限于实例之间的内存交互及存储之间的Applylog),从写流量看不属于真正的Scaleout。

  读扩展:实例可弹性扩展。

  大小限制:数据大小上限64T。

  读写响应:大大简化commit的机制,读写响应时间很好。

  兼容性:由于实例层本身就源于MySQL,所以对MySQL兼容性好(100%)。

  移植性:移植性很差,它依赖AWS很多组件,尤其是底层的共享存储系统,所以基本不具有移植性,它定位就是一套云环境下RDS。

  灵活性:架构可改造性很差。

  内存交互:日志会在实例节点Buffer之间进行复制应用,通过这种“内存交互”方式读性能会更优。

  事务性能:不存在2PC及写扩大的问题,写入性能上更优。

  高可用:不是强一致机制,极端情况下通过其它节点可能读不到最新的数据。

  节点状态:实例分主次,只能通过从库选举方式来进行实例层的高可用。

  适合场景:主要解决的OLTP场景,它的优化器和MySQL一样,不是为分布式系统设计。目前还不支持Hashjoin、并行等技术(从2017AWSRe:Invent最新消息看,后面会逐步支持,包括Pushdownquery),但综合比较看仍不适合OLAP分析场景。

  开源状态:商业数据库。

  TiDB

  架构:属于Sharednothing的分布式架构,他通过Raft协议进同步,实现每个Raftgroup内的数据强一致性。

  存储:数据格式是KV,数据模型是基于LSMTree的模型,底层(Rocksdb)。

  写扩展:写入分布式写入,线性扩展,写入容量理论上限很高,LSM写入性能更好。

  读扩展:存储节点、实例节点都可进行线性扩展(读写扩展),属于典型的Scaleout。

  大小限制:理论无上限。

  读写响应:两阶段提交、Raft复制对响应时间都会有一定延迟成本。

  兼容性:高兼容MySQL连接协议,但和Aurora比,不支持存储过程,触发器,兼容性略差。

  移植性:具有很好的移植性,支持多种部署方式,可以在公有云、私有云、私有环境下快速搭建。

  灵活性:实例层、存储层拆分的很清晰,独立,可在TiKV上直接调研API、或者部署诸如Tispark完成架构的灵活多样性。

  内存交互:实例节点Buffer不存在交互机制,不能共享内存。

  事务性能:分布式事务锁机制,写入性能,尤其是冲突比较多写入场景性能较差。

  高可用:只要网络条件允许,可以创建真正意义的异地多活分布式数据库。

  节点状态:实例层无状态,实例层的高可用更胜一筹。

  适合场景:首先解决了OLTP场景,同时通过Tispark可以比较好的支持OLAP场景,而且由于共用一份TiKV集群或者实时同步机制,能实现实时分析场景。

  开源状态:开源数据库,社区活跃。

  对比总结

  两个产品作为NewSQL的两个方向,都属于跨时代的产品,也都有很多令人激动的Feature,他们用各自的方式提升、扩展了数据库的写入容量,都是替换Sharding+Proxy很好的方案,Sharding+Proxy虽然是目前主流方案,但它存在很多问题,具体可关注笔者近期将发布的另一篇文章《数据库Sharding+Proxy方案成本汇总》。


  Aurora在内存一致性、读写性能、MySQL兼容性、事务限制、内存管理、Cashrecovery等方面优于TiDB。但反过来TiDB在弹性扩展、架构的解耦性、灵活性、场景覆盖等方面更胜一筹,更重要的是TiDB移植性很好,笔者觉得这点会成为两类产品普及度一个很重要的因素,同时TiDB是可以真正实现跨区域的异地多活的分布式数据库。


  总结

  云计算的发展为存储分离的提供了更多可能,也加速了这种趋势,传统关系型数据库需要在单机层面花费了昂贵的成本去实现ACID,这样导致单机的容量非常有限,以MySQL为例,M-S的架构虽然解决了读流量的扩展问题,但写流量成了最大的瓶颈(虽然有各种Sharding、多主等方案,但本质上并未解决写流量扩展问题),Aurora依托自身的云产品,把数据库这个盒子打开进行抽茧剥丝,通过把写入、恢复等昂贵操作下沉到存储服务中的方式,很大程度了提升了集群的写入容量,粗略估计可能会提升一个数量级,在这个容量范围内的OLTP业务覆盖量还是非常大的,但反过来说,这种架构本质上还是单节点写入,而且从目前看上去Aurora在实例层改动很少,这么看上去有点小马拉大车的感觉,笔者把它理解为变向的Scaleup。同时,Aurora在自动拓展存储容量、自动修复数据、闪崩恢复、将缓存从实例中分离持久化进而解决了热数据问题上都是值得借鉴的很好创新。Aurora可以说是在云环境下(AWS)量身定做的产物,它所依赖的共享存储,不是简单的共享,而是一个包含计算,能自动复制的共享存储服务系统,但这一点就就很难进行复制。


  最后,不管是计算与存储分离的云数据库,还是分布式数据库,NewSQL的时代已经来临。


来源:本文由E8运维原创撰写,欢迎分享本文,转载请保留出处和链接!