mysql group replication,组复制,提供了多写(multi-master update)的特性,增强了原有的mysql的高可用架构。mysql group replication基于mysql插件架构实现,本身就是一个mysql插件。
提供的特性:
- 多写,写冲突检测
- 良好的扩展能力,可动态增删节点,组成员自动管理
- 组内高可用
- 确保组内数据最终一致性【重要】(通过分布式协议和分布式recovery机制保证)
1. 架构
组复制基于mysql插件架构实现,本身作为一个mysql插件而存在。如上图所示,蓝色和绿色两部分表示组复制架构。
组复制依赖mysql server主模块(上图灰色部分)以及mysql的复制架构,包括binary log caches、binary log applier模块、GTID、relay log模块、replication applier threads(组复制distributed recovery第一个阶段会使用这些线程)和performance_schema(用于存储监控组复制的状态信息)。
上图架构中各个模块的作用简单介绍如下:
- APIs
API层提供了组复制与上层mysql server交互的接口。上层(mysql server)会主动通知下层(组复制)根据它的状态去做对应的动作,包括server的启动、恢复、接受连接、提交事务等等。组复制根据这些接口调用做出对应的响应动作。
- Capture、Applier、Recovery
组复制中的三个核心模块,当上层API发生调用,将根据调用类型路由到下面这三个模块执行相应的逻辑。Capture模块负责追踪事务执行的上下文信息;Applier模块负责将远程事务加载到本地执行;Recovery模块管理组内节点的recovery。
- Replication Protocol Logics
Replicaiton Protocol Logics模块实现了replication协议的逻辑,逻辑功能包括: 处理冲突检测、接收并传播事务到组内去执行。
- Group Communication System API
提供状态机(Replication State Machine)的高层抽象API,以此分离与下层协议实现的逻辑。
- Group Communication Engine
根据上层的状态机API的抽象,这一层主要提供Paxos协议变种的核心实现。
2. Group
一个Group由若干个mysql节点组成,每个Group有自己的唯一命名来标识,格式为UUID。Group内的mysql节点数据保持一致。一个组可以动态增加和删除节点。
组成员管理自动化。新增一个节点到一个组里面,这个节点会自动同步自身数据跟组内其他节点保持一致。保持同步会利用Asynchronous MySQL replication。当一个节点从一个组里移除,将自动更改group的配置,以让其他节点知道这个节点的移除。
3. Multi Primary
组复制有两种运行模式: (1) Single-Primary Mode 和 (2) Multi-Primary Mode,其中Single-Primary Mode表示一个组里面只有一个节点用于写,而Multi-Primary Mode下组内所有节点都可以参与写,事务的提交可以在组内的任意一个节点进行。
看起来Multi-Primary模式好像很屌!但是,这个模式下目前存在挺多的使用限制:
3.1 并发写冲突
Multi-Primary模式下,对于事务的提交,需要组内所有节点进行通讯,共同决定事务是否可以提交。高并发模式下有可能出现分布式提交冲突——不同的节点并发提交的事务操作了同一行数据。group replication提供的乐观的解决方案是——先提交(时间序靠前的)的事务,能够正确被提交,而后面提交的事务,将会失败。
官方建议,避免并发写冲突的最好的做法是将这些会产生冲突的写事务放到同一个节点上进行提交。
3.2 并发DDL
MySQL目前不支持事务类型的DDL,也就是说DDL操作不是一个原子操作,在Multi-Primary模式下,DDL操作并发地在多个节点执行,很有可能产生数据不一致的情况。因此,官方提醒,在Multi-Primary下DDL操作最好在同一个节点上执行。
如果运行在Single-Primary模式下,就没有上面所提到的限制了。
4. Distributed Recovery
当一个新节点(被叫作joiner)加入一个group,需要从group里面其他节点同步状态。这个新节点从其他节点获取这些落后数据,并接受新提交的事务,最终保持与group内其他节点数据一致的这个过程,称为distributed recovery(DR)。DR过程分为两个阶段:
4.1 阶段1
Joiner选择group里面的一个节点作为复制源(这个节点被称作donor),基于原有的异步复制技术进行recovery。这个过程会建立从donor到joiner的replication channel,joiner不断接收从donor端发送的binary log,apply到自身。于此同时,joiner还会监听并缓存group里面自从它加入以来产生的新事务。阶段1结束后,将会关闭之前建立在donor和joiner之间的replication channel。
donor有可能在这个阶段挂掉,此时joiner将自动选择集群内的其他一个存活节点作为donor,继续之前的recovery。这个过程无需人为干预。
4.2 阶段2
阶段2,joiner执行在阶段1缓存队列里的新事务(直到缓存队列为空),然后将joiner的状态标记为online
,表示这个节点的数据已经跟其他节点保持一致,可以被正常访问。
只有online的节点才能正常接收用户请求。
4.3 Recovering From a Point-in-time
这里有个问题: 在阶段1,joiner怎么知道它在什么时候会完成所有缺失数据的recovery,单单依靠GTID机制是不够的(依靠GTID机制只能让joiner知道它是否少了这部分数据)。这个时候,需要有另外一个机制,来代表一个时间点——这个时间点能够标识阶段1的结束,也就是说在这个时间点上,joiner已经完成了从donor上所有缺失数据的recovery,接下来可以进入阶段2了。
这个机制,就是binary log view change markers,在阶段1传输binary log流中,会带上view change markers。那么joiner在接受到binary log的时候,就可以利用这些标识来判断那个时间点的到来。
4.4 View and View Changes
在解释view change markers之前,需要介绍view change markers关联的概念: View 和 View Changes。
- View
一个Group里面所有节点在某个时间点上达成的配置,对外表示为一个View。
- View Changes
View Changes指引起Group配置发生变化的事件,比如新增节点,删除节点。
每个View有一个唯一标识(View Identifier)进行标识。当发生View Changes时,就会生成这样一个唯一标识来表示这个新的View。
View changes产生的view ids作为新节点与donor同步数据的临界点。View change事件对应到binary log里面是一个新binary log event的实现——view change log event。在这个view change log event里面携带了view id。
假设在之前的multi-primary部署示例完成后,我们通过登陆S2执行以下语句检索到view change event log:
mysql > show binlog events\G;
可以看到如下view change event log:
*************************** 9. row ***************************
Log_name: binlog.000001
Pos: 742
Event_type: View_change
Server_id: 1
End_log_pos: 881
Info: view_id=14817781596395401:1
4.5 Distributed Recovery Example
接下来我们分析一个例子: 一个新节点加入一个group的过程
4.5.1 Begin: Stable Group
一开始group有3个节点S1、S2、S3,整个group处于稳定状态
4.5.2 View Change: Member Joins
接下来新节点S4准备加入group,引起view changes(VC4)。所有的节点都会在缓存队列里面缓存view change event log到队列。同时,新节点S4会选择group里面的某个节点作为donor。
所有的节点都要缓存view change event log到队列,因为这个event log之前有可能还存在着未apply的event log,这些event log是属于old view的。
4.5.3 State Transfer: Catching Up
当S4选择好donor后,就会建立与donor的replication channel,基于async replication机制从donor上同步状态进行recovery,直到S4遇到期望的view change marker为止。
于此同时,S4也会不断地接受group里面下发的新提交事务,缓存到队列里面。
4.5.4 Finish: Caught Up
当S4遇到期望的view change marker(根据view change event log 判断)以后,与donor之间的replication channel将会被关闭,这个时候,就会从之前的缓存队列里面取新的事务(图示中的T22)进行apply,直到缓存队列为空,然后就可以将S4的状态标记为online,这就是阶段2的主要动作。
注意,阶段2的执行周期是不确定的,取决于系统的负载情况,因为在阶段2期间,有可能系统不断产生新的事务。系统的负载越高,产生的事务会越多,那么阶段2经历的时间自然也就越长了。