ysql5.7.17推出了一个全新的复制组件:mysql group replication,将原有的gtid复制功能进行了增强,增加了多主多写模式。组复制基于paxos协议实现复制组内最终的数据一致性。
下图是传统异步复制原理:master事务的提交不受slave的影响,数据复制异步进行,slave收到master传过来的binlog之后写入到自己的relay log,并且异步的执行sql应用,master节点不关心slave的日志传输以及日志应用情况。
下图是半同步复制原理:master必须确保至少一个slave节点收到relay log并且返回给master确认收到指令后master节点才可以进行事务的提交,此时slave节点应用relay log依然是异步的:
由于传统异步复制的缺陷,可能会导致主从数据不一致的问题,在主节点异常宕机时从节点可能造成数据丢失。基于这个缺陷,mysql官方发布了group replication(MGR)功能。下图是mgr的原理图:
Mysql group replication复制技术利用基于paxos的分布式一致性协议保证数据的一致性,大致思想是一次写操作分为两个部分:prepare/promise, 和 propose/accept,也就是我们熟知的两阶段提交。第一次由提交者Leader向所有其他服务器发出prepare消息准备请求,所有服务器中大多数如果回复就表示准备好了,可以接受写入;第二次提交者向所有服务器发出正式建议propose,所有服务器中大多数如果回复已经接收就表示成功了。当然算法很复杂,还涉及到提议编号等。简单来说就是一个事务的提交必须经过复制组内大部分成员(N/2+1)的投票才能进行提交,其实这样看来paxos协议主要提供了了分布式环境下不同主机对于同一个事务的协商机制。并且协议提供了选主机制,当主节点宕机,从节点会自动通过选举机制被选举为主节点。这样paxos就提供了分布式环境的高容错的一致性算法。
MGR在多主模式产生后其实诞生了一个问题,因为事务或者说binlog具有顺序性,最终写入数据库的事务或者日志肯定是顺序写入的,这样如果在分布式环境下如果每个节点都提供写功能,则一定会造成不同节点事务的冲突。对于这个问题mgr采用乐观锁来解决这个问题。乐观锁的大体思想就是对于某个操作mysql有一个transaction set(事务集)的概念,对某一个数据进行修改时不对这个数据进行加锁,只有在提交的时候才进行冲突检测,事务集里后操作的事务则返回失败。相比采用悲观锁,这种方式有利有弊,好处是能提供比悲观锁更好的性能,因为我们知道悲观锁是类似互斥锁的概念,事务对某个数据进行操作时,先对它进行加锁,其他事务无法访问该数据,这样的话在互联网、银行等高并发的场景下效率十分低下。但是采用悲观锁的坏处就是后来的事务被返回错误,这样就要求应用程序要对自己的代码进行修改,需要具有处理sql失败的能力。
说完理论,再说说实践,mgr提供了两种复制模式,单主模式和多主模式。从字面意思可以理解,单主只能有一个节点提供写,多主每个节点都能进行写操作。group_replication_single_primary_mode这个控制了单主/多主模式,参数值为1是单主模式,0为多主模式。搭建过程很简单,下面以三节点多主模式为例进行搭建。
在每个节点的配置文件my.cnf下添加以下配置:
#gitd repl
server_id=1
gtid_mode=on
enforce_gtid_consistency=on
master_info_repository=table #复制元数据存入系统表
relay_log_info_repository=table #复制元数据存入系统表
binlog_checksum=none #禁用二进制日志事件校验和
log_slave_update=on
log_bin=/mysqldata/myinst1/binlog/mysql-bin
log_bin_index=/...
binlog_format=row
#group replication
transaction_write_set_extraction=XXHASH64 #写集合使用XXHASH64算法将其编码为散列
loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
loose-group_replication_start_on_boot=off #不开机启动
loose-group_replication_bootstrap_group=off #插件不自动引导组
loose-group_replication_local_address="192.168.11.178:33061"
loose-group_replication_group_seeds="192.168.11.177:33061,192.168.11.178:33061,192.168.11.179:33061"
group_replication_single_primary_mode=off #使用多主模式
然后在每个节点安装group replication插件:
INSTALL PLUGIN group_replication SONAME‘group_replication.so’;
创建复制用户:
grant replication slave on *.* tomgrpl@’197.168.11.%’ identified by ‘mgrpl’;
执行change master命令:
CHANGE MASTER TO MASTER_USER='mgrpl', MASTER_PASSWORD='mgrpl' FOR CHANNEL 'group_replication_recovery';
在第一个节点启动group replication之前需要执行如下命令,启动完后再进行关闭,这个参数后面进行单独讲解:
SET GLOBAL group_replication_bootstrap_group=ON;
启动第一个节点:
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=off
然后启动其他节点:
START GROUP_REPLICATION;
以上是mgr搭建过程。在搭建过程中也遇到不少坑。
1.如果有原来的同步/半同步信息,需要先清空,否则安装不上group_replication_plugin,报初始化group_replication函数失败。
2.如果开启了并行写入(slave_parallel_workers!=0),需要打开slave_preserve_commit_order=on,否则集群无法启动。
3.group_replication_start_on_boot=on参数只有在第一次启动集群或者集群全部宕机后在第一台启动的数据库上动态设置,等待所有服务器加入集群后再设置为off。
4.performance_schema下有一些集群的状态信息表:replication_connection_status、replication_group_member_stats、replication_group_members等,但是查看前需要启用performance_schema
对于group_replication_bootstrap_group这个参数,mysql官方文档有如下介绍:
Configure this server to bootstrap the group. This option must only be set on one server and only when starting the group for the first time or restarting the entire group. After the group has been bootstrapped, set this option to OFF. It should be set to OFF both dynamically and in the configuration files. Starting two servers or restarting one server with this option set while the group is running may lead to an artificial split brain situation, where two independent groups with the same name are bootstrapped.
从上面的说明可以看出这个参数在第一次启动集群中的第一个节点之前需要进行开启,主要是控制避免集群脑裂分割的情况。
文章最后对MGR的优缺点进行一个简单总结:
MGR复制优点:
a.多写,无需进行读写分离
b.事务乐观锁,提高了性能
c.无集中管理,无状态
d.每个节点各自拥有一份完整数据
e.良好的扩展性,节点增加简便
f.基于paxos协议的分布式事务,能够保证一致性
MGR缺点:
a.仅支持innodb,每张表都要有一个主键,用于做write set的冲突检测
b.必须打开gtid,日志格式必须为row,用于选主与write set
c.最多支持9个节点
d.由于采用乐观锁,事务可能commit失败,需要应用配合
e.不支持save point,不支持外键