目录
1. MGR简介
1. 为什么是MGR
MGR是MySQL Group Replication的缩写,即MySQL组复制。
在以往,我们一般是利用MySQL的主从复制或半同步复制来提供高可用解决方案,但这存在以下几个比较严重的问题:
主从复制间容易发生复制延迟,尤其是在5.6以前的版本,以及当数据库实例中存在没有显式主键表时,很容易发生。
主从复制节点间的数据一致性无法自行实现最终一致性。
当主节点发生故障时,如果有多个从节点,无法自动从中选择合适的节点作为新的主节点。
如果采用(增强)半同步复制,那么当有个从节点因为负载较高、网络延迟或其他意外因素使得事务无法及时确认时,也会反过来影响主节点的事务提交。
因为上述几个明显的缺点,因此MySQL推出了全新的高可用解决方案 -- 组复制,这是本系列文章要着重介绍的新特性。
MGR是MySQL 5.7.17开始引入的,但随着5.7版本逐渐退出历史舞台(MySQL 5.7已于2020年10月起不再做大的功能更新,只有修修补补以及针对安全更新),更多MGR相关特性都只在MySQL 8.0上才有。
因此,如果线上还有基于MySQL 5.7版本的MGR环境的话,建议尽快升级、迁移到MySQL 8.0版本。进一步提醒,推荐MySQL 8.0.22及之后的版本,整体会更稳定可靠,也有些很不错的新功能(不只是MGR方面的)。
2. MGR技术概要
MGR具备以下几个特点:
基于shared-nothing模式,所有节点都有一份完整数据,发生故障时可以直接切换。
MGR提供了数据一致性保障,默认是最终一致性,可根据业务特征需要自行调整一致性级别。
支持在线添加、删除节点,节点管理更方便。
支持故障自动检测及自动切换,发生故障时能自动切换到新的主节点,再配合MySQL Router中间件,应用层无需干预或调整。
支持单节点、多节点写入两种模式,可根据架构或业务需要选择哪种方案,不过强烈建议选用单主模式。
MGR可以选择单主(Single-Primary)模式
如上图所示,一开始S1节点是Primary角色,提供读写服务。当它发生故障时,剩下的S2-S5节点会再投票选举出S2作为新的Primary角色提供读写服务,而S1节点在达到一定超时阈值后,就会被踢出。
亦可选择多主(Multi-Primary)模式(再次强烈建议选用单主模式)
如上图所示,一开始S1-S5所有节点都是Primary角色,都可以提供读写服务,任何一个节点发生故障时,只需要把指向这个节点的流量切换下就行。
上述两种架构模式下,应用端通过MySQL Router连接后端在MGR服务,当后端节点发生切换时,Router会自动感知,对应用端来说几乎是透明的,影响很小,架构上也更灵活。
3. MGR技术架构
首先来个MGR的技术架构图:
MGR是以Plugin方式嵌入MySQL,部署更灵活方便。
事务从Server层通过钩子(hook)进入MGR API接口层,再分发到各组件层,在组件层完成事务Capture/Apply/Recover,通过复制协议层(Replication Protocol Logics)传输事务,最后经由GCS协调事务在各节点的最终一致性。
MGR节点间由组通信系统(GCS)提供支持,它提供了故障检测机制、组成员角色管理,以及安全且有序的消息传递,这些机制可确保在各节点间一致地复制数据。这项技术的核心是Paxos算法的实现,在MySQL里称之为XCom,由它充当MGR的通信引擎。
对于要提交的事务,组中的多数派节点必须就全局事务序列中给定的事务顺序达成一致。各节点做出决定提交或中止事务的选择,但所有节点都要做出相同的决定。如果发生网络分区,导致节点间无法达成一致决定,则在网络恢复前,MGR无法工作。
MGR支持单主和多主两种模式,在单主模式下,各节点会自动选定主节点,只有该主节点能同时读写,而其他(从)节点只能只读。在多主模式下,所有节点都可以进行读写。
相对于MariaDB Galera Cluster(以及基于此技术的Percona XtraDB Cluster,下面为了书写方便,都统称为PXC),个人认为MGR具备以下几个优势:
PXC的消息广播机制是在节点间循环的,需要所有节点都确认消息,因此只要有一个节点故障,则会导致整个PXC都发生故障。而MGR则是多数派投票模式,个别少数派节点故障时,一般不影响整体的可用性。这也是PXC存在的最大问题。
PXC的节点间数据传输除了binlog,还有个gcache,这相当于是给MySQL又增加两个黑盒子。而MGR则都是基于原生binlog的,没有新增黑盒子,运行起来更可靠,需要排障时也更方便。
发生网络分区时,整个PXC集群都不可用。而MGR则至少还能提供只读服务。
PXC的流控机制影响更大,一旦触发流控,所有节点都受到影响。而MGR触发流控后,只会影响本地节点,不影响远程节点。当然了,MySQL的流控做的也比较粗糙,在GreatSQL中进一步完善和优化。
执行DDL期间,整个PXC集群都不可同时执行DML,也就是说不支持Online DDL。而MGR是支持的,这也是很大的优势。
相对于传统主从复制(Replication),我认为MGR的优势有以下几点:
主从复制非常容易产生复制延迟,尤其是当表中没有显式主键时。而在MGR里,要求表一定要有主键(或是可用作聚集索引的非空唯一索引),避免了这个问题。
半同步复制中,一旦slave因为锁或其他原因响应慢的话,也会导致master事务被阻塞。MGR是采用多数派确认机制,个别节点响应慢对Primary节点的影响没那么大(不要选用AFTER模式)。
主从复制没有类似MGR那样提供事务数据的一致性保证。MGR自带了事务数据一致性保障机制。
以上是我根据MySQL、MariaDB、Percona的资料整理得到的观点,不一定准确和全面,有不完善的地方还请留言指正。
4. 小结
- 本节主要介绍了什么是MGR,MGR的技术架构概要,以及MGR相对PXC的几个技术优势。
- MGR是MySQL四部战略走的关键一环,依靠MGR和MySQL Shell、MySQL Router已实现了读节点扩展,以及写节点扩展(MGR多主模式),下一步预计实现sharding,让我们拭目以待。
2. 组复制技术架构
1. 传统主从复制技术架构
传统主从复制的方式是在master节点上执行数据更新事务,而后记录这些事务到binlog中,再将binlog发送到slave节点转储成relay log,在slave节点上再有单独的线程读取这些relay log然后重新执行或应用这些事务,它是shared-nothing的,每个节点都有一份完整的数据副本,其技术流程图如下所示:
MySQL还提供了半同步复制,这是在传统主从复制的基础上增加了一个同步的步骤,master节点上提交事务前,要先等到slave节点确认收到事务信息才可以(所以前文才说当slave节点响应慢时会影响master节点的事务提交),其技术流程图如下所示:
2. MGR组复制技术架构
MGR也是shared-nothing的,每个节点都有一份完整的数据副本,节点间通过GCS(Group Communication System)进行交互。GCS层提供了节点间的全局消息及其有序性的保证。
MGR可以做到在任何节点、任何时间都能执行读写事务(不含只读事务),不过读写事务要被整个复制组确认后才能提交。如果是只读事务则没有这个限制,任何节点都可以发起及提交。
当读写事务准备提交前,它会向复制组发出一个原子广播,内容包括:该事务修改的数据,及其所对应的writeset。复制组中所有节点要么接收该事务,要么都不接收。如果组中所有节点都接收该事务消息,那么它们都会按照与之前发送事务的相同顺序收到该广播消息。因此,所有组成员都以相同的顺序接收事务的写集,并为事务建立全局顺序。
在多个节点上并行执行的事务是可能产生冲突的,这时候就需要对比判断两个并行事务的writeset来确认,这个过程称为事务认证,也叫做冲突检测。事务冲突检测是行级别的,也就是说两个并行的事务更新同一行时,则视为产生冲突。这时的做法是全局顺序在前面的事务可以成功,所有节点都提交该事务。而全局顺序在后面的事务会失败回滚,各节点会删除该事务。这实际上是个分布式的谁先提交谁先赢得事务的规则。建议:如果经常发生节点间的事务冲突,那最好将这些事务放在同一个节点上执行,这样它们在本地事务并发控制协调下可能都可以提交成功,而不至于由于MGR的冲突检测而导致某个事务总是被回滚。
对于正在应用或外化的事务,MGR允许它们不一定按照原有顺序执行,只要不破坏事务的一致性和有效性即可。MGR默认要求是最终一致性,也就是说当所有事务都应用完毕后,所有节点的数据是一致的。当流量巨大时,事务可能会被外化而导致顺序轻微不一致。例如在多主模式下,一个本地事务在通过认证后会被立即外化,尽管此时可能还有个有这更早全局顺序的远程事务还没被应用,只要MGR的认证线程认为这个事务不会产生冲突即可。在单主模式下,在Primary节点上的本地并发事务,在不产生冲突的情况下,其提交和外化的顺序可能和该事物的全局事务顺序有轻微不一致。在Secondary节点上,由于没有写事务,因此它们的事务顺序和全局事务顺序是一致的。
下图描述了MGR的组复制协议,可以看到和传统主从复制(及半同步复制)的一些差异。为了简单起见,图中少了共识算法和Paxos相关的信息:
3. MGR的单主和多主模式
MGR支持单主或多主两种模式。
在启动时,通过设置选项 group_replication_single_primary_mode
来决定使用哪种模式,各节点中该值的设置要求一致。设置为 ON 时表示采用 单主模式,当设置为 OFF 时表示采用 多主模式。
在运行过程中,不能在线修改 group_replication_single_primary_mode
的值,但是从MySQL 8.0.13开始,可以通过调用 group_replication_switch_to_single_primary_mode()
和 group_replication_switch_to_multi_primary_mode()
这两个udf在线修改运行模式,或者通过MySQL Shell修改。
在 单主模式 下,有且只有一个(Primary)节点可以写入数据,其余(Secondary)节点都只能读数据。而在 多主模式 下,可以在任意节点上同时读写数据。
MGR最多只能支持9个节点,无论单主还是多主模式。
4. 节点管理
MGR由一组节点构成,每个节点都有唯一的名字,以 UUID 的格式表现。节点可以动态加入或离开(也可能是被动被驱逐)MGR。
MGR的组成员服务用于维护定义各活跃节点的信息,这些活跃节点信息也称之为组视图(view)。各节点的组视图是一致的,这表示在给定时刻组中有哪些活跃成员。
MGR各节点除了在事务提交时要保持一致外,也包括组视图发生变化时也要达成一致。当有新节点加入,或现有节点离开时,都会触发新的组视图变更。
当有节点主动离开集群时,它会触发集群自动重配置,剩下的节点会就新的组视图达成一致。但若节点是因为网络异常或宕机等原因意外离开集群时,则无法触发自动重配置,这时候集群故障检测机制会在该节点离开一段时间后识别到这个状态,并发出重配置组视图的提议。重配置组视图需要得到多数派成员的同意才行,当无法形成一致时,就无法实现自动重配置,需要人工介入处理。无法形成一致意见可能的原因有,剩下的节点数没达到总结点数的一半以上,也就是无法形成多数派。
在节点被确认故障之前,或在重新配置组以删除该故障节点前,允许该节点短暂离线,然后尝试重新加入集群。在这种情况下,该节点可能会丢失它以前的状态(事务数据),如果此时其他节点向它发送了包含崩溃前的消息,则这就可能会导致数据不一致等问题。
为了解决这个问题,从MySQL 5.7.22开始,MGR会检查具有相同地址+端口的节点再次以新身份加入集群的情况,确认当前是否还有其旧身份存在。这时候其新身份不能加入,直到旧身份能从集群中删掉。
注意:
选项
group_replication_member_expel_timeout
的作用是设置一个等待期,使得节点在被正式驱逐前有更多时间尝试重新加回集群,也就是说处于被怀疑状态的节点,在超时之前还可尝试重新加入集群,再次作为活跃节点。当节点超过group_replication_member_expel_timeout
阈值并被从集群中驱逐时,或节点执行STOP GROUP_REPLICATION
退出集群,或因节点宕机等情况下,该节点必须以新身份重新加入集群。
5. 故障检测
MGR自带故障检测机制,它能发现并报告哪个节点处于静默状态,达到一定条件后会认为这个节点已死。它是个分布式的故障检测服务,提供了哪个节点处于(被怀疑)已死状态的信息。
当一个节点静默(不主动发信息,也不回复其他节点的信息)时,可能会触发被怀疑。当节点A在给定时间内还没有收到节点B的消息时,则发生消息超时并引发怀疑。在这之后,集群内其他成员如果一致同意(多数派达成一致)对该节点的怀疑是确定的话,则会判定该节点发生了故障。
如果某个节点因为网络故障和其他节点断开连接了,那么它可能也会怀疑其他节点发生了故障。但由于它不能形成多数派决议,因此这个怀疑是无效的,此时该节点无法执行任何读写事务,最多只能执行只读事务。
当网络不稳定时,随意两个节点间可能频繁断开和重连,理论上说可能会导致所有节点都会标记为驱逐,集群会退出并需要重建。为了避免这种情况,从MySQL 8.0.20开始,GCS会跟踪标记为驱逐的节点,并决定某个可疑节点是否还留在多数派节点中,这使得集群中至少有一个节点而不会退出。当被驱逐节点正式被从集群中移出时,GCS会删掉起被标记为驱逐的记录,使得它后面还能重新加回。
6.容错机制
MGR是基于分布式的Paxos算法实现,因此要求有多数派节点存活以保证投票。这就决定了在不影响系统整体可用性前提下,可容忍发生故障的节点数量。假设总节点数是n,可容忍发生故障的节点数是f,则它们的关系是:n = 2*f + 1。简言之,容忍发生故障的节点数,不高于总节点数的一半。
下表展示了不同节点数的对应关系:
总节点数 | 多数派节点数 | 最大容忍故障节点数 |
---|---|---|
1 | 1 | 0 |
2 | 2 | 0 |
3 | 2 | 1 |
4 | 3 | 1 |
5 | 3 | 2 |
6 | 4 | 2 |
7 | 4 | 3 |
8 | 5 | 3 |
9 | 5 | 4 |
3. 安装部署MGR集群
本文介绍如何利用GreatSQL 8.0.25构建一个三节点的MGR集群。
1. 安装准备
准备好下面三台服务器:
IP | 端口 | 角色 |
---|---|---|
192.168.150.21 | 3306 | mgr1 |
192.168.150.22 | 3306 | mgr2 |
192.168.150.23 | 3306 | mgr3 |
配置hosts解析
# cat >> /etc/hosts << EOF
192.168.150.21 mgr1
192.168.150.22 mgr2
192.168.150.23 mgr3
EOF
安装MySQL8
# yum install mysql-server -y
2. 启动MySQL Server
首先修改 /etc/my.cnf.d/mysql-server.cnf 配置文件,增加:
#/etc/my.cnf*.d/mysql-server.cnf
[mysqld]
...
#开启GTID,必须开启
gtid_mode = ON
#强制GTID的一致性
enforce_gtid_consistency = ON
#binlog格式,MGR要求必须是ROW,不过就算不是MGR,也最好用
binlog_format = row
#server-id必须是唯一的
server-id = 1
#MGR使用乐观锁,所以官网建议隔离级别是RC,减少锁粒度
transaction_isolation = READ-COMMITTED
#因为集群会在故障恢复时互相检查binlog的数据,
#所以需要记录下集群内其他服务器发过来已经执行过的binlog,按GTID来区分是否执行过.
log-slave-updates = 1
#binlog校验规则,5.6之后的高版本是CRC32,低版本都是NONE,但是MGR要求使用NONE
binlog_checksum = NONE
#基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错
master_info_repository = TABLE
#同上配套
relay_log_info_repository = TABLE
#组复制设置#记录事务的算法,官网建议设置该参数使用 XXHASH64 算法
transaction_write_set_extraction = XXHASH64
#相当于此GROUP的名字,是UUID值,不能和集群内其他GTID值的UUID混用,可用uuidgen来生成一个新的,
#主要是用来区分整个内网里边的各个不同的GROUP,而且也是这个group内的GTID值的UUID
loose-group_replication_group_name = '5dbabbe6-8050-49a0-9131-1de449167446'
#IP地址白名单,默认只添加127.0.0.1,不会允许来自外部主机的连接,按需安全设置
loose-group_replication_ip_whitelist = '127.0.0.1/8,192.168.6.0/24'
#是否随服务器启动而自动启动组复制,不建议直接启动,怕故障恢复时有扰乱数据准确性的特殊情况
loose-group_replication_start_on_boot = OFF
#本地MGR的IP地址和端口,host:port,是MGR的端口,不是数据库的端口
loose-group_replication_local_address = '192.168.6.151:33081'
#需要接受本MGR实例控制的服务器IP地址和端口,是MGR的端口,不是数据库的端口
loose-group_replication_group_seeds = '192.168.6.151:33081,192.168.6.152:33081,192.168.6.153:33081'
#开启引导模式,添加组成员,用于第一次搭建MGR或重建MGR的时候使用,只需要在集群内的其中一台开启,
loose-group_replication_bootstrap_group = OFF
#是否启动单主模式,如果启动,则本实例是主库,提供读写,其他实例仅提供读,如果为off就是多主模式了
loose-group_replication_single_primary_mode = ON
#多主模式下,强制检查每一个实例是否允许该操作,如果不是多主,可以关闭
loose-group_replication_enforce_update_everywhere_checks = on
将mgr1的文件发送到mgr2和mgr3, 修改server-id和loose-group_replication_local_address的相关值
# scp /etc/my.cnf.d/mysql-server.cnf mgr2:/etc/my.cnf.d/
# scp /etc/my.cnf.d/mysql-server.cnf mgr2:/etc/my.cnf.d/
启动服务
# systemctl restart mysqld
3.安装插件
利用这份配置文件,重启MySQL Server,之后就应该能看到已经成功加载 group_replicaiton
插件了:
[root@mgr1 ~]# mysql -e "show plugins;" | grep "group_replication"
如果没正确加载,也可以登入MySQL Server自行手动加载这个plugin:
[root@mgr1 ~]# mysql -e "install plugin group_replication soname 'group_replication.so'"
[root@mgr1 ~]# mysql -e "show plugins;" | grep "group_replication"
group_replication ACTIVE GROUP REPLICATION group_replication.so GPL
4.配置账号
接下来,创建MGR服务专用账户,并准备配置MGR服务通道:
#每个节点都要单独创建用户,因此这个操作没必要记录binlog并复制到其他节点
mysql> set session sql_log_bin=0;
mysql> create user repl@'%' identified with mysql_native_password by 'repl';
mysql> GRANT BACKUP_ADMIN, REPLICATION SLAVE ON *.* TO `repl`@`%`;
#创建完用户后继续启用binlog记录
mysql> set session sql_log_bin=1;
#配置MGR服务通道
#通道名字 group_replication_recovery 是固定的,不能修改
mysql> CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='repl' FOR CHANNEL 'group_replication_recovery';
5.启动MGR单主模式
在mgr1节点,启动引导,进入mysql服务端
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;
SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3460f111-9314-11ee-a532-000c295df6cf | mgr1 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
1 row in set (0.01 sec)
在mgr2\mgr3节点,进入mysql服务端
START GROUP_REPLICATION;
SELECT * FROM performance_schema.replication_group_members;
再次查看MGR节点状态:
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3460f111-9314-11ee-a532-000c295df6cf | mgr1 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
| group_replication_applier | 52854f96-9314-11ee-8821-000c29ced34f | mgr2 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
| group_replication_applier | 70fa199f-9314-11ee-a8eb-000c29f957d2 | mgr3 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.00 sec)
看到上面这个集群共有3个节点处于ONLINE状态,其中 192.168.150.21 是 PRIMARY 节点,其余两个都是 SECONDARY 节点,也就是说当前这个集群采用 单主 模式。如果采用多主模式,则所有节点的角色都是 PRIMARY。
6. 向MGR集群中写入数据
接下来我们连接到 PRIMARY 节点,创建测试库表并写入数据:
[root@mgr1 ~]# mysql
mysql> create database mgr;
mysql> use mgr;
mysql> create table t1(c1 int unsigned not null primary key);
mysql> insert into t1 select rand()*10240;
mysql> select * from t1;
+------+
| c1 |
+------+
| 442 |
+------+
再连接到其中一个 SECONDARY 节点,查看刚刚在 PRIMARY 写入的数据是否可以看到:
mysql> select @@hostname;
+------------+
| @@hostname |
+------------+
| mgr2 |
+------------+
1 row in set (0.00 sec)
mysql> select * from mgr.t1;
+-----+
| c1 |
+-----+
| 442 |
+-----+
1 row in set (0.01 sec)
确认可以读取到该数据。
到这里,就完成了三节点MGR集群的安装部署。
4. MGR管理维护
今天介绍MGR集群的日常管理维护操作,包括主节点切换,单主&多主模式切换等。
现在有个三节点的MGR集群:
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3460f111-9314-11ee-a532-000c295df6cf | mgr1 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
| group_replication_applier | 52854f96-9314-11ee-8821-000c29ced34f | mgr2 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
| group_replication_applier | 70fa199f-9314-11ee-a8eb-000c29f957d2 | mgr3 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.00 sec)
1. 切换主节点
当主节点需要进行维护时,或者执行滚动升级时,就可以对其进行切换,将主节点切换到其他节点。
在命令行模式下,可以使用 group_replication_set_as_primary() 这个udf实现切换,例如:
-- 将Primary角色切换到第二个节点
mysql> select group_replication_set_as_primary('52854f96-9314-11ee-8821-000c29ced34f');
+--------------------------------------------------------------------------+
| group_replication_set_as_primary('52854f96-9314-11ee-8821-000c29ced34f') |
+--------------------------------------------------------------------------+
| Primary server switched to: 52854f96-9314-11ee-8821-000c29ced34f |
+--------------------------------------------------------------------------+
1 row in set (0.02 sec)
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3460f111-9314-11ee-a532-000c295df6cf | mgr1 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
| group_replication_applier | 52854f96-9314-11ee-8821-000c29ced34f | mgr2 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
| group_replication_applier | 70fa199f-9314-11ee-a8eb-000c29f957d2 | mgr3 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.00 sec)
顺便提一下,在MySQL 5.7版本中,只能通过重启以实现主节点的自动切换,不能手动切换。从这个角度来说,如果想要使用MGR,最好是选择MySQL 8.0版本,而不要使用5.7版本。
2. 切换单主/多主模式
在命令行模式下,可以调用 group_replication_switch_to_single_primary_mode()
和 group_replication_switch_to_multi_primary_mode()
来切换单主/多主模式。
#直接调用函数即可
mysql> select group_replication_switch_to_multi_primary_mode();
+--------------------------------------------------+
| group_replication_switch_to_multi_primary_mode() |
+--------------------------------------------------+
| Mode switched to multi-primary successfully. |
+--------------------------------------------------+
1 row in set (1.01 sec)
#查看各节点状态
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3460f111-9314-11ee-a532-000c295df6cf | mgr1 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
| group_replication_applier | 52854f96-9314-11ee-8821-000c29ced34f | mgr2 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
| group_replication_applier | 70fa199f-9314-11ee-a8eb-000c29f957d2 | mgr3 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.00 sec)
#切换成单主模式时可以指定某个节点的 server_uuid,如果不指定则会根据规则自动选择一个新的主节点
#在这里,我选择了指定mgr3节点作为新主
mysql> select group_replication_switch_to_single_primary_mode('70fa199f-9314-11ee-a8eb-000c29f957d2');
+-----------------------------------------------------------------------------------------+
| group_replication_switch_to_single_primary_mode('70fa199f-9314-11ee-a8eb-000c29f957d2') |
+-----------------------------------------------------------------------------------------+
| Mode switched to single-primary successfully. |
+-----------------------------------------------------------------------------------------+
1 row in set (0.02 sec)
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3460f111-9314-11ee-a532-000c295df6cf | mgr1 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
| group_replication_applier | 52854f96-9314-11ee-8821-000c29ced34f | mgr2 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
| group_replication_applier | 70fa199f-9314-11ee-a8eb-000c29f957d2 | mgr3 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.00 sec)
注意,在已经是单主模式时,无论是 group_replication_switch_to_single_primary_mode()
还是 switchToSinglePrimaryMode()
函数中指定另一个节点时是不会发生切换的,但也不会报错,只有提示。
3. 添加新节点
接下来我们演示如何向MGR集群中添加一个新节点。
首先,要先完成MySQL Server初始化,创建好MGR专用账户、设置好MGR服务通道等前置工作。
接下来,直接执行命令 start group_replication
启动MGR服务即可,新增的节点会进入分布式恢复这个步骤,它会从已有节点中自动选择一个作为捐献者(donor),并自行决定是直接读取binlog进行恢复,还是利用Clone进行全量恢复。
如果是已经在线运行一段时间的MGR集群,有一定存量数据,这时候新节点加入可能会比较慢,建议手动利用Clone进行一次全量复制。还记得前面创建MGR专用账户时,给加上了 BACKUP_ADMIN 授权吗,这时候就排上用场了,Clone需要用到这个权限。
下面演示如何利用Clone进行一次全量数据恢复,假定要新增的节点是 192.168.150.24 (给它命名为 mgr4)。
#在mgr4上设置捐献者
#为了降低对Primary节点的影响,建议选择其他Secondary节点
mysql> set global clone_valid_donor_list='192.168.150.24:3306';
#停掉mgr服务(如果有的话),关闭super_read_only模式,然后开始复制数据
#注意这里要填写的端口是3306(MySQL正常服务端口),而不是33061这个MGR服务专用端口
mysql> stop group_replication; set global super_read_only=0; clone INSTANCE FROM repl@192.168.150.21:3306 IDENTIFIED BY 'repl';
全量复制完数据后,该节点会进行一次自动重启。重启完毕后,再次确认 group_replication_group_name
、group_replication_local_address
、group_replication_group_seeds
这些选项值是否正确,如果没问题,执行 start group_replication
后,该节点应该就可以正常加入集群了。
4. 删除节点
在命令行模式下,一个节点想退出MGR集群,直接执行
stop group_replication
即可,如果这个节点只是临时退出集群,后面还想加回集群,则执行start group_replication
即可自动再加入。而如果是想彻底退出集群,则停止MGR服务后,执行reset master; reset slave all;
重置所有复制(包含MGR)相关的信息就可以了。
5. 异常退出的节点重新加回
当节点因为网络断开、实例crash等异常情况与MGR集群断开连接后,这个节点的状态会变成 UNREACHABLE,待到超过
group_replication_member_expel_timeout
+ 5 秒后,集群会踢掉该节点。等到这个节点再次启动并执行start group_replication
,正常情况下,该节点应能自动重新加回集群。
6. 重启MGR集群
正常情况下,MGR集群中的Primary节点退出时,剩下的节点会自动选出新的Primary节点。当最后一个节点也退出时,相当于整个MGR集群都关闭了。这时候任何一个节点启动MGR服务后,都不会自动成为Primary节点,需要在启动MGR服务前,先设置 group_replication_bootstrap_group=ON
,使其成为引导节点,再启动MGR服务,它才会成为Primary节点,后续启动的其他节点也才能正常加入集群。可自行测试,这里不再做演示。
P.S,第一个节点启动完毕后,记得重置选项 group_replication_bootstrap_group=OFF
,避免在后续的操作中导致MGR集群分裂。
7. 小结
本文介绍了MGR集群几种常见管理维护操作方法,包括切换主节点,切换单主/多主模式,添加节点,删除节点,异常节点重加入,重启整个MGR集群等。
5. MGR状态监控
MGR和传统主从复制类似,在运行过程中主要关注各节点的运行状态,以及Secondary节点的事务是否有延迟。本文介绍如何监控MGR节点状态、事务状态等。
1. 节点状态监控
通过查询 performance_schema.replication_group_members
表即可知道MGR各节点的状态:
mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3460f111-9314-11ee-a532-000c295df6cf | mgr1 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
| group_replication_applier | 52854f96-9314-11ee-8821-000c29ced34f | mgr2 | 3306 | ONLINE | SECONDARY | 8.0.30 | XCom |
| group_replication_applier | 70fa199f-9314-11ee-a8eb-000c29f957d2 | mgr3 | 3306 | ONLINE | PRIMARY | 8.0.30 | XCom |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+----------------------------+
3 rows in set (0.01 sec)
输出结果中主要几个列的解读如下:
MEMBER_ID 列值就是各节点的 server_uuid,用于唯一标识每个节点,在命令行模式下,调用 udf 时传入 MEMBER_ID 以指定各节点。
MEMBER_ROLE 表示各节点的角色,如果是 PRIMARY 则表示该节点可接受读写事务,如果是 SECONDARY 则表示该节点只能接受只读事务。如果只有一个节点是 PRIMARY,其余都是 SECONDARY,则表示当前处于 单主模式;如果所有节点都是 PRIMARY,则表示当前处于 多主模式。
MEMBER_STATE
表示各节点的状态,共有几种状态:ONLINE、RECOVERING、OFFLINE、ERROR、UNREACHABLE 等,下面分别介绍几种状态。
ONLINE,表示节点处于正常状态,可提供服务。
RECOVERING,表示节点正在进行分布式恢复,等待加入集群,这时候有可能正在从donor节点利用clone复制数据,或者传输binlog中。
OFFLINE,表示该节点当前处于离线状态。提醒,在正要加入或重加入集群时,可能也会有很短瞬间的状态显示为 OFFLINE。
ERROR,表示该节点当前处于错误状态,无法成为集群的一员。当节点正在进行分布式恢复或应用事务时,也是有可能处于这个状态的。当节点处于ERROR状态时,是无法参与集群事务裁决的。节点正在加入或重加入集群时,在完成兼容性检查成为正式MGR节点前,可能也会显示为ERROR状态。
UNREACHABLE,当组通信消息收发超时时,故障检测机制会将本节点标记为怀疑状态,怀疑其可能无法和其他节点连接,例如当某个节点意外断开连接时。当在某个节点上看到其他节点处于 UNREACHABLE 状态时,有可能意味着此时部分节点发生了网络分区,也就是多个节点分裂成两个或多个子集,子集内的节点可以互通,但子集间无法互通。
当节点的状态不是 ONLINE 时,就应当立即发出告警并检查发生了什么。
在节点状态发生变化时,或者有节点加入、退出时,表
performance_schema.replication_group_members
的数据都会更新,各节点间会交换和共享这些状态信息,因此可以在任意节点查看。
2. MGR事务状态监控
另一个需要重点关注的是Secondary节点的事务状态,更确切的说是关注待认证事务及待应用事务队列大小。
可以执行下面的命令查看当前除了 PRIMARY 节点外,其他节点的 trx_tobe_certified
或 relaylog_tobe_applied
值是否较大:
mysql> SELECT MEMBER_ID AS id, COUNT_TRANSACTIONS_IN_QUEUE AS trx_tobe_certified, COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS relaylog_tobe_applied, COUNT_TRANSACTIONS_CHECKED AS trx_chkd, COUNT_TRANSACTIONS_REMOTE_APPLIED AS trx_done, COUNT_TRANSACTIONS_LOCAL_PROPOSED AS proposed FROM performance_schema.replication_group_member_stats;
+--------------------------------------+--------------------+-----------------------+----------+----------+----------+
| id | trx_tobe_certified | relaylog_tobe_applied | trx_chkd | trx_done | proposed |
+--------------------------------------+--------------------+-----------------------+----------+----------+----------+
| 3460f111-9314-11ee-a532-000c295df6cf | 0 | 0 | 3 | 3 | 3 |
| 52854f96-9314-11ee-8821-000c29ced34f | 0 | 0 | 3 | 4 | 0 |
| 70fa199f-9314-11ee-a8eb-000c29f957d2 | 0 | 0 | 3 | 3 | 0 |
+--------------------------------------+--------------------+-----------------------+----------+----------+----------+
3 rows in set (0.01 sec)
其中,
relaylog_tobe_applied
的值表示远程事务写到relay log后,等待回放的事务队列,trx_tobe_certified
表示等待被认证的事务队列大小,这二者任何一个值大于0,都表示当前有一定程度的延迟。
还可以通过关注上述两个数值的变化,看看两个队列是在逐步加大还是缩小,据此判断Primary节点是否"跑得太快"了,或者Secondary节点是否"跑得太慢"。
多提一下,在启用流控(flow control)时,上述两个值超过相应的阈值时(group_replication_flow_control_applier_threshold 和 group_replication_flow_control_certifier_threshold 默认阈值都是 25000),就会触发流控机制。
3. 其他监控
另外,也可以查看接收到的事务和已执行完的事务之间的差距来判断:
mysql> SELECT RECEIVED_TRANSACTION_SET FROM performance_schema.replication_connection_status WHERE channel_name = 'group_replication_applier' UNION ALL SELECT variable_value FROM performance_schema.global_variables WHERE variable_name = 'gtid_executed'\G
*************************** 1. row ***************************
RECEIVED_TRANSACTION_SET: 5dbabbe6-8050-49a0-9131-1de449167446:1-6
*************************** 2. row ***************************
RECEIVED_TRANSACTION_SET: 5dbabbe6-8050-49a0-9131-1de449167446:1-6
2 rows in set (0.01 sec)
4. 小结
本文介绍了MGR监控的主要关注点,包括节点状态以及复制延迟状态,以及如何预判复制延迟会继续扩大还是能及时追上的方法。
6. 利用MySQL Router构建读写分离MGR集群
- 本文介绍如何在MGR集群前端部署MySQL Router以实现读写分离、读负载均衡,以及故障自动转移。
- MySQL Router是一个轻量级的中间件,它采用多端口的方案实现读写分离以及读负载均衡,而且同时支持mysql和mysql x协议。
- 建议把MySQL Router部署在应用服务器上,每个应用服务器都部署一套,这样应用程序可以直接连接本机IP,连接的效率更高,而且后端数据库发生变化时,程序端也无需修改IP配置。
1. 部署MySQL Router
MySQL Router第一次启动时要先初始化:
[root@proxy ~]# yum install mysql-router-community-8.0.35-1.el7.x86_64.rpm
2. 启动mysqlrouter服务
这就初始化完毕了,按照上面的提示,直接启动 mysqlrouter 服务即可:
[root@proxy ~]# systemctl enable --now mysqlrouter.service
Created symlink from /etc/systemd/system/multi-user.target.wants/mysqlrouter.service to /usr/lib/systemd/system/mysqlrouter.service.
[root@proxy ~]# ps -ef | grep -v grep | grep mysqlrouter
mysqlro+ 1461 1 0 11:38 ? 00:00:00 /usr/bin/mysqlrouter
可以看到 mysqlrouter 服务正常启动了。
mysqlrouter 初始化时自动生成的配置文件是 /etc/mysqlrouter/mysqlrouter.conf
,主要是关于R/W、RO不同端口的配置,例如:
cat >> /etc/mysqlrouter/mysqlrouter.conf << EOF
[routing:write]
bind_address = 192.168.150.24
bind_port = 8001
mode = read-write
destinations = 192.168.150.21:3306
[routing:blanace]
bind_address = 192.168.150.24
bind_port = 8002
mode = read-only
destinations = 192.168.150.22:3306,192.168.150.23:3306
EOF
[root@proxy ~]# systemctl restart mysqlrouter.service
3. 确认读写分离效果
创建用户
mysql> create user admin@'192.168.150.%' identified with mysql_native_password by '123456';
Query OK, 0 rows affected (0.00 sec)
mysql> grant all on *.* to admin@'192.168.150.%';
Query OK, 0 rows affected (0.01 sec)
现在,用客户端连接到6446(读写)端口,确认连接的是PRIMARY节点:
[root@mgr1 ~]# mysql -uadmin -p123456 -h192.168.150.24 -P8001
Enter password:
...
#记住下面几个 MEMBER_ID
mysql> select MEMBER_ID,MEMBER_ROLE from performance_schema.replication_group_members;
+--------------------------------------+-------------+
| MEMBER_ID | MEMBER_ROLE |
+--------------------------------------+-------------+
| 3460f111-9314-11ee-a532-000c295df6cf | SECONDARY |
| 52854f96-9314-11ee-8821-000c29ced34f | SECONDARY |
| 70fa199f-9314-11ee-a8eb-000c29f957d2 | PRIMARY |
+--------------------------------------+-------------+
3 rows in set (0.00 sec)
mysql> select @@server_uuid;
+--------------------------------------+
| @@server_uuid |
+--------------------------------------+
| 3460f111-9314-11ee-a532-000c295df6cf |
+--------------------------------------+
1 row in set (0.00 sec)
# 确实是连接的PRIMARY节点
同样地,连接8002(只读)端口,确认连接的是SECONDARY节点:
[root@mgr2 ~]# mysql -uadmin -p123456 -h192.168.150.24 -P8002
Enter password:
...
mysql> select @@server_uuid;
+--------------------------------------+
| @@server_uuid |
+--------------------------------------+
| 52854f96-9314-11ee-8821-000c29ced34f |
+--------------------------------------+
1 row in set (0.00 sec)
# 确实是连接的SECONDARY节点
4. 确认只读负载均衡效果
MySQL Router连接读写节点(Primary节点)默认的策略是 first-available,即只连接第一个可用的节点。Router连接只读节点(Secondary节点)默认的策略是 round-robin-with-fallback,会在各个只读节点间轮询。
保持8002端口原有的连接不退出,继续新建到8002端口的连接,查看 server_uuid,这时应该会发现读取到的是其他只读节点的值,因为 mysqlrouter 的读负载均衡机制是在几个只读节点间自动轮询。在默认的 round-robin-with-fallback 策略下,只有当所有只读节点都不可用时,只读请求才会打到PRIMARY节点上。
[root@mgr3 ~]# mysql -uadmin -p123456 -h192.168.150.24 -P8002
mysql> select @@server_uuid;
+--------------------------------------+
| @@server_uuid |
+--------------------------------------+
| 70fa199f-9314-11ee-a8eb-000c29f957d2 |
+--------------------------------------+
1 row in set (0.00 sec)
7. 小结
本文介绍了如何利用MySQL Router实现读写分离、读负载均衡,以及故障自动转移,利用MySQL Router可以提升应用端的透明性,后端数据库发生一些变化时,应用端无需跟着频繁变更。