官方文档:https://dev.mysql.com/doc/refman/8.0/en/group-replication.html
什么是组复制
由于传统异步复制的缺陷,可能会导致主从数据不一致的问题,在主节点异常宕机时从节点可能造成数据丢失。基于这个缺陷,Mysql5.7.17推出了一个 高可用与高扩展的解决方案Mysql Group Replication(简称MGR), 将原有的gtid复制功能进行了增强,支持单主模式和多主模式。组复制在数据库层面上做到了只要集群中大多数主机可用,则服务可用,也就是说3台服务器的集群,允许其中1台宕机。

Group Replication 提供了分布式状态机复制,服务器之间具有很强的协调性。当服务器属于同一组时,它们会自动进行协调。该组可以在具有自动选主的单主模式下运行,在这种模式下,一次只有一台服务器接受更新。或者,对于更高级的用户,可以在多主模式下部署该组,其中所有服务器都可以接受更新,即使它们是并发执行的。
与传统复制相比,Group Replication有以下大幅改进:
- 传统复制的主从复制方式有一个主和不等数量的从。主节点执行的事务会异步发送给从节点,在从节点重新执行。而Group Replication采用整组写入的方式,避免了单点争用。
- Group Replication在传输数据时使用了Paxos协议。Paxos协议保证了数据传输的一致性和原子性。基于Paxos协议,Group Replication构建了一个分布式的状态复制机制,这是实现多主复制的核心技术。
- Group Replication提供了多写方案,为多活方案带来了实现的可能。
MGR 能保证数据库服务的连续可用,却无法处理如下问题:当一个组成员变为不可用时,连接到它的客户端必须被重定向或故障转移到其他组成员。此时需要使用连接器、负载均衡器、路由器或某种形式的中间件,例如 MySQL Router 8.0 。MGR 本身不提供这些工具。此时便引入了 InnoDB Cluster ,后面详述。
单主模式
组中的每个MySQL服务器实例都可以在独立的物理主机上运行,这是部署组复制的推荐方式。
在单主模式下(group_replication_single_primary_mode=ON),组中只有一个主服务器,该主服务器被设置为读写模式。组中的所有其他成员都被设置为只读模式(super_read_only=ON)。

