MySQL Group Replication(MGR)-5.7后期之秀-单(荐)/多主

支持多主模式,但官方推荐单主模式:

● 多主模式下,客户端可以随机向MySQL节点写入数据

● 单主模式下,MGR集群会选出primary节点负责写请求,primary节点与其它节点都可以进行读请求处理

优点:

● 基本无延迟,延迟比异步的小很多

● 支持多写模式,但是目前还不是很成熟

● 数据的强一致性,可以保证数据事务不丢失

缺点:

● 仅支持innodb

● 只能用在GTID模式下,且日志格式为row格式

适用的业务场景:

● 希望对对写服务提供高可用,又不想安装第三方软件

● 数据强一致的场景

搭建环境

3台CentOS7系统虚拟机,2G内存2核CPU,10G硬盘

单主MGR拓扑

系统配置

服务节点

CentOS7.9(2核2G)

192.168.0.79-Master服务器(hostname:n0)

CentOS7.9(2核2G)

192.168.0.78-Master服务器(hostname:n1)

CentOS7.9(2核2G)

192.168.0.77-Master服务器(hostname:n2)

系统环境配置

每一台主机都需要进行下面的配置

设置静态IP

# 根据不同的主机修改下面的值
cd /etc/sysconfig/network-scripts

sed -i 's/dhcp/static/g' ifcfg-ens33
sudo cat >> ifcfg-ens33 <<-'EOF'
IPADDR=192.168.0.79
GATEWAY=192.168.0.1
NETMASK=255.255.255.0
DNS1=114.114.114.114
DNS2=8.8.8.8
EOF

service network restart

修改主机名

#注意配置的主机是那一套 n0 192.168.0.79,n1 192.168.0.78,192.168.0.77 n2
hostnamectl set-hostname n0

配置Hosts

sudo cat > /etc/hosts <<-'EOF'
192.168.0.79 n0
192.168.0.78 n1
192.168.0.77 n2
EOF

更换YUM镜像

# CentOS 7仓库已经被归档,现在使用默认的yum镜像源会直接报错
# 备份默认的 YUM
mv /etc/yum.repos.d /etc/yum.repos.d.backup
 
# 设置新的 YUM 目录
mkdir /etc/yum.repos.d
 
# 下载阿里 YUM 配置到该目录中
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
 
# 重建缓存
yum clean all && yum makecache

关闭selinux

# 非生产环境可以这样做
sed -i 's/enforcing/disabled/' /etc/selinux/config 
setenforce 0

防火墙设置

firewall-cmd --zone=public --add-port=3306/tcp --permanent
firewall-cmd --zone=public --add-port=24901/tcp --permanent
firewall-cmd --reload

MySQL安装与配置

MySQL 安装

# 配置镜像源
cd /home/
wget --no-check-certificate https://repo.mysql.com/mysql80-community-release-el7-5.noarch.rpm
yum localinstall -y mysql80-community-release-el7-5.noarch.rpm

# 安装MySQL,这里我并没有指定版本,文档中安装的是8.0.28,我这里安装的是8.0.41这个版本
# 最好指定版本,不要用太新的版本,可能会有参数不一样或Spring Boot驱动版本或yaml参数问题
# 不添加--nogpgcheck会出现GPG密钥报错
yum install -y mysql-community-server --nogpgcheck

修改配置文件my.cnf

Master节点配置(n0)

