0208MySQL5.7之Group Replication
转自http://blog.itpub.net/29510932/viewspace-2055679/
MySQL Group Replication: Hello World!
对测试版(on labs)的Group Replication的第一印象:这个MySQL插件让多主结构的MySQL集群能够进行全更新(update everywhere)。
它糅合了分布式系统(比如组通信)和RDBMS中replication的技术和概念。
通过这个插件,一组MySQL服务器组成了一个完美的分布式、强一致性的集群,集群内的MySQL服务器共同合作来保持集群的一致性。
介绍
在深入MySQL Group Replication的细节之前,首先了解一些相关的背景知识和Group Replication的工作流程。
了解这些信息助于理解Group Replication和传统的Replication的区别,以及这个新插件是如何实现这一功能的。
图1 多种MySQL Replication的工作流程简图
Primary-Backup Replication
源生的MySQL Replication的本质即为简单的主备复制。一个主库(master)和备库(一个,或者多个)之间,主库执行并提交了事务,在这之后(因此才称之为异步),这些事务才在备库上重新执行一遍(基于statement)或者变更数据内容(基于row)。
这是一种无共享结构,所有的服务器上都默认保存了完整的数据副本。
MySQL在此之上,以插件的形式实现了一个变种的同步方案,称之为半同步(semi-sync replication)。
这个插件在源生的异步复制上,添加了一个同步的过程:当备库接收到了主库的变更(即事务)时,会通知主库。主库上的操作有两种:接收到这个通知以后才去commit事务;接受到之后释放session。
这两种方式是由主库上的具体配置决定的。图1描述了这几种方式的工作流程。
Group Replication
基于组的复制(Group-based Replication)是一种被使用在容错系统中的技术。Replication-group(复制组)是由能够相互通信的多个服务器(节点)组成的。
在通信层,Group replication实现了一系列的机制:比如原子消息(atomic message delivery)和全序化消息(total ordering of messages)。
这些原子化,抽象化的机制,为实现更先进的数据库复制方案提供了强有力的支持。
MySQL Group Replication正是基于这些技术和概念,实现了一种多主全更新的复制协议。
简而言之,一个Replication-group就是一组节点,每个节点都可以独立执行事务,而读写事务则会在于group内的其他节点进行协调之后再commit。
因此,当一个事务准备提交时,会自动在group内进行原子性的广播,告知其他节点变更了什么内容/执行了什么事务。
这种原子广播的方式,使得这个事务在每一个节点上都保持着同样顺序。
这意味着每一个节点都以同样的顺序,接收到了同样的事务日志,所以每一个节点以同样的顺序重演了这些事务日志,最终整个group保持了完全一致的状态。
然而,不同的节点上执行的事务之间有可能存在资源争用。这种现象容易出现在两个不同的并发事务上。
假设在不同的节点上有两个并发事务,更新了同一行数据,那么就会发生资源争用。
面对这种情况,Group Replication判定先提交的事务为有效事务,会在整个group里面重演,后提交的事务会直接中断,或者回滚,最后丢弃掉。
因此,这也是一个无共享的复制方案,每一个节点都保存了完整的数据副本。图1也描述了具体的工作流程,能够简洁的和其他方案进行对比。
这个复制方案,在某种程度上,和数据库状态机(DBSM)的Replication方法比较类似。
MySQL Group Replication细节
MySQL Group Replication通过group communication实现了多主全更新,并且以插件的形式对现有的replication框架进行了拓展。
Group Replication使用了binlog缓存,GTID,基于row的replication,除此之外,还要求所有的数据都要存储在innodb里面,而且还依赖一个外部组件来实现group之间的通信:Corosync。
同时,Group replication实现了一个自动的分布式恢复机制,使得group在动态的新增或者移除某个节点时,这些节点在能同步group的其他节点的状态,最终保持完全一致。
自恢复,弹性,冗余:group
在Group Replication中,数台相互复制的服务器(节点)组成了Replication-group。每个group都有一个名字,目前是按照UUID的方式命名的。
在一个group中,任何节点都可以动态的加入或者移除,group本身会自动调整,无需人工干预。
如果一个新节点要加入这个group,服务器会自动从group的其他节点上同步状态,直到新节点和其他节点保持一致。
事实上,这种状态上的变化,还是依靠源生的Replication完成的,所以在Group Replication的背后并没有什么新的,复杂的机制。
这意味着,从整体上来看,Group Replication和MySQL DBA们熟悉的机制(Replication)并没有什么太多的区别。
另外,如果一台服务器从group中移除了,或者是被临时移除进行维护,那么group中剩下的服务器将会注意到这一点(有机器被移除了),然后自动维护新的group的信息。
因此,这一个是弹性的group,请大声的赞扬Group Replication!
全更新
因为在group中并不存在主库(主节点),所以group中的每一个节点都可以执行事务,每一个事务都会更改group的状态(读写事务)。
如上所述,每一个节点在结束事务的时候,都可能以乐观的态度来执行,之后在组内协调,并commit。
这个协调的阶段,主要是有两个目的:1.检查这个事务是否可以commit;2.广播这个事务的产生的变更,使得这个事务在其他的节点上能够commit(如果没有发生冲突)。
由于事务是通过原子广播传播出去的,所以要么就是所有的节点都接收了这个事务,要么就是所有的节点都拒绝了这个事务。
如果节点都选择接收这个事务,那么也会以同样的顺序收到这个事务(If they do receive, then they all receive it in the same order w.r.t. other transactions that were sent before. 不懂w.r.t)。
争用检测是通过检查和比较事务的写入内容来完成的,因此这个检测是行级的。争用的解决方案依照先来者胜出的原则。
假设trx2先于trx1,且trx1和trx2来自于不同的节点,修改了同一行数据,那么当trx2执行完(excuted)的时候,trx2的节点还没有收到trx1的变更,
如果在这种情况下判断trx1胜出,那么在trx2的服务器上,就需要中断/回滚trx2,这意味着trx2在试图修改陈旧的数据
(个人理解:因为trx2的节点在执行完trx2的时候根本就没有收到过trx1,所以假设判定trx1胜出,那么trx2的节点在执行完trx2的时候,group的节点处于不同的状态:trx2的节点是trx2-commit,trx1的节点则是trx2-rollback/abort)。
TIP:如果事务经常发生资源争用,那么把这些事务放到同一个节点上执行会比较好,这样就能在本地处理好这些争用,而不是利用复制协议去处理。
通过Performance_schema来监控和检查系统
在介绍了这个replication插件的group和全更新的概念以后,让我们一起来看看用户如何能观察到这个插件在背后做了什么。
整个系统的状态(包括group的组成,资源争用的数据,服务的状态)都保存在performance_schema的表中。
虽然这是一个分布式的replication协议,但是实际上,用户只需要登录任意一个节点,就能查询到整个group的所有状态/数据。
例如,用户登录了某一个节点,并查询了group的成员和状态:
用户也可以查询replication协议的其他数据。比如队列中有多少个事务,以及检测到的资源争用:
架构:先睹为快
一起简单了解一下系统的架构。这个架构,重用了很多的基础框架,对部分代码进行了大幅度的重构,还专门写了一些新的接口。
图2 MySQL Group Replication架构
插件本身与服务器和replication层结合得比较紧密,它用到了binlog的缓存,slave 应用器,GTID,relay log,replication线程等接口。
因此仅从MySQL的外部来看,用户并不会感觉到有什么特别不同的地方。
然而,这会要求各种接口必须要配合的非常好,所以插件中的模块化组件不仅要能完成以上接口的功能,还要做好兼容。图2展示了MySQL Replication插件的架构。
从插件架构的顶部开始,首先是一系列和server层交互的API,所有和server层交互的数据/通信都是通过这些API进行。
这些接口隔离了server核心层和插件,并且大部分的hooks都位于事务执行的管道/队列中。
从server的角度来看,我们能得知各种信息和警告,如server正在启动,server正在恢复,server已经准备好接受客户端连接,server正在commit事务,等等。
而从插件的角度来看,会发现插件指示server commit/中断事务,插件指示把relay-log中的事务放进队列,等等。
API下面是三个组件,capture组件是负责跟踪正在执行的事务的上下文;applier组件负责重演group中的远程事务(其他DB上的事务);
recovery组件主要管理分布式恢复机制,负责让新节点更新至最新的状态/数据,或者是回退事务,直到和group的其他节点保持一致。
继续往下看,接下来就是Replication协议层,这个协议包含了一段特殊的逻辑,用来处理资源争用检测,接收并传播组中的事务。
最后就是Group communication API,这个API是通信工具集的一种高级抽象。因此它能对插件的其他组件隐去消息层的细节。
如同之前提到的,MySQL Group Replication插件包含了绑定corosync的接口,因此这个接口实际上是corosync到client API的一个隐式映射。
紫色的框架是插件的外部组件,由于他们组成了堆栈的其他部分,所以在这里一并展示出来。
这种框架与现有的一种数据库框架:Open Replication of Databases(GORDA)比较相似。
MySQL Group Replication的高可用性
通常情况下,一个容错系统会采用冗余,Replication的方法,来向那些可能会出故障的独立服务器(节点)复制整个系统的状态。
所以,就算其中的一部分节点down掉了,整个系统依然是可用的(可能会降低系统的性能或者是可用性,但是至少系统还是可用的)。
MySQL Group Replication非常符合如上的描述。节点down机是完全独立且隔离的,不会影响到其他节点。
同时,这种情况还会被group的其他节点通过分布式的错误检测机制所发现和追踪。
Down掉的节点,也会通过分布式的恢复机制来重新同步状态,完成后即可再次加入group。所以Group Replication并不需要故障转移。
在多主全更新特性的支持下,单节点down机甚至不会影响到全更新的这个过程。因此,MySQL Group Replication可以保证整个系统的持续可用。
需要注意的是,尽管数据库服务已经启动,但是节点的崩溃会导致连接到这个节点的所有客户端进行重新向,或者是重连。
这种场景并非MySQL Group Replication能解决的。连接器,负载均衡器,或者是如MySQL Farbic之类的中间件会更加适合解决这类问题。
此外,向group里面添加新的节点需要执行至少一次分布式恢复的逻辑。这其中包括了状态转移的操作,以及数据同步。
因此,比较推荐的做法就是在向group加入新节点之前,先同步好数据,这样子的话,加入group之后只需要同步状态,会使得执行分布式恢复的速度快很多。
简而言之,只依靠MySQL Group Replication,就可以提供高可用,高弹性,可靠的MySQL服务。