一、MGR基础
1、复制背景
1.1 主从复制
传统MySQL提供了一种简单的主从复制方式,一个主,有一个或者多个从。主节点执行和提交事务,然后将他们发送到从节点,以重新执行(在基于语句的复制中)或者应用(在基于行的复制中)。这是一个 shared-nothing的系统,默认所有的server都有一个完整的数据副本。
MySQL异步复制
可以看到的是,异步复制是不考虑从库有没有接受binlog文件的,所以会造成主从不一致: 当主库commit一个事务后,数据库发生宕机,刚好它的binlog还没来得及传送到slave端,这个时候选任何一个slave端都会丢失这个事务,造成数据不一致情况。
所以还要一种半同步复制,它在协议中加了一个同步的步骤,这意味着主节点提交的时候需要等待从节点确认它已经收到了事物,只有这样主节点才能够继续提交。
MySQL半同步复制
从上面两个图中可以看到传统的MySQL复制协议。蓝色的箭头表示不同的server之间或者server和client应用之间的信息交互。1.2 组复制
不管是异步复制还是半同步复制,都有两个弊端:
- 写操作集中在MASTER服务器上;
- MASTER宕机后,需要人为选择新主并重新给其他的slave端执行change master(可用第三方工具实现,但是mysql的复制就是没提供,所以也算是弊端)
基于这种现状,MySQL官方提供了 MySQL Group Replication。
MySQL组复制
相对于传统的复制,MGR提供的功能有:- 多主:多写模式下支持集群中的所有节点都可以写入
- 一致性: 依靠分布式一致性协议(Paxos协议的变体)确保集群中大部分节点收到日志
- 弹性:同个MGR中,节点的加入或者移除都是自动调整
- 可用性: 确保系统发生故障(包括脑裂)依然可用,双写对系统无影响
2、理论基础
MGR有单主和多主两种模式。
1.1 组复制
复制组由多个server成员组成,并且组中每个成员可以独立的执行事务。但是所有的读写(RW)事务只有在冲突检测成功后才会提交。只读事务(RO)不需要冲突检测,可以立即提交。
在始发server上,当事务准备好提交时,该server会广播写入值(已改变的行)和对应的写入集(已更新行的唯一标识符)。然后为该事物建立一个全局的顺序。最终,这意味着所有server成员以相同的顺序接收同一组事务。
因此,所有的server成员以相同的顺序应用相同的更改,以确保组内一致。
在不同的server上并发执行的事物可能存在冲突,根据组复制冲突检测机制。如果在不同server上执行的两个并发事务更新同一行,则存在冲突。冲突解决过程中,排在最前面的事物可以在所有server成员上提交,第二个事物在源server上回滚,并且在组中其他server上删除。其实这就是分布式的先提交当选原则。
1.2 容错
MySQL Group Replication构建于Paxos分布式算法的实现之上,以在服务器之间提供分布式协调。因此,它需要大多数服务器处于活动状态才能达到仲裁成员数,从而做出决定。这直接影响系统可以容忍的故障数量,以及自身及其整体功能。容忍f个故障所需的服务器数量 n= 2 x f + 1。
在实践中,这意味着为了容忍一个故障,该组必须具有三个server。因此,如果一个server发生故障,仍然有两个server构成多数(三分之二),并允许系统继续自动做出决策并继续进行。但是,如果第二个server以外fail掉 ,那么该组(剩下一个server)会锁定,因为没有多数人可以达成协议。
二、部署安装
1、单写部署
官方文档提供的是在一台服务器上安装3个mysql来搭建单主模式,这里是按照官方文档的步骤,但是部署在三台服务器上。
1.1 环境:
1.2 配置文件
编辑配置文件/data1/my3306.cnf,3个节点 除了server_id、loose-group_replication_local_address、report_host 三个参数不一样外,其他保持一致。
#GTID
server_id=11
gtid_mode=ON
#强制gtid一致性,开启后对于特定create table不被支持
#ON:不允许任何事务违反GTID一致性
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_bin=binlog
log_slave_updates=ON
binlog_format=ROW
master_info_repository=TABLE
relay_log_info_repository=TABLE
#MGR
#定义用于生成标识与事务关联的写入的哈希的算法,哈希值将用于分布式冲突检测和处理
transaction_write_set_extraction=XXHASH64
#通知插件它正在加入或创建的组,可以使用SELECT UUID()生成一个UUID
loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
#指示插件在服务器启动时不自动引导组操作
loose-group_replication_start_on_boot=OFF
件使用哪个ip:port与组中的其他成员进行内部通信。这里的ip与端口不能与MySQL提供的ip:port 相同,如果使用相同ip则port必须不相同
loose-group_replication_local_address= "192.168.1.11:33061"
#设置组成员的主机名和端口
loose-group_replication_group_seeds= "192.168.1.11:33061,192.168.1.12:33061,192.168.1.13:33061"
#插件是否引导组,此选项只能在任何时候在一个服务器实例上使用,通常是第一次引导组时(或者在整个组关闭并重新备份的情况下)
loose-group_replication_bootstrap_group= off
1.3 安装启动
每个服务器是一个节点,都安装好mysql
mysqld --defaults-file=/data1/my3306.cnf --datadir=/data1/mysql3306 --user=mysql3306 --initialize-insecure
mysqld --defaults-file=/data1/my3308.cnf --datadir=/data1/mysql3308 --user=mysql3308 --initialize-insecure
启动:
/usr/local/mysql-5.7.26/bin/mysqld_safe --defaults-file=/data1/mysql3306/my3306.cnf &
/usr/local/mysql-5.7.26/bin/mysqld_safe --defaults-file=/data1/mysql3308/my3308.cnf &
1.4 安装MGR插件,设置复制账号(所有节点执行)
#无密码登录
#安装MGR插件
mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';
#设置复制账号
mysql> SET SQL_LOG_BIN=0;
mysql> CREATE USER repl@'%' IDENTIFIED BY 'repl';
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'%';
mysql> FLUSH PRIVILEGES;
mysql> SET SQL_LOG_BIN=1;
mysql> CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='repl' FOR CHANNEL 'group_replication_recovery';
1.5 启动MGR单主模式
#主启动(任选一个作为主库,这里选择了1.11)
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
mysql> SELECT * FROM performance_schema.replication_group_members;
#查看MGR组信息
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | 463679cf-7222-11e9-9459-000c29a6154a | vm1 | 3306 | ONLINE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
#其他节点加入MGR,在从库(1.12,1.13)上执行
mysql> START GROUP_REPLICATION;
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 0bbd0bcc-7223-11e9-a4fc-000c296aa366 | 192.168.1.12 | 3308 | ONLINE |
| group_replication_applier | 18f64c9b-7223-11e9-bba2-000c293610f1 | 192.168.1.13 | 3308 | ONLINE |
| group_replication_applier | 463679cf-7222-11e9-9459-000c29a6154a | 192.168.1.11 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
#查找主节点:
mysql> SHOW STATUS LIKE 'group_replication_primary_member';
+----------------------------------+--------------------------------------+
| Variable_name | Value |
+----------------------------------+--------------------------------------+
| group_replication_primary_member | 463679cf-7222-11e9-9459-000c29a6154a |
+----------------------------------+--------------------------------------+
或者
select variable_value from performance_schema.global_status where variable_name = 'group_replication_primary_member';
这个时候从是只读的,在上执行SQL会报错:
ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement
2、多写部署(单主切换到多主)
多主模式可以直接由单主模式切换, 需要在所有节点上先关闭组复制,设置 group_replication_single_primary_mode=OFF 等参数,再启动组复制。
# 停止组复制(所有节点执行):
mysql> stop group_replication;
#单主模式设置为ON,多主模式设置为OFF
mysql> set global group_replication_single_primary_mode=OFF;
#在所有节点启用多主数据更新的严格一致性检查
mysql> set global group_replication_enforce_update_everywhere_checks=ON;
# 随便选择某个节点执行(这里选择之前是从节点的1.12执行)
mysql> SET GLOBAL group_replication_bootstrap_group=ON;
mysql> START GROUP_REPLICATION;
mysql> SET GLOBAL group_replication_bootstrap_group=OFF;
# 其他节点执行
mysql> START GROUP_REPLICATION;
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 0bbd0bcc-7223-11e9-a4fc-000c296aa366 | 192.168.1.12 | 3308 | ONLINE |
| group_replication_applier | 18f64c9b-7223-11e9-bba2-000c293610f1 | 192.168.1.13 | 3308 | ONLINE |
| group_replication_applier | 463679cf-7222-11e9-9459-000c29a6154a | 192.168.1.11 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
#多主模式,变量 group_replication_primary_member 为空(8.0版本会直接显示MEMBER_ROLE)
mysql> SHOW STATUS LIKE 'group_replication_primary_member';
+----------------------------------+-------+
| Variable_name | Value |
+----------------------------------+-------+
| group_replication_primary_member | |
+----------------------------------+-------+
可以测试写入是多节点的
3、多主切换到单主模式
# 所有节点执行
mysql> stop group_replication;
mysql> set global group_replication_enforce_update_everywhere_checks=OFF;
mysql> set global group_replication_single_primary_mode=ON;
# 主节点(192.168.1.11)执行
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
# 从节点(192.168.1.12、192.168.1.13)执行
START GROUP_REPLICATION;
三、节点加入
1.1 环境:(这里是在多主模式下加入,单主模式下亦然)
因为MGR也是使用的GTID,所以新节点的加入也是需要通过备份和binlog来进行恢复,然后加入组复制的。这里因为binlog都在,所以直接加入一个新安装的MySQL实例加入到组里。
1.2 新节点加入步骤
#新节点配置文件:
loose-group_replication_local_address= "192.168.1.14:33061"
loose-group_replication_group_seeds= "192.168.1.11:33061,192.168.1.12:33061,192.168.1.13:33061,192.168.1.14:33061"
#在其他三个节点执行:
set global group_replication_group_seeds='192.168.1.11:33061,192.168.1.12:33061,192.168.1.13:33061,192.168.1.14:33061';
#新节点加入:
set global group_replication_single_primary_mode=OFF;
set global group_replication_enforce_update_everywhere_checks=ON;
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='repl' FOR CHANNEL 'group_replication_recovery';
#查看所有节点:
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 0bbd0bcc-7223-11e9-a4fc-000c296aa366 | 192.168.1.12 | 3308 | ONLINE |
| group_replication_applier | 18f64c9b-7223-11e9-bba2-000c293610f1 | 192.168.1.13 | 3308 | ONLINE |
| group_replication_applier | 241b7e73-74d4-11e9-aef0-000c293af32c | 192.168.1.14 | 3308 | ONLINE |
| group_replication_applier | 463679cf-7222-11e9-9459-000c29a6154a | 192.168.1.11 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
到此新节点已经加入。
四、故障转移
1、多主模式
第三部分新加入了节点的时候属于多主模式,所以首先来看看,当多主模式下,有节点发生了故障会如何处理。
从图中很容易看出来,一个节点fail了,写线程直接连接到其他的节点上了,因为所有节点都是读写的,所以也就存在选取哪个为主节点了。
这里直接将1.12这个节点的MySQL给kill掉,看看对组的影响:
#kill了1.12之后,很明显这个节点直接被移出了
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 18f64c9b-7223-11e9-bba2-000c293610f1 | 192.168.1.13 | 3308 | ONLINE |
| group_replication_applier | 241b7e73-74d4-11e9-aef0-000c293af32c | 192.168.1.14 | 3308 | ONLINE |
| group_replication_applier | 463679cf-7222-11e9-9459-000c29a6154a | 192.168.1.11 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
#给集群插入一些数据,再讲kill的节点重新加入组里
set global group_replication_single_primary_mode=OFF;
set global group_replication_enforce_update_everywhere_checks=ON;
START GROUP_REPLICATION;
可以发现新插入的数据同步到1.12节点了
2、单主模式
单主模式存在一个选取哪个为新主的过程,大体过程为:
通过安装字典顺序(使用其UUID)来排序剩余的server成员并选择列表中第一个成员来作为下一个 主节点。
先将多主模式切换到单主模式,查看节点情况:
#按照官方文档规则,单主模式下主fail了,应该是1.12这个server充当着
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 0bbd0bcc-7223-11e9-a4fc-000c296aa366 | 192.168.1.12 | 3308 | ONLINE |
| group_replication_applier | 18f64c9b-7223-11e9-bba2-000c293610f1 | 192.168.1.13 | 3308 | ONLINE |
| group_replication_applier | 241b7e73-74d4-11e9-aef0-000c293af32c | 192.168.1.14 | 3308 | ONLINE |
| group_replication_applier | 463679cf-7222-11e9-9459-000c29a6154a | 192.168.1.11 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
mysql> SHOW STATUS LIKE 'group_replication_primary_member';
+----------------------------------+--------------------------------------+
| Variable_name | Value |
+----------------------------------+--------------------------------------+
| group_replication_primary_member | 463679cf-7222-11e9-9459-000c29a6154a |
+----------------------------------+--------------------------------------+
#kill掉1.11这个主,看看哪个成为新的主
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 0bbd0bcc-7223-11e9-a4fc-000c296aa366 | 192.168.1.12 | 3308 | ONLINE |
| group_replication_applier | 18f64c9b-7223-11e9-bba2-000c293610f1 | 192.168.1.13 | 3308 | ONLINE |
| group_replication_applier | 241b7e73-74d4-11e9-aef0-000c293af32c | 192.168.1.14 | 3308 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
#果然,1.12这台server成为了主
mysql> SHOW STATUS LIKE 'group_replication_primary_member';
+----------------------------------+--------------------------------------+
| Variable_name | Value |
+----------------------------------+--------------------------------------+
| group_replication_primary_member | 0bbd0bcc-7223-11e9-a4fc-000c296aa366 |
+----------------------------------+--------------------------------------+
#再要想加入1.11这台,按照之前加入节点的步骤就行了
参考:https://dev.mysql.com/doc/refman/5.7/en/group-replication.html