sudo cat >> /etc/my.cnf <<-'EOF'
#使用mysql_native_password密码策略,防止navicat连不上mysql8
default_authentication_plugin=mysql_native_password
#设置MySQL插件所在目录,因为MGR基于插件,所以必须设置插件路径
plugin_dir=/usr/lib64/mysql/plugin
#服务器编号,Master=1
server_id=1
#开启binlog的GTID模式
gtid_mode=ON
#开启后MySQL只允许能够保障事务安全,并且能够被日志记录的SQL语句被执行
enforce_gtid_consistency=ON
#关闭binlog校验
binlog_checksum=NONE
#定义用于事务期间哈希写入提取的算法,组复制模式下必须设置为 XXHASH64。
transaction_write_set_extraction=XXHASH64
#确定组复制恢复时是否应该应用 SSL,通常设置为“开”,但默认设置为“关”。
loose-group_replication_recovery_use_ssl=ON
#该服务器的实例所在复制组的名称,必须是有效的 UUID,所有节点必须相同。
loose-group_replication_group_name="bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee"
#确定服务器是否应该在服务器启动期间启动组复制。
loose-group_replication_start_on_boot=OFF
#大坑2:
#为复制组中其他的成员提供的网络地址,指定为“主机:端口”的格式化字符串。
#很多人想当然认为端口应该是3306,起始不然,MGR需要开启新端口24901同步交换
#所以这里不要写错,同时,前面我们配置了hosts文件做了主机名与IP的映射,这里直接写主机名即可
loose-group_replication_local_address="n0:24901"
#用于建立新成员到组的连接组成员列表。
#这个列表指定为由分隔号间隔的组成员网络地址列表,类似 host1:port1、host2:port2 的格式。
#同样采用n0~n2的主机名替代
loose-group_replication_group_seeds="n0:24901,n1:24901,n2:24901"
#配置此服务器为引导组,这个选项必须仅在一台服务器上设置,
#并且仅当第一次启动组或者重新启动整个组时。成功引导组启动后,将此选项设置为关闭。
loose-group_replication_bootstrap_group=OFF
EOF

Slave节点配置(n1)

sudo cat >> /etc/my.cnf <<-'EOF'
default_authentication_plugin=mysql_native_password
plugin_dir=/usr/lib64/mysql/plugin
#设置唯一的服务器编号
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
#这个参数决定primary节点到secondary节点的请求是否为基于 RSA 密钥对的密码交换所需的公钥
loose-group_replication_recovery_get_public_key=ON
loose-group_replication_recovery_use_ssl=ON
loose-group_replication_group_name="bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee"
loose-group_replication_start_on_boot=OFF
#设置本机地址n1:24901
loose-group_replication_local_address="n1:24901"
loose-group_replication_group_seeds="n0:24901,n1:24901,n2:24901"
loose-group_replication_bootstrap_group=OFF
EOF

Slave节点配置(n2)

sudo cat >> /etc/my.cnf <<-'EOF'
default_authentication_plugin=mysql_native_password
plugin_dir=/usr/lib64/mysql/plugin
#设置唯一的服务器编号
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
#这个参数决定primary节点到secondary节点的请求是否为基于 RSA 密钥对的密码交换所需的公钥
loose-group_replication_recovery_get_public_key=ON
loose-group_replication_recovery_use_ssl=ON
loose-group_replication_group_name="bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee"
loose-group_replication_start_on_boot=OFF
#设置本机地址n2:24901
loose-group_replication_local_address="n2:24901"
loose-group_replication_group_seeds="n0:24901,n1:24901,n2:24901"
loose-group_replication_bootstrap_group=OFF
EOF

启动MySQL并配置账号

# 启动MySQl服务器
systemctl start mysqld

# 获取初始密码
grep 'temporary password' /var/log/mysqld.log
# 运行结果:
2025-03-07T10:58:14.577426Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: ucl*RZz%y8lj

# 登录MySQL
mysql -uroot -pucl*RZz%y8lj

# 修改root密码为asAS123456!
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'asAS123456!';

# 创建rpl_user账户,此账户用于实现主从数据同步
CREATE USER rpl_user@'%' IDENTIFIED BY 'asAS123456!';
# 赋予主从同步权限
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';

#创建一个远程连接用户,这个用户用在Navcate、JDBC登录的时候,直接用root登录不好
create user 'remote'@'%' identified with mysql_native_password by 'asAS123456!';
#为remote用户赋予所有数据库资源的访问权限,熟悉grant的小伙伴可以自己调整
grant all privileges on *.* to remote@'%';

# 删除已产生的Binlog
# 前面执行了很多高权限的命令,如果不清空,在RelayLog重放时,这些命令会无法正确执行,导导致从属服务器卡死在"RECEVERING"状态
RESET MASTER;

安装MGR插件

在登录MySQL的情况下

# 安装MGR插件
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
# 查看结果,出现PLUGIN_STATUS=ACTIVE,说明安装成功了
SELECT * FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'group_replication' \G

进行组网数据同步

Master节点配置(n0)

# 我们在 primary.cnf 配置文件中把 group_replication_bootstrap_group 参数设置成 OFF,在 primary 服务器启动时并不会直接启动复制组,通过下面的命令动态的开启复制组是我们的集群更安全。
# 第一条命令是标记当前节点作为集群的主服务器
#第二条命令是创建组
#第三条命令关闭了,是因为已经在创建的组中成为了主服务器,这个操作是一次性的,为了防止其他错误就将它关闭了
# 在Master(n0)节点上运行
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

