[来自IT] 本文根据郭忆老师在年5月12日现场演讲内容整理而成。 讲师简介:
郭忆——七年云端数据库开发经验,主导了网易私有云关系数据库服务的建设,支撑了网易云音乐、考拉海购、网易新闻等大型互联网应用,专注于云端数据库的高可用架构和性能优化。 摘要: 过去一年中,数据库的高可用技术出现了很多令人欣喜的进展,AWS公布了Aurora的实现细节,第一次让我们看到了Cloud-Native数据库的高可用是如何实现的,更令人兴奋的是年末的AWSre:Invent大会上,Aurora支持mutil-master,解决了写扩展的问题;与Aurorasharestorage不同的是,MySQLGroupReplication作为一个sharenothing架构的高可用解决方案,在MySQL官方的主导下也日趋完善。本次分享将为大家介绍这些新技术的实现细节,以及网易在这些新技术的方案选型方面的考量。 分享大纲: 1、数据库高可用发展历程 2、Aurora高可用架构设计 3、MGR高可用架构设计 4、网易多副本数据一致高可用架构设计 正文: 1、数据库高可用发展历程 首先我们先来简单回顾一下数据库高可用的发展历程,最原始的数据库高可用的做法是基于replication快速搭建主从复制架构。因为replication是MySQL官方原生支持的,所以搭建起来非常快速和容易,但MySQL原生是一个异步复制,异步复制在一些核心的业务场景下存在数据丢失的问题,尽管MySQL5.5发布了SemiSyncReplication,但因为它实现机制的缺陷,它是在事务提交以后才发送给从机,再返回客户端的流程,所以还是存在数据丢失的风险,MySQL引入了group
直到MySQL官方5.7版本推出无损复制losslessreplication,这时的同步复制是在事务提交之前就把数据发送给从机,能够确保数据的完全一致,但是这种同步复制在写密集型的场景下依旧存在问题,主要表现在从库没办法跟上主库进行复制,导致主库长期处于异步复制状态,从而导致切换的时候丢失数据。 另外同步复制还依赖于复制的延迟,我们知道MySQL是基于Binlog在做复制,Binlog是在事务提交之后才产生的,也就是说从库天生就慢于主库,这样就导致必须把从库的Relaylog回放完才能进行切换。所以复制延迟就很大程度上决定了切换时间,这也是基于Binlog复制进行数据库高可用的一个缺陷。
第二条思路是基于日志的数据库高可用架构,业界比较成熟的两个产品一个是MHA,它在一些互联网公司有比较广泛的使用。另外一个是通过Binlog双写的机制,把Binlog写在共享存储或者是其它的高可用设备上,来确保数据的可靠性。它的好处就是在限定场景下能够确保数据的一致性,比如MHA实现的基本原理是主库挂掉后,它通过SSH到主库的服务器上拿取主库的Binlog,然后把这部分Binlog补全到从库上面去,但实现前提是它能够通过SSH登陆到原先的主机所在的服务器上,假如说这个服务器宕掉了、SSH登不上去了或者是主机所在服务器网卡挂掉了、网络登不上去了,这样都会导致丢数据情况的发生。 日志双写的思路跟MySQL同步复制是有点相似的,它的缺点同样是存在异步状态下丢数据的问题,如果写密集压力比较大的情况下,就会存在长期异步状态,从而导致丢失数据。
第三个数据库高可用的实践方向是基于块设备镜像的数据库高可用实现方式,其中两个业界典型的方案,一个是AmazonRDS技术方案的实现,它是基于EBS硬盘的数据库高可用来实现RDS数据库高可用。另外一个是开源方案DRBD基于文件系统层和设备管理层之间的基于块设备镜像的方式来实现数据库高可用,它的优势是在于对数据库方案来说是透明的,换句话说我可以跑数据库,我也可以跑任何的应用,因为我是基于块设备层来做高可用,所以它对数据库是透明的,它是一个通用的高可用解决方案。 它也有缺点,使用过DRBD的同学应该都了解,它的性能其实非常差,至少在一些核心应用场景下是根本没有办法使用的,它跨网络的流量比较大,因为所有的IO都要跨网络,这样本身的网卡对于带宽要求就会非常高,其次本身跨网络的IO代价也非常高。
第四个方向是MGR(MySQLGroupReplication),其实它是多副本的高可用的架构,这里有两个标志性的产品,一个是Galera,它是MySQL在MGR之前的一个技术方案,两者总的技术方向都是Shared-nothing多副本的高可用数据库架构,Galera的话,首先说优点,它是一个shared-nothing的架构,原来是两副本,现在有多副本及金融级的数据可靠,另外可能还有跨机房的部署支持。 第二个是MGR,新版本的MGR支持多写,可以解决写扩展性问题。原来一谈到写扩展性问题最先想到的一般都是分库分表的方案,MGR从另外一个层面提供了解决写扩展的技术方向。接下里谈缺点,缺点是它本身是基于Binlog进行的数据同步,那就会存在数据复制延迟的问题,第二个是多写模式下所有的事务提交都需要进行冲突检测,即使没有冲突事务也要进行冲突检测,在MGR的场景下其实性能是有一定折损的。 接下来讲讲Galera和MGR之间的一些区别,Galera也是多副本,但是它们的同步复制协议不一样,Galera是基于单令牌环的同步复制算法,MGR是基于改进的Paxos协议实现的数据同步复制算法,相比下来MGR的性能是远胜于Galera。
第五个数据库高可用方向是Cloud-NativeDatebase,这里也有两个标志性产品,在年12月份Amazon就已经发布了Aurora产品,去年阿里也发布了PolarDB产品,它们优点都是Shareddiskcluster基于共享磁盘的架构,另外它们都做到了计算存储分离,解决了扩展性问题。另外从它们的方案上来看都是具有通用性,也就是说换一个数据库可以很快复制出一个基于相同架构的PG数据库,另外它们都标称自己的性能比原生的MySQL可能有几倍甚至几十倍的性能提升。但它们也有缺点,一个是技术门槛高,因为对MySQL进行了大幅的改动,Aurora没有使用InnoDB,它完全是自研的基于LSMTree的方式去做的,另一个是强依赖于底层基础设施,底层都是最新的设备,比如RDMA网络层的加速设备来帮助它获得一些更好的性能。 2、Aurora高可用架构设计 其实我自己很喜欢Aurora高可用设计,之前的AWSRDS其实也是AWS的三大服务之一,除了云主机对象存储,第三个卖的最赚钱的就是RDS,之前的RDS存在什么样的问题呢?使用过AWSRDS的同学应该有印象就是它的性能比较差,一般认为基于云的数据库它一定是做不过物理级的性能的。
但Aurora给了一个相反的答案,我们首先要看看它是怎么做到的,第一个就是上图中原生的AWSRDS所有需要跨网络数据传输的数据有哪些?首先有redo、Binlog,还有Page、DoubleWrite和FRM文件,其中还有三次的传输是串行的,也就是说它是具有先后次序执行的,这样的性能其实很难满足数据库在线业务的应用场景。
Aurora设计的基本思想是数据传输,之前需要传输前面的5个数据,尤其是Page,现在只传输redo,而且上层的instance和下层的Storage之间只通过redo进行数据更新的传输,另外它底层是用统一的跨可用区、多副本高可用的共享存储系统,数据库实例和存储系统只通过redo进行数据的同步,Cache之间通过redo来同步减少数据的延迟。 Recovery异步执行可以做到秒级的故障恢复,原来MySQL是需要有一个checkpoint的,在故障恢复的时候,它把故障恢复之前的已经提交的事务但没有更新的脏页全部执行完,才叫Recovery结束,所以它会有一个Recovery时间。在Aurora中没有这个Recovery过程,它的Recovery很简单,后面我会具体介绍。
它的Storage设计也相对来说非常有特色,它底层的数据库实例的存储都是由10G大小的Segment来组成的,每个Segment有6个副本分布在3个可用域内,存储节点是挂载了本地SSD的EC2,这点是需要特别注意的,刚才提到的serverinstance层和Storage层只通过redo进行日志的传输,Storage层其实就是一个挂载了本地盘的EC2实例,所以它用的全都是本地盘,除了redo是跨网络的,Page的更新全都是本地的,这样它的性能获得了很大的一个提升。另外它的单个数据库最大的支持64TB,Segment是存储数系统数据修复的最小单位,因为它只有10G,所以它可以快速的并发的进行修复。通过标记Segment不可用可以完成计划内的数据迁移,尤其是在热点不均衡情况下进行数据的迁移。
作为高可用的一个设计来说,Quorum设计是非常重要的,每个Segment有6个副本部署在3个可用域上,每一次写要写4个副本,每次读要读3个副本,这样就可以保证它读到的数据是一致的,它写的数据是可持久化的也是一致的。它的一个容错性是失去整个可用域和另外一个可用域的存储节点,都不会影响整个系统的读可用性,失去任意的两个节点,无论是同一个可用域还是不同可用域,都不会影响它写的可用性,这是它高可用设计的基本原则。
上图是Aurora的事务提交流程,首先redo在mtr提交时会copy到redologbuffer里面去,redologrecord根据每个redolog的PageID,就是这个redolog所更新的是哪一个Page,根据Page所在的存储节点会划分成多个batch,然后发送到具体的存储节点。如果一个PG内的6个Segment中有4个写成功了,基于Quorum设计原则,数据会返回客户端,则事务提交成功,并且会推动VDL。 VDL是在Recovery中很重要的一个标志,首先VDL可以理解为一个LSN(Locksequencenumber),每个Page根据待更新的redologrecord的长度来决定Page的更新,这个是Aurora实现的非常重要的改变,MySQL在刷脏页的时候,它会根据redolog直接在Page上面去做更新,但Aurora不是根据checkpoint去做页面更新,它会细化到每个页,根据每个页上待更新的redolog的长度来决定这个Page要不要做更新,所以它不是根据checkpoint去做页面更新的。 另外就是同一个PG内不同的Segment通过gossip协议来补全日志,因为它每个节点上面6个副本写4个副本,另外2个副本不一定会有数据,所以它需要通gossip协议去补齐数据,来保证它的一致性。
理解Aurora的故障恢复,首先需要理解三个概念,第一个概念叫做CPL,每个mtr(Mini-transaction)是MySQL内部来确保一个页或者多个页更新的一致性的原子事务,它最后一个LogEvent对应的LSN叫CPL,VCL是持久化最大的一个logrecordlsn,基于前面两个概念,其实可以理解VDL是小于VCL的最大的CPL,持久化最大的mtr的LogEvent。 Aurora做故障恢复的时候需要建立VDL来确保各个副本达成一致性的一致性点,VDL之前的LockSequence代表的是已经提交的事务是可读的,VDL之后的其实是没有提交的事务,是需要进行回滚的,但是提交和回滚是Aurora都是通过异步来完成的,不是像MySQL是同步完成的。
最新发布的Aurora支持多写,之前Aurora是单节点的写入,它实现的基本原理是利用LamportClock逻辑时钟的概念,解决在并发系统中具有因果关系的事务顺序执行问题。 多写中有个很重要的问题就是冲突检测,如果两个事务同时更新了同一个Page的话,如何来进行冲突检测呢?它是以Page为单位进行冲突检测,基于LSN来实现版本管理和冲突检测,基于逻辑时钟来解决具有因果关系的事务执行问题,基于Quorum原则,最先写成功4个副本的事务就会提交,没有写成功的事务就会被回滚掉。 在上图中,每一个redologrecord都携带了一个它要更新的Page的PageID和它要更新的PageID对应的LSN(注意区分本条redologrecord的lsn),如果两个事务同时更新同一个Page,起始状态下,两个事务对应的logrecord上携带的要更新的Page的lsn是一样的,并且与Page的实际lsn也是一致的,如果此时,一个事务更新成功,这个事务会更新page上的lsn(会更大),另外一个事务要去更新该page的时候,就会发现page上的lsn已经和该事务携带的要更新的page的lsn不一致了,这样Page就会拒绝更新,则两个事务,只有第一个执行成功了,另外一个事务会被回滚。基于Quorum原则,最早取得quorum的事务则写入成功,没有获取quorum的事务被回滚。 3、MGR高可用架构设计
刚才介绍了Aurora具体的实现细节,那我们接下来看另外一个MGR高可用的具体实现,MGR跟Aurora是完全不同的方向,Aurora是基于共享硬盘存储、共享硬盘集群的方向。MGR是基于一个Shared-nothing的技术架构,也就是说每个节点具有相同的数据副本,进行独立处理。 MGR最大的亮点是基于Paxos协议实现了数据多副本的一致性集群,同时MGR支持single-master和multi-master模式,基本上可以看到现在的数据一致性集群都开始往支持多写的趋势发展。另外它会作为MySQLInnoDBcluster解决方案的组成部分,MySQL其实之前有NDB存储引擎层的高可能解决方案,但对于InnoDB层MySQL官方没有一个很标准的高可用解决方案。
MGR基本实现原理是所有节点都具有相同的数据副本,它本质上是基于Binlog来实现数据的同步,本地事务在提交的时候会进入全局的排序,事务在节点去提交执行的时候,它会先在本地节点进行执行,一直到这个事务提交的时候才会进入Paxos协议全局排序阶段,所以说它前面的事务执行流程都是在本地进行的,在事务提交的时候才进入全局排序阶段。 基于Paxos协议,它可以确保集群内所有的节点按照相同的事务执行次序去执行,即Paxos协议给了MGR的所有节点都按照相同的事务执行次序,这个就是Paxos协议的核心,也是MGR最大精华之处。 另外是冲突检测问题,如果支持多写的话,势必会存在冲突的问题,它是基于writeset来进行冲突检测,writeset可以理解为是MySQLBinlog的一个LogEvent,这个后面会详细介绍。
一致性协议是基于Paxos协议的改进协议,原生的BasicPaxos协议它的任意节点都可以发起提议,每个value至少需要两次网络开销、两次磁盘持久化并且容易产生活锁,这是原来BasicPaxos协议的一个劣势。基于BasicPaxos协议,发展出来两个比较主流的改进协议,一个是raft协议,它是单写的并且简化了prepare阶段的开销,大幅提高了它的性能。另外一个是Muti-Paxos协议,它有点类似raft但跟raft不同的是它的prepare是基于租约的方式去实行的,另外一个它的日志是允许空洞的,raft的协议是不允许空洞的。 MGR中也使用了Mencius协议,相对于BasicPaxos,它也同样节约prepare阶段的开销,相比于Muti-Paxos它消除了leader的性能瓶颈,集群内每个成员都会负责一部分提议的生成,这个集群内的每个节点都会负责一部分提案的提交。但有一个前提是集群每个节点它处理的事务负载都是一样的,都是按照相同的频率来去处理事务。 但这个在实际场景下是不可能做到的,所以它加了很多优化和限制,比如说加了skip消息,假如说一个节点,当前如果没有要提交的事务的话,我可以发一个skip消息,把所有的事务直接跳掉,其它节点接到skip消息后,直接就跳到下一个节点进行提交,这样它的事务提交速度就得到了提升。
MGR另外一个就是writeset,它可以理解为在每个事务中都增加了一个LogEvent,关心的小伙伴可以去看一下,能发现MySQLBinlog文件里面并没有LogEvent,这个LogEvent它在哪里呢?其实它在内存里面,它根本就没有写文件,是在内存里面维护的LogEvent,所以你在文件里面其实找不到LogEvent。 LogEvent主要包括哪些信息呢?一个是事务更新的主键,还有这个事务所对应的数据库的快照版本,快照版本是用一个gtid-executed的数值来表示的。它只在内存中维护,不会写出Binlog文件来保证兼容性。 另外一个是它的冲突检测,刚才提到了所有事务在执行完提交阶段都会进入Paxos协议层来确保所有成员节点按照相同的次序去执行,然后分别进行冲突检测,也就是说冲突检测实际上是在成员内的每个节点上独立进行完成的。因为大家都是按照相同的次序执行相同的事务,所以得到的都是相同的结果,这是冲突检测的最基本原理。 每个成员节点实际上都维护了一个冲突检测的数据库,所有待检测的事务对应的数据库版本都必须要大于冲突检测中已经通过的检测记录版本,也就是说冲突检测数据库里面维护的是一个更新的主键ID和这个记录所对应的版本,一个事务想要去更新这条记录,必须保证你更新的事务已经包含了这个记录所对应的版本才能更新,如果不包含的话就存在冲突。 所有节点对已经执行的这个事务对应的记录会从冲突检测从数据库中进行purge,刚才讲到冲突检测数据库会不会去维护所有数据库记录呢?它会进行一个优化,会进行异步的purge,purge的原则是假如说这个事务已经在所有节点执行完了、执行成功了,这个记录就会被purge掉,这是MGR的冲突检测数据库的记录维护的一个基本原理。
举个例子,MGR集群里有T1、T2两个事务分别在两个节点来执行,这两个节点的数据库快照版本都是gtid-executed:group:1—,经过全局排序以后,按照T1和T2的事务执行次序去执行,T1先执行T2后执行,首先T1去执行冲突检测,冲突检测数据库中是有一条记录的,比如说ID=1这个记录对应的版本是group:1—,这个T1对应的也是group:1—,可以认为T1对应的版本已经包含了这个记录的版本,那T1是通过冲突检测的。 然后T2开始执行冲突检测时,T2对应的版本还是1—,但是这时候冲突检测数据库中这个记录1对应的版本已经从1—了,T2所对应的版本已经不能包含记录1所对应的数据库版本,这时候的T2是应该被回滚掉的,因为已经存在冲突了,这就是T1和T2两个事务更新同一条记录在MGR集群中冲突检测和发现回滚的基本流程。 4、网易多副本数据一致高可用架构设计 网易公司在年就开始在数据库同步复制这块进行了一些探索,并且在年就已经研发了基于MySQLGroupReplication实现的同步复制技术。在刚才提到数据库一年中发生了两个重要的变化,一个是基于Shared-storge,一个是基于Shared-nothing。
网易云数据库是年在公司内部开始进行大规模推广,现在大家其实可以看到网易的互联网产品,网易考拉海购、云音乐、邮箱都已经运行在云数据库上,它底层是一个laaS的架构,laaS之上构建了RDS和DDB分库分表的分布式中间件,外围还有一些数据迁移、数据库性能诊断以及数据库运维的工具链。对于RDS来说,根据业务需求分为三个产品,一个是单机版的,一个是双机高可用版的,还有一个就是多副本高可用版的。
今天我们重点介绍一下多副本的实现,网易云数据库是基于一个云基础设施来构建的,网易基于openstack进行自研的基于KVM的虚拟化的laaS环境,也是基于MGR来进行多副本的数据库高可用的架构,它的3个节点位于3个不同的可用域中,这个可用域在物理上完全隔离的,基于VPC网络来实现集群内部的数据同步,它会有一个RDS的VPC,在整个VPC网络内进行数据的同步,提供读写只读两个域名,用户其实可以通过读写域名访问primary结点,通过只读域名来访问secondary节点,通过权重来影响节点的切换。
上图是基于MGR的基本故障恢复流程,故障恢复时间主要由两部分组成,选举时间和applytime,故障节点修复以后会重新加入到MGR集群中,并且选择只读节点作为Seed节点。其实MGR有一个很好用的功能叫做流控,我们通过控制只读的能力来尽快完成数据的修复。
刚才提到流控,它有三个参数,MGR的缺陷是基于Binlog进行数据同步,就会存在复制延迟问题,但MGR有流控系统,它可以在发现从库复制延迟比较高的情况下,限制主库的写入速度,来减少主从复制的延迟,其实这是一种自保护的机制。
最后就是大家比较关心的性能问题,上图可以看到,MGR的性能其实是可以达到甚至超过原有同步复制的水平,当然相对于异步复制水平可能还是稍微差一点,但是基本上是可以达到原有同步复制的水平,并且获得了更高的可用性,这个结果已经超出了我们设计的目标。