MySQL group Replication(MGR)
1.MGR概述:
MySQL group Replication(MGR)是MySQL官方于2016年12月推出的一个基于Paxos协议的状态复制,彻底解决基于传统的异步复制和半同步复制中数据一致性问题无法保证的情况。
在多写模式下支持集群中的所有节点都可以写入,特点如下∶
支持多节点并发地执行事务。MGR 是多节点多副本的集群,有多少个节点,就有多少份数据(最多九个节点,最低三个节点)。节点之间的数据是最终完全一致的。所以,要支持从多个节点并发执行事务,通过Paxos 协议保证从各个节点并发执行的事务在每个节点都以相同的顺序被执行/应用,这样节点之间的数据才能最终一致。
(2)自动事务冲突检测。在高并发的多写模式(MGR 的一种运行模式)下,节点间事务的提交可能会产生冲突。比如,两个不同的事务在两个节点上操作了同一行数据,这个时候就会产生冲突。假设事务A在A节点执行,事务B在B节点执行,两个事务更新同一条记录。在事务的执行阶段,这两个事务是完全独立的,相互不可见,完全感知不到对方的存在。只有当事务执行到binlog提交的阶段,将事务的日志通过Paxos 协议广播到对方节点之后,MGR们才能感知到对方的存在。利用这些日志,MGR 就能验证事务A跟事务B是否有冲突,这就是冲突检查机制。然后采用乐观策略∶依赖事务提交的时间先后顺序,先发起提交的节点正确提交,后面提交的会失败。
(3)数据强一致性保障。如果使用 MySQL主从做高可用方案,有时需要避免一些情况的发生∶主库服务器在宕机之前没有将所有的日志发送给从库,此时启用从库作为主库,数据将丢失;新主库跟老主库双活,并都有流量写入,就是通常所说的"脑裂"。切换程序以为老主库宕掉了,但实际上没有宕机,两边都有业务流量,数据的一致性将严重破坏。MGR在传输数据时使用Paxos 协议,保证数据传输的一致性和原子性。
(4)容错性高。这是Paxos 协议的多数派原则,当单个节点故障时,不影响集群整体可用性,只要没有超过半数的节点宕机就可以。MGR可以识别出组内成员是否挂掉(组内节点心跳检测)。一个节点失效后,将由其他节点决定是否将这个失效的节点从组里剔除。
2.MGR的基本原理:
MySQL 官方在 5.7.17 版本正式推出组复制(MySQL Group Replication,MGR)。一个复制组由若干个节点(数据库实例)组成,组内各个节点维护各自的数据副本,依靠分布式一致性协议(Paxos协议的变体)实现了分布式下数据的最终一致性。这也是一个无共享的复制方案,每一个节点都保存了完整的数据副本,提供了真正的数据高可用方案。
在MGR中,一个事务必须经过组内大多数节点决议并通过才能得以提交。简要地说,当客户端发起一个更新事务时,该事务先在本地执行,执行完成之后就发起对事务的提交操作。在还没有真正提交之前,需要将产生的复制写集广播出去,复制到其他成员。如果冲突检测成功,则组内决定该事务可以提交,其他成员可以应用,否则就回滚。最终所有组内成员以相同的顺序应用相同的修改,保证组内数据强一致性。如图10-3所示,由3个节点Masterl、Master2 和 Master3组成一个复制组,所有成员独立完成各自的事务。Consensus 层为一致性协议层。在事务提交过程中产生组间通信,由2个节点决议(certify)通过这个事务,事务才能够最终得以提交并响应。
从图可以看出,MGR和半同步复制的差异在 binlog日志生成的时间点上。MGR在事务执行后,提交时先将事务日志写入本地的广播通道,然后事务日志通过Paxos 协议进行全局一致性广播,广播完成之后,再进行事务间的冲突验证。如果验证通过,事务发起节点的用户线程继续提交该事务,然后生成 binlog 日志;其他节点在事务验证通过之后将事务日志转换成relay-log。如果事务验证失败,则事务的发起节点回滚该事务;其他节点将事务日志丢弃,不再转换成 relay-log。这样就保证了节点间数据的一致性。
MGR的工作模式相当于在事务进行binlog 提交的入口处设置了一个门闸,或者说是一个检查站。事务提交时,检查站会对事务进行验证,流程是∶先将事务日志通过Paxos 协议进行全局一致性广播;广播完成之后对这个事务进行事务间的冲突检测;如果检测通过,就放行,事务继续提交;如果不通过,事务就回滚。这就是MGR 的工作机制。
从MGR工作的原理可以看出,Group Replication基于Paxos协议的一致性算法校验事务执行是否有冲突,然后顺序执行事务,达到最终的数据一致性。组内其他节点只要验证成功了,就会返回成功的信号,即使当前数据并没有真正写入当前节点,所以这里的全同步复制其实还是虚拟的全同步复制。
MGR 还可以通过设置参数 group_replication single_primary_mode来进行控制是多主同时写入还是单主写入。在生产环境里,对数据延迟要求很苛刻的情况下,官方推荐单主写入,即建议在一个主节点上读写,避免造成数据不一致的情况发生。
此外,Group Replication 内部实现了限流措施,作用就是协调各个节点,保证所有节点执行事务的速度大于队列增长速度,从而避免丢失事务。实现原理很简单∶在整个 Group Replication 集群中,同时只有一个节点可以广播消息(数据),每个节点都会获得广播消息的机会(获得机会后也可以不广播),当慢节点的待执行队列超过一定长度后,MGR会广播一个FC PAUSE 消息,所以节点收到消息后会暂缓广播消息,并不提供写操作,直到该慢节点的待执行队列长度减小到一定长度后 Group Rplicaton数据同步才开始恢复。
3.MGR的服务模式
MGR服务模式MySQL组复制支持sigle-primary(单主)模式和multi-primary(多主)模式两种工作方式,默认是单主模式,也是官方推荐的组复制模式。在单主模式下,组内只有一个节点负责写入.但可以从任意一个节点读取,组内数据保持最终一致;多主模式即为多写方案,写操作会下发到组内所有节点,组内所有节点同时可读可写,也能够保证组内数据的最终一致性。MySQL参数配置文件mycnf里的配置项group_replication single_primary_mode用来配置节点到底是运行在单主模式还是多主模式。
一个MGR的所有节点必须配置使用同一种模式,不可混用。比如说 A、B、C三个节点组成一个MGR组,要么都运行在单主模式下,要么都运行在多主模式下。无论部署模式如何,组复制不处理客户端故障切换,必须由应用程序本身或中间件框架来处理。
3.1单主模式
在此模式下,只有一个节点 primary,主节点提供更新服务,组中的其他节点都自动设置为只读模式,即所有其他加入的节点自动识别主节点并设置自己为只读。主节点意外宕机或者下线时,在满足大多数节点存活的情况下,内部发起选举,选出下一个可用的读节点,提升为主节点。
在单主模式下,当master主节点故障时,会自动选举一个新的master主节点,选举成功后,MGR将设置为可写,其他 slave 将指向这个新的master。选择主节点的方法很简单——按字典顺序(使用其UUID)选择UUID最小的成员作为主节点。
在切换主节点期间,MGR机器不会处理应用重连接到新的主节点,这需要应用层自己来做或者由中间件Proxy去保证。
3.2多主模式
在多主模式下,没有单个主模式的概念,组中所有成员同时对外提供查询和更新服务,没有主从之分,也没有选举程序,因为没有节点发挥任何特殊的作用。加入组时,所有服务器都设置为读写模式。
多主模式在使用时有一些限制∶。
(1)不支持SERIALIZABLE串行隔离级,因为在MGR多主模式时,多个成员节点之间的并发操作至少目前无法通过锁来实现串行的隔离级别。
(2)不能完全支持级联外键约束。
(3)不支持在不同节点上对同一个数据库对象并发执行 DDL。并行执行DDL可能导致教据一致性等方面的错误,目前不支持在多节点同时执行同一对象的DDL。
以上限制检查可以通过选项参数 group_replicationenforce_update_everwherechecks 来(在单主模式部署,该选项必须设置为 FALSE)。
4.MGR 的注意事项
(1)在MGR集群中,只支持InnoDB存储引擎的表,并且该表必须有显式的主键。
(2)MGR 组通信引擎目前仅支持IPv4网络,并且对节点间的网络性能要求较高,低延迟、高带宽的网络是部署MGR集群的基础。
(3)在 MGR 多主模式下,一个事务在执行时并不会做前置的检查,但是在提交阶段会和其他节点通信,对该事务是否能够提交达成一个决议。在多个节点对相同记录进行修改,在提交时会进行冲突检测,首先提交的事务将获得优先权。例如,对同一条记录的修改,t1 事务先于2事务,那么t1事务在冲突检测后获得执行权,顺利提交,而t2事务进行回滚。显然,在这种多点写入条件下对同一条记录的并发修改会大量回滚,导致性能很低。MySQL官方建议将这种对于同一条记录的修改放在同一个节点执行,这样可以利用节点本地锁来进行同步等待、减少事务回滚、提高性能。
(4)在多主模式下可能导致死锁。比如 selec …for update在不同节点执行,由于多节点锁无法共享,因此很容易导致死锁。
(5)MGR集群目前最多支持9个节点,大于9个节点时将拒绝新节点的加入。
(6)整个集群的写入吞吐量是由最弱的节点限制,如果有一个节点变得缓慢(比如硬盘故障),那么整个集群将是缓慢的。为了稳定的高性能要求,所有的节点应使用统一的硬件。
(7)MGR集群自身不提供VIP 机制,需要结合第三方软件(如HAProxy+自定义脚本)实现秒级故障切换。
MGR 的发布所带来的意义在于完成了真正的多节点读写的集群方案,基于原生复制及Paxos 协议的组复制技术,并以插件的方式提供,实现了数据的强一致性。MGR的扩展性很强,是因为是多节点读写,Failover 切换变得更加简单,增加和删除节点也非常简单,能自动完成同步数据和更新组内信息的操作。
5.MGR部署安装:
1.环境介绍
3台服务器搭建3节点MGR集群,MySQL版本8.0.27,操作系统版本centos 7.4。
IP地址 主机名
192.168.0.31 mgr1
192.168.0.32 mgr2
192.168.0.33 mgr3
2.关闭防火墙
systemctl disable firewalld
systemctl stop firewalld
分别在主机mgr1,主机mgr2,主机mgr3上执行:
Mgr1:
Mgr2:
Mgr3:
3.配置各主机名和ip映射,所有节点配置一样
192.168.0.31 mgr1
192.168.0.32 mgr2
192.168.0.33 mgr3
主机mgr1:
主机mgr2:
主机mgr3:
4.编辑配置文件 /etc/my.cnf
主机mgr1:
[mysqld]
datadir=/var/lib/mysql
basedir=/var/lib/mysql
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
port=3306
socket=/var/lib/mysql/mysql.sock
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format = ROW
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name=“800dc45d-aab7-4d5c-9e02-e121bbb84989”
loose-group_replication_start_on_boot=off
loose-group_replication_local_address=“192.168.0.31:33061”
loose-group_replication_group_seeds=“192.168.0.31:33061,192.168.0.32:33061,192.168.0.33:33061”
loose-group_replication_bootstrap_group=off
loose-group_replication_single_primary_mode=FALSE
loose-group_replication_enforce_update_everywhere_checks= TRUE
主机mgr2:
[mysqld]
datadir=/var/lib/mysql
basedir=/var/lib/mysql
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
port=3306
socket=/var/lib/mysql/mysql.sock
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format = ROW
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name=“db50acce-481f-4e37-a985-6a177a4f2e54”
loose-group_replication_start_on_boot=off
loose-group_replication_local_address=“192.168.0.32:33061”
loose-group_replication_group_seeds=“192.168.0.31:33061,192.168.0.32:33061,192.168.0.33:33061”
loose-group_replication_bootstrap_group=off
loose-group_replication_single_primary_mode=FALSE
loose-group_replication_enforce_update_everywhere_checks= TRUE
主机mgr3:
[mysqld]
datadir=/var/lib/mysql
basedir=/var/lib/mysql
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
port=3306
socket=/var/lib/mysql/mysql.sock
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format = ROW
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name=“dcae8d75-4ed2-414b-b353-84dbe18187e5”
loose-group_replication_start_on_boot=off
loose-group_replication_local_address=“192.168.0.33:33061”
loose-group_replication_group_seeds=“192.168.0.31:33061,192.168.0.32:33061,192.168.0.33:33061”
loose-group_replication_bootstrap_group=off
loose-group_replication_single_primary_mode=FALSE
loose-group_replication_enforce_update_everywhere_checks= TRUE
4.参数说明:
group_replication变量使用的loose-前缀是指示Server启用时尚未加载复制插件也将继续启动
transaction_write_set_extraction = XXHASH64
*指示Server必须为每个事务收集写集合,并使用XXHASH64哈希算法将其编码为散列
loose-group_replication_group_name=“01e5fb97-be64-41f7-bafd-3afc7a6ab555”
*表示将加入或者创建的复制组命名为01e5fb97-be64-41f7-bafd-3afc7a6ab555
*可自定义(通过cat /proc/sys/kernel/random/uuid)
loose-group_replication_start_on_boot=off
*设置为Server启动时不自动启动组复制
loose-group_replication_local_address=“192.168.0.31:33061”
*绑定本地的192.168.0.31及33061端口接受其他组成员的连接,IP地址必须为其他组成员可正常访问
loose-group_replication_group_seeds=“192.168.0.31:33061,192.168.0.32:33061,192.168.0.33:33061”
*本行为告诉服务器当服务器加入组时,应当连接到192.168.0.31:33061,192.168.0.32:33061,192.168.0.33:33061
*这些种子服务器进行配置。本设置可以不是全部的组成员服务地址。
loose-group_replication_bootstrap_group = off
*配置是否自动引导组
loose-group_replication_ip_whitelist=”10.30.0.0/16,10.31.0…0/16,10.27.0.0/16″
*配置白名单,默认情况下只允许192.168.109.x连接到复制组,如果是其他IP则需要配置。
5.登录数据库,加载MGR插件,创建同步账号
分别登陆主机mgr1,主机mgr2,主机mgr3
执行主机mgr1:
set global validate_password.mixed_case_count=0;
set global validate_password.number_count=0;
set global validate_password.length=0;
set global validate_password.policy=0;
set global validate_password.special_char_count=0;
alter user root@localhost identified by ‘1234’;
SET SQL_LOG_BIN=0;
CREATE USER rpl_user@’%’ IDENTIFIED BY ‘1234’;
grant replication slave,replication client on . to rpl_user@’%’;
install plugin clone soname ‘mysql_clone.so’;
create user clone_user@’%’ identified by ‘1234’;
grant backup_admin on . to clone_user;
grant replication slave,replication client on . to clone_user;
INSTALL PLUGIN group_replication SONAME ‘group_replication.so’;
SHOW PLUGINS;
ALTER USER rpl_user@’%’ IDENTIFIED BY ‘1234’ PASSWORD EXPIRE NEVER;
ALTER USER rpl_user@’%’ IDENTIFIED WITH mysql_native_password BY ‘1234’;
ALTER USER clone_user@’%’ IDENTIFIED BY ‘1234’ PASSWORD EXPIRE NEVER;
ALTER USER clone_user@’%’ IDENTIFIED WITH mysql_native_password BY ‘1234’;
SET SQL_LOG_BIN=1;
set global gtid_mode=1;
set global gtid_mode=2;
set global ENFORCE_GTID_CONSISTENCY=1;
set global gtid_mode=3;
show variables like ‘%GTID_MODE%’;
CHANGE MASTER TO MASTER_USER=‘rpl_user’, MASTER_PASSWORD=‘1234’ FOR CHANNEL ‘group_replication_recovery’;
查询server_id正确:
主机mgr2:
set global server_id=2;
set global validate_password.mixed_case_count=0;
set global validate_password.number_count=0;
set global validate_password.length=0;
set global validate_password.policy=0;
set global validate_password.special_char_count=0;
alter user root@localhost identified by ‘1234’;
SET SQL_LOG_BIN=0;
CREATE USER rpl_user@’%’ IDENTIFIED BY ‘1234’;
grant replication slave,replication client on . to rpl_user@’%’;
install plugin clone soname ‘mysql_clone.so’;
create user clone_user@’%’ identified by ‘1234’;
grant backup_admin on . to clone_user;
grant replication slave,replication client on . to clone_user;
INSTALL PLUGIN group_replication SONAME ‘group_replication.so’;
SHOW PLUGINS;
ALTER USER rpl_user@’%’ IDENTIFIED BY ‘1234’ PASSWORD EXPIRE NEVER;
ALTER USER rpl_user@’%’ IDENTIFIED WITH mysql_native_password BY ‘1234’;
ALTER USER clone_user@’%’ IDENTIFIED BY ‘1234’ PASSWORD EXPIRE NEVER;
ALTER USER clone_user@’%’ IDENTIFIED WITH mysql_native_password BY ‘1234’;
SET SQL_LOG_BIN=1;
set global gtid_mode=1;
set global gtid_mode=2;
set global ENFORCE_GTID_CONSISTENCY=1;
set global gtid_mode=3;
show variables like ‘%GTID_MODE%’;
CHANGE MASTER TO MASTER_USER=‘rpl_user’, MASTER_PASSWORD=‘1234’ FOR CHANNEL ‘group_replication_recovery’;
查询主机mgr2中MySQL中server_id不对,修改server_id:
set global server_id=2;
主机mgr3:
set global server_id=2;
set global validate_password.mixed_case_count=0;
set global validate_password.number_count=0;
set global validate_password.length=0;
set global validate_password.policy=0;
set global validate_password.special_char_count=0;
alter user root@localhost identified by ‘1234’;
SET SQL_LOG_BIN=0;
CREATE USER rpl_user@’%’ IDENTIFIED BY ‘1234’;
grant replication slave,replication client on . to rpl_user@’%’;
install plugin clone soname ‘mysql_clone.so’;
create user clone_user@’%’ identified by ‘1234’;
grant backup_admin on . to clone_user;
grant replication slave,replication client on . to clone_user;
INSTALL PLUGIN group_replication SONAME ‘group_replication.so’;
SHOW PLUGINS;
ALTER USER rpl_user@’%’ IDENTIFIED BY ‘1234’ PASSWORD EXPIRE NEVER;
ALTER USER rpl_user@’%’ IDENTIFIED WITH mysql_native_password BY ‘1234’;
ALTER USER clone_user@’%’ IDENTIFIED BY ‘1234’ PASSWORD EXPIRE NEVER;
ALTER USER clone_user@’%’ IDENTIFIED WITH mysql_native_password BY ‘1234’;
SET SQL_LOG_BIN=1;
set global gtid_mode=1;
set global gtid_mode=2;
set global ENFORCE_GTID_CONSISTENCY=1;
set global gtid_mode=3;
show variables like ‘%GTID_MODE%’;
CHANGE MASTER TO MASTER_USER=‘rpl_user’, MASTER_PASSWORD=‘1234’ FOR CHANNEL ‘group_replication_recovery’;
查询主机mgr2中MySQL中server_id不对,修改server_id:
set global server_id=3;
7.启动MGR单主模式
启动MGR,在主库(192.168.0.31)上执行
set global group_replication_bootstrap_group=ON;
START group_replication;
set global group_replication_bootstrap_group=OFF;
SELECT * FROM performance_schema.replication_group_members;
8.其他节点加入MGR,在从库(192.168.0.32,192.168.0.33)上执行
主机mgr2执行:
START GROUP_REPLICATION;
主机mgr3上执行:
START GROUP_REPLICATION;
可以看到,3个节点状态为online,并且主节点为192.168.0.31,只有主节点可以写入,其他节点只读,MGR单主模式搭建成功。
9.切换多主模式
MGR切换模式需要重新启动组复制,因些需要在所有节点上先关闭组复制,设置 group_replication_single_primary_mode=OFF 等参数,再启动组复制。
停止组复制(所有节点执行):
stop group_replication;
set global group_replication_single_primary_mode=OFF;
set global group_replication_enforce_update_everywhere_checks=ON;
主机mgr1:
主机mgr2:
主机mgr3:
任意选择一个节点执行:
节点执行:
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
其他节点执行:
START GROUP_REPLICATION;
查看信息:
10.切换至单主模式:
所有节点执行:
stop group_replication;
set global group_replication_enforce_update_everywhere_checks=OFF;
set global group_replication_single_primary_mode=ON;
mgr1:
mgr2:
mgr3:
主节点(192.168.0.31)执行
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
mgr1:
从节点(192.168.0.32、192.168.0.33)执行
START GROUP_REPLICATION;
mgr2:
mgr3:
查看MGR组信息
8.报错:
1.mysql> CHANGE MASTER TO MASTER_USER=‘rpl_user’, MASTER_PASSWORD=‘1234’ FOR CHANNEL ‘group_replication_recovery’;
ERROR 4070 (HY000): When configuring a group replication channel you must do it when GTID_MODE = ON.
解决方案:
执行: set global gtid_mode=1;
set global gtid_mode=2;
set global ENFORCE_GTID_CONSISTENCY=1;
set global gtid_mode=3;
6.MGR监控:
组 复 制 的状态 信 息被保 存在 performance_schema 这 个系统 数 据库下的replication group_members、replication group_member_stats 等几个表中,可以进行 SQL语句查询。
通过SQL语句"selectfrom performance schemarplication group_members;"来查看所有组内成员是否都是ONLINE 状态.
查看组中的同步情况、当前复制状态,执行命令"electfrom performance_schema.replication group_member_stats\G;",
CHANNEL_NAME :通道名称事务。
VIEW_ID∶前缀部分由组初始化时产生,为当时的时间戳,组存活期间该值不分发生变化。所以,该字段可VIEW_ID用于区分两个视图是否为同一个组的不同时间点;后缀部分在每次视图更改时会触发一次更改,从1开始单调递增。
MEMBER_I:实例的 UUID,每个成员都有不同的值。
COUNT_TRANSACTIONS_IN_QUEUE:待处理冲突检测的队列中的事务数。一旦检测到事务 的冲突并通过检测,它们就会排队等待提交。
COUNT_TRANSACTIONS_CHECKEED:已检测冲突的事务数。
COUNT_CONFLICTS_DETECTED : 未通过冲突检测的事务数。
COUNT_TRANSACTIONS_ROWS_ VALIDATING:用于认证但未进行垃圾回收的事务数量。
TRANSACTIONS_COMMITTED_ALL_MEMBERS:已在复制组的所有成员上成功提交的事务数量。
LAST_CONFLICT_TRANSACTION:已检测的最后一个无冲突事务的事务标识符。
检测节点是否存在堆积(大于0表示有延迟):
select COUNT_TRANSACTIONS_IN_QUEUE from performance_schema.replication_group_member_stats where MEMBER_ID=@@server_uuid;
7.MGR的主节点故障无感知切换:
需要使用MGR+ProxySQL(中间件)实现