Slave节点配置(n1,n2)

#指定主从账户与指定通信频道
CHANGE MASTER TO MASTER_USER="rpl_user", MASTER_PASSWORD="asAS123456!" FOR CHANNEL 'group_replication_recovery';
#开启组网数据同步
START GROUP_REPLICATION;
当两个从节点都运行完毕后,运行下面结果进行验证。
SELECT * FROM performance_schema.replication_group_members;

ProxySQL代理安装

环境配置

设置静态IP

在新的服务器上需要设置,我这里依旧在192.168.0.79(n0)上运行所以不需要重新配置

# 根据不同的主机修改下面的值
cd /etc/sysconfig/network-scripts

sed -i 's/dhcp/static/g' ifcfg-ens33
sudo cat >> ifcfg-ens33 <<-'EOF'
IPADDR=192.168.0.69
GATEWAY=192.168.0.1
NETMASK=255.255.255.0
DNS1=114.114.114.114
DNS2=8.8.8.8
EOF

service network restart

防火墙设置

# 开放防火墙,6032是ProxySQL的管理端口,6033是ProxySQL的客户端接入端口
firewall-cmd --zone=public --add-port=6032/tcp --permanent
firewall-cmd --zone=public --add-port=6033/tcp --permanent
firewall-cmd --reload

安装MySQL

# 配置镜像源
cd /home/
wget --no-check-certificate https://repo.mysql.com/mysql80-community-release-el7-5.noarch.rpm
yum localinstall -y mysql80-community-release-el7-5.noarch.rpm

# 安装MySQL,这里我并没有指定版本,安装的是8.0.41这个版本
# 不添加--nogpgcheck会出现GPG密钥报错
yum install -y mysql-community-client --nogpgcheck

安装ProxySQL

cd /home/
wget --no-check-certificate https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/mgr/download/proxysql-2.2.0-1-centos7.x86_64.rpm
yum localinstall -y proxysql-2.2.0-1-centos7.x86_64.rpm --nogpgcheck

# 启动SQLProxy服务
service proxysql start

登入ProxySQL Admin,进行设置

# ProxySQL Admin,它是一个伪装为MySQL的ProxySQL服务端
mysql -u admin -padmin -h 127.0.0.1 -P6032 --prompt='Admin> '

insert into mysql_servers(hostgroup_id,hostname,port) values (10,'n0',3306);
insert into mysql_servers(hostgroup_id,hostname,port) values (10,'n1',3306);
insert into mysql_servers(hostgroup_id,hostname,port) values (10,'n2',3306);
load mysql servers to runtime;
save mysql servers to disk;

# 查看配置结果
select * from mysql_servers;

在主节点(n0)进行配置

下面需要在主节点192.168.0.79(no)的mysql数据库中运行这些SQL,这样可以同步到其他节点

创建监控账号与程序账号给ProxySQL使用

# MySQL降低密码强度
set global validate_password.policy=0;
set global validate_password.length=4;
CREATE USER 'monitor'@'%' IDENTIFIED BY "monitor@1025";
CREATE USER 'proxysql'@'%' IDENTIFIED BY "proxysql@1025";
GRANT ALL PRIVILEGES ON *.* TO 'monitor'@'%' ;
GRANT ALL PRIVILEGES ON *.* TO 'proxysql'@'%' ;
FLUSH PRIVILEGES;

创建gr_member_routing_candidate_status视图,这是主从故障切换关键所在

# 下面的SQL最好分开执行
# 切换
use sys;

# 1
CREATE FUNCTION my_id() RETURNS TEXT(36) DETERMINISTIC NO SQL RETURN (SELECT @@global.server_uuid as my_id);

# 2
CREATE FUNCTION gr_member_in_primary_partition()
RETURNS VARCHAR(3)
DETERMINISTIC
BEGIN
RETURN (SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM
performance_schema.replication_group_members WHERE MEMBER_STATE NOT IN ('ONLINE', 'RECOVERING')) >=
((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0),
'YES', 'NO' ) FROM performance_schema.replication_group_members JOIN
performance_schema.replication_group_member_stats USING(member_id) where member_id=my_id());
END