查找primary的两种方式
# 方式1:查询performance_schema.replication_group_members的MEMBER_ROLE列
mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;
+-------------------------+-------------+
| MEMBER_HOST | MEMBER_ROLE |
+-------------------------+-------------+
| remote1.example.com | PRIMARY |
| remote2.example.com | SECONDARY |
| remote3.example.com | SECONDARY |
+-------------------------+-------------+
# 方式2:查看group_replication_primary_member变量状态
mysql> SHOW STATUS LIKE 'group_replication_primary_member';
单主模式部署示例
文档:https://dev.mysql.com/doc/refman/8.0/en/group-replication-configuring-instances.html
1) 部署三个MySQL Server实例
可以利用docker快速部署3个MySQL实例
|----------------------|-----------|------------|-----------------|
| 角色 | server_id | DB Port | 内部通信 |
| mgr-node1(primary) | 1 | 3321>3306 | mgr-node1:33061 |
| mgr-node2(Secondary) | 2 | 3322>3306 | mgr-node2:33061 |
| mgr-node3(Secondary) | 3 | 3323>3306 | mgr-node3:33061 |
# 创建组复制的网络 保证三个mysql容器之间可以通过容器名访问
docker network create --driver bridge mgr-network
mkdir -p /mysql/mgr/node1/data /mysql/mgr/node1/conf /mysql/mgr/node1/log
mkdir -p /mysql/mgr/node2/data /mysql/mgr/node2/conf /mysql/mgr/node2/log
mkdir -p /mysql/mgr/node3/data /mysql/mgr/node3/conf /mysql/mgr/node3/log
#运行mysql容器
docker run -d \
--name mgr-node1 \
--privileged=true \
--restart=always \
--network mgr-network \
-p 3321:3306 \
-v /mysql/mgr/node1/data:/var/lib/mysql \
-v /mysql/mgr/node1/conf:/etc/mysql/conf.d \
-v /mysql/mgr/node1/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1
docker run -d \
--name mgr-node2 \
--privileged=true \
--restart=always \
--network mgr-network \
-p 3322:3306 \
-v /mysql/mgr/node2/data:/var/lib/mysql \
-v /mysql/mgr/node2/conf:/etc/mysql/conf.d \
-v /mysql/mgr/node2/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1
docker run -d \
--name mgr-node3 \
--privileged=true \
--restart=always \
--network mgr-network \
-p 3323:3306 \
-v /mysql/mgr/node3/data:/var/lib/mysql \
-v /mysql/mgr/node3/conf:/etc/mysql/conf.d \
-v /mysql/mgr/node3/log:/logs \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai mysql:8.0.27 \
--lower_case_table_names=1
2)配置组复制实例
以mgr-node1配置为例,创建/mysql/mgr/node1/conf/custom.cnf,添加以下配置:
[mysql]
# 设置mysql客户端默认编码
default-character-set=utf8
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
secure-file-priv= NULL
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
#对于Group Replication,数据必须存储在InnoDB事务存储引擎中
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
#指定sever_id,三个Mysql实例需要分别改为对应的sever_id
server_id=1
# 必须开启GTID支持
gtid_mode=ON
enforce_gtid_consistency=ON
# 启用二进制日志
log-bin=mysql-bin
#组复制设置
#实例启动时会将组复制插件加载到插件列表中
plugin_load_add='group_replication.so'
# 组名三个节点必须保证一致,必须是UUID,可以使用 SELECT UUID()生成一个
group_replication_group_name="117dc7ea-b9bd-11ee-9bdb-0242ac120002"
# 插件在服务器启动时不自动启动,可以等配置好服务器之后手动启动
group_replication_start_on_boot=off
# 配置与组内其他成员通信是使用的主机名和端口,内部通讯端口,推荐使用 33061
group_replication_local_address= "mgr-node1:33061"
# 设置组成员的主机名和端口
group_replication_group_seeds= "mgr-node1:33061,mgr-node2:33061,mgr-node3:33061"
# 通常会在实例运行时配置group_replication_bootstrap_group,以确保只有一个成员实际引导组
group_replication_bootstrap_group=off
# 最大连接数
max_connections=1000
# 设置默认时区
default-time_zone='+8:00'
# 0:区分大小写
# 1:不区分大小写
lower_case_table_names=1
!includedir /etc/mysql/conf.d/
mgr-node2和mgr-node3同上,注意配置文件路径和修改server_id和group_replication_local_address

3)配置用于分布式恢复的用户凭证
mgr-node1上配置
3.1)重新启动mysql实例
docker restart mgr-node1 mgr-node2 mgr-node3
3.2)禁用二进制日志记录,以便在每个实例上分别创建复制用户
# mgr-node1为例
[root@192-168-65-185 ~]# docker exec -it mgr-node1 /bin/bash
root@0955d5f390c6:/# mysql -uroot -p123456
mysql> SET SQL_LOG_BIN=0;
3.3) 创建MySQL用户,授予所需的权限
mysql> CREATE USER fox@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO fox@'%';
GRANT CONNECTION_ADMIN ON *.* TO fox@'%';
GRANT BACKUP_ADMIN ON *.* TO fox@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO fox@'%';
FLUSH PRIVILEGES;
3.4)如果前面禁用了二进制日志,再次启用二进制日志
mysql> SET SQL_LOG_BIN=1;
3.5)创建复制用户后,必须向服务器提供用于分布式恢复的用户凭据。
-
使用CHANGE REPLICATION SOURCE TO | CHANGE MASTER TO设置的用户凭据以明文形式存储在服务器上的复制元数据存储库中。当组复制启动时,它们将被应用,包括当系统变量group_replication_start_on_boot设置为ON时自动启动。
-
在START GROUP_REPLICATION上指定的用户凭据仅保存在内存中,并通过STOP GROUP_REPLICATION语句或服务器关闭来删除。必须发出START GROUP_REPLICATION语句来再次提供凭据,因此无法使用这些凭据自动启动Group Replication。这种指定用户凭据的方法有助于保护组复制服务器免受未经授权的访问。此功能从MySQL 8.0.21开始支持。
#from MySQL 8.0.23:
mysql> CHANGE REPLICATION SOURCE TO SOURCE_USER='fox', SOURCE_PASSWORD='123456' FOR CHANNEL 'group_replication_recovery';
注意:如果不想配置ssl,可以配置参数group_replication_recovery_public_key_path=ON来在请求复制用户密钥时给公钥
#实例加入集群需要获取公钥
set global group_replication_recovery_get_public_key=on;
- 指定mgr-node1引导组(primary节点),启用组复制
为了安全地引导组,连接到mgr-node1并执行以下语句:
#mgr-node1启动组复制,并且作为primary
mysql> SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
# 查询组成员信息,mgr-node1是否是primary
mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;

为了验证后续其他节点入组情况,下面将创建一个表并向其中添加一些数据进行验证。
CREATE DATABASE IF NOT EXISTS test;
USE test;
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
INSERT INTO t1 VALUES (1, 'Fox');
5)向组中添加实例mgr-node2和mgr-node3
#添加复制用户
SET SQL_LOG_BIN=0;
CREATE USER fox@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO fox@'%';
GRANT CONNECTION_ADMIN ON *.* TO fox@'%';
GRANT BACKUP_ADMIN ON *.* TO fox@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO fox@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
CHANGE REPLICATION SOURCE TO SOURCE_USER='fox', SOURCE_PASSWORD='123456' FOR CHANNEL 'group_replication_recovery';
set global group_replication_recovery_get_public_key=on;
# mgr-node2和mgr-node3启用主复制
mysql> START GROUP_REPLICATION;
# 查询组成员信息
mysql> SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;

确认数据同步情况
SELECT * FROM test.user;

如果数据没有正常同步,可以通过docker log -f mgr-node2排查问题
多主模式
文档:https://dev.mysql.com/doc/refman/8.0/en/group-replication-multi-primary-mode.html
在多主模式下(group_replication_single_primary_mode=OFF),没有成员具有特殊的角色。任何与其他组成员兼容的成员在加入组时都被设置为读写模式,并且可以处理写事务,即使它们是并发发布的。
如果一个成员停止接受写事务,例如,在服务器意外退出的情况下,连接到它的客户端可以被重定向或故障转移到处于读写模式的任何其他成员。Group Replication本身不处理客户端故障转移,因此需要使用中间件框架(如MySQL Router 8.0)、代理、连接器或应用程序本身来安排。

多主模式部署示例
在前面单主模式基础上改为多主模式
1)三个节点都关闭单主模式
三个节点都执行如下操作
# 停止组复制
stop GROUP_REPLICATION;
# 关闭单主模式
set global group_replication_single_primary_mode=off;
# 开启多主一致性检查
set global group_replication_enforce_update_everywhere_checks=ON;

2)选择mgr-node1引导组复制
# 开启组复制引导
set global group_replication_bootstrap_group=on;
# 开启组复制
start group_replication;
# 关闭组复制引导
set global group_replication_bootstrap_group=off;

3)mgr-node2和mgr-node3开启组复制
# 开启组复制
start group_replication;
# 查看到添加到组复制集群的服务器信息
select * from performance_schema.replication_group_members;

4) 测试
-
mgr-node1插入数据,看mgr-node2是否可以查到
#mgr-node1
INSERT INTO test.t1 VALUES (2, 'aaa');
#mgr-node2
select * from test.t1;