# 3
CREATE VIEW gr_member_routing_candidate_status AS SELECT
sys.gr_member_in_primary_partition() as viable_candidate,
IF( (SELECT (SELECT GROUP_CONCAT(variable_value) FROM
performance_schema.global_variables WHERE variable_name IN ('read_only',
'super_read_only')) != 'OFF,OFF'), 'YES', 'NO') as read_only,
Count_Transactions_Remote_In_Applier_Queue as transactions_behind, Count_Transactions_in_queue as 'transactions_to_cert'
from performance_schema.replication_group_member_stats where member_id=my_id();

回到 ProxySQL 进行配置

# 注意,此处又回到了ProxySQL上,不是在主节点的MySQL上执行
# 如果之前你退出了,使用下面的命令登入
mysql -u admin -padmin -h 127.0.0.1 -P6032 --prompt='Admin> '

# 在ProxySQL中设置监控账号与程序账号
set mysql-monitor_username='monitor';
set mysql-monitor_password='monitor@1025';
insert into mysql_users(username,password,active,default_hostgroup,transaction_persistent)
values('proxysql','proxysql@1025',1,10,1);

# 读写分组
# 主负责写、从负责读,当MGR主库切换后,代理自动识别主从。
# ProxySQL代理每一个后端MGR集群时,都必须为这个MGR定义写组10、备写组20、读组30、离线组40,
# 注意:max_transactions_behind 是设置延迟大小,可以给大点,建议自己去开个并行复制。
insert into mysql_group_replication_hostgroups (writer_hostgroup,backup_writer_hostgroup,reader_hostgroup,
offline_hostgroup,active,max_writers,writer_is_also_reader,max_transactions_behind) values (10,20,30,40,1,1,0,100);

# 启用规则
load mysql servers to runtime;
save mysql servers to disk;
load mysql users to runtime;
save mysql users to disk;
load mysql variables to runtime;
save mysql variables to disk;

状态检查

status:写组10、备写组20、读组30、离线组40

select hostgroup_id, hostname, port,status from runtime_mysql_servers;

错误检查

通过下面命令查看后台执行日志,如有错误在error里面会显示

select hostname,port,viable_candidate,read_only,transactions_behind,error from mysql_server_group_replication_log order by time_start_us desc;

配置读写分离规则

insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)
VALUES (1,1,'^SELECT.*FOR UPDATE$',10,1),
(2,1,'^SELECT',30,1);
load mysql query rules to runtime;
save mysql query rules to disk;

大坑一(Unknown system variable 'query_cache_size' )

Spring Boot 调用SQL时报错Unknown system variable 'query_cache_size' 。

可能是pom.xml的mysql-connector-java太老,MySQL版本是8.0.x以上,那么驱动版本也得是8.0.以上

如果你绕过ProxySQL代理,Spring Boot直接通过主库3306端口,发现可以正常执行SQL,那么就是proxysql的问题

proxysql目前安装最新的,内置的mysql版本也才是5.5.30的,所以如果你的数据库是8.0及以上的。

一定要记得修改proxysql内置mysql的版本号 登上proxysql的应用端执行以下命令

# 8.0.xu以上就行,我这里是修改为41,因为我mysql主从节点都是41
update global_variables set variable_value="8.0.41 (ProxySQL)" where variable_name='mysql-server_version';

load mysql variables to run;
save mysql variables to disk;

我现在MySQL版本是8.0.41

POM文件的mysql-connector-java点进去查看的是8.0.27

<!--mysql jdbc-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

yaml

spring:
  datasource:
    url: jdbc:mysql://192.168.0.79:6033/workdb?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
    username: proxysql
    password: proxysql@1025
    driver-class-name: com.mysql.cj.jdbc.Driver

服务器重启后重新组网

所有服务proxySQL和所有节点全部重启后,需要重新组网,proxySQL正常启动即可

# 登录proxySQL
mysql -u admin -padmin -h 127.0.0.1 -P6032 --prompt='Admin> '
# 查询节点状态
select hostgroup_id, hostname, port,status from runtime_mysql_servers;

所有节点全部都重启了,需要先在主节点上运行命令,再到从节点运行命令。

# 主节点运行
mysql -uroot -p...
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

# 从节点运行
mysql -uroot -p...
START GROUP_REPLICATION;

如果是某个节点掉了,无论主从都执行以下命令即可

mysql -uroot -p...
START GROUP_REPLICATION;