MySQL InnoDB Cluster
InnoDB Cluster 基本概述
InnoDB Cluster是MySQL官方实现高可用+读写分离的架构方案,其中包含以下组件
MySQL Group Replication
,简称MGR,是MySQL的主从同步高可用方案,包括数据同步及角色选举mysqlshell
是InnoDB Cluster的管理工具,用来创建和管理集群mysqlrouter
是业务流量入口,支持对MGR的主从角色判断,可以配置不同的端口分别对外提供读写服务,实现读写分离
InnoDB Cluster 基础信息
| IP地址 | 端口 | 角色 |版本|
| :-------- | --------: | ------: | ------: |------: |
| 10.186.63.179 | 6000/6001 | mysqlrouter/mysqlshell |MySQL Enterprise 8.0.22
| 10.186.63.65 | 3310 | MGR/mysqlshell |MySQL Enterprise 8.0.22
| 10.186.63.66 | 3310 | MGR/mysqlshell |MySQL Enterprise 8.0.22
| 10.186.63.66 | 3310 | MGR/mysqlshell |MySQL Enterprise 8.0.22
InnoDB Cluster 安装配置
1. 安装mysqlshell/mysqlrouter
mysqlshell和mysqlrouter可直接从官方网站下载对应操作系统版本软件包即可,以RHEL7系统为例安装
rpm -ivh mysql-router-commercial-8.0.22-1.1.el7.x86_64.rpm
rpm -ivh mysql-shell-commercial-8.0.22-1.1.el7.x86_64.rpm
2. 安装MySQL数据库
安装过程省略,只需要按照配置列表初始化生成3个数据库实例,由于默认root只允许本地登录,为方便管理,需要额外在每个实例下创建一个拥有远程访问的权限用户,如下为例:
-- 关闭会话级别binlog日志
set session sql_log_bin=off;
-- 创建远程管理用户并授权
create user root identified by 'root';
grant all on *.* to root with grant option;
3. InnoDB Cluster 初始化
3.1 参数及权限配置预需求检测
mysqlsh root@10.186.63.65:3310 --js
// 检查实例是否符合InnoDB Cluster的参数及权限配置要求
dba.checkInstanceConfiguration('root@10.186.63.65:3310')
dba.checkInstanceConfiguration('root@10.186.63.66:3310')
dba.checkInstanceConfiguration('root@10.186.63.67:3310')
如果不满足要求则会报错,如当权限不足时报错并提升授予以下权限
3.2 初始化InnoDB Cluster相关配置
mysqlsh root@10.186.63.65:3310 --js
// 对实例配置InnoDB Cluster相关参数
dba.configureInstance('root@10.186.63.65:3310',{clusterAdmin: "'mgr_admin'@'%'",clusterAdminPassword:"mgr_admin"})
dba.configureInstance('root@10.186.63.66:3310',{clusterAdmin: "'mgr_admin'@'%'",clusterAdminPassword:"mgr_admin"})
dba.configureInstance('root@10.186.63.67:3310',{clusterAdmin: "'mgr_admin'@'%'",clusterAdminPassword:"mgr_admin"})
3.3 创建集群/添加节点
需使用mysqlsh连接到任意一个节点后操作
初始化会执行以下操作
- 创建一个集群管理用户,包含Replication slave和backup admin权限
- 设置MGR相关配置
- 再次校验MySQL配置是否符合要求
// 初始化集群(第一个节点)
mysqlsh root@10.186.63.65:3310 --js
dba.createCluster('zhenxing',{memberWeight:90})
// 查看创建的集群状态
var cluster = dba.getCluster()
cluster.status()
// 添加第二个节点
var cluster = dba.getCluster()
// 执行命令后新节点默认采用全量克隆方式添加到集群
cluster.addInstance('10.186.63.66:3310',{memberWeight:50})
cluster.addInstance('10.186.63.67:3310',{memberWeight:25})
// 也可以明确指定用增量方式加入新节点(确保各节点数据均一致或存在binlog日志可同步)
cluster.addInstance('10.186.63.66:3310',{memberWeight:50,recoveryMethod:'incremental'})
cluster.addInstance('10.186.63.67:3310',{memberWeight:25,recoveryMethod:'incremental'})
cluster.addInstance('10.186.63.66:3310',{memberWeight:50,recoveryMethod:'clone'})
// 查看集群的参数配置(包括memberWeight优先级配置)
cluster.status()
cluster.options().defaultReplicaSet.topology
4. mysqlrouter配置
mysqlrouter 是一个轻量级的中间件,无状态的,建议和应用部署在一块,mysqlrouter有两种配置方式
- 手工填写后端 MGR 节点的地址,但是这样就没法感知 Primary 节点的变化,手工创建 MGR 时只能这么配置
- 引导模式自动进行配置,通过 mysql_innodb_cluster_metadata 元数据库动态感知 Primary 节点的变化,实现对应用的透明,这也是 InnoDB Cluster 的标准配置方法。
- mysqlrouter 必须通过配置
两个端口来实现读写分离
,没有解析 SQL 的功能。
## 创建一个非root用户来引导mysqlrouter
useradd mysql
## 初始化mysqlrouter
## 配置文件保存在/data/mysqlrouter/目录下,包含启停脚本
mysqlrouter --bootstrap root@10.186.63.65:3310 --directory=/data/mysqlrouter/ --conf-use-sockets --user=mysql --force
## 启动mysqlrouter
cd /data/mysqlrouter/
./start.sh
4.1 使用mysqlrouter访问
------ 通过读写端口 6446 访问数据库
mysqlsh root@10.186.63.179:6446
mysqlsh root@10.186.63.179:64460
-- 验证主从角色
select @@hostname,@@port,@@server_uuid;
select * from performance_schema.replication_group_members;
-- 验证写入操作正常
create database demo;
use demo;
create table t1(id int primary key auto_increment);
insert into t1 select 1;
select * from t1;
------ 通过读端口 6447 访问数据库
mysqlsh root@10.186.63.179:6447
mysqlsh root@10.186.63.179:64470
-- 验证读取操作正常
select @@hostname,@@port,@@server_uuid;
select * from performance_schema.replication_group_members;
select * from demo.t1;
-- 只读端口无法写入数据
MySQL 10.186.63.179:6447 ssl SQL > insert into demo.t1 select 1;
ERROR: 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement
InnoDB Cluster 常用操作
1. 查看集群状态
MySQL 10.186.63.179:6446 ssl JS > var cluster = dba.getCluster()
MySQL 10.186.63.179:6446 ssl JS > cluster.status()
{
"clusterName": "zhenxing",
"defaultReplicaSet": {
"name": "default",
"primary": "10.186.63.65:3310",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"10.186.63.65:3310": {
"address": "10.186.63.65:3310",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.22"
},
"10.186.63.66:3310": {
"address": "10.186.63.66:3310",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.22"
},
"10.186.63.67:3310": {
"address": "10.186.63.67:3310",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.22"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "10.186.63.65:3310"
}
2. 配置节点权重
memberWeight选项的值域为0到100之间的整数,缺省值为50。该值是故障转移时自动选举主节点的百分比权重,具有较高memberWeight值的实例更有可能在单主群集中被选为主节点
// 在集群创建完成后修改权重
var cluster = dba.getCluster()
cluster.setInstanceOption('10.186.63.65:3310','memberWeight',100)
cluster.setInstanceOption('10.186.63.66:3310','memberWeight',50)
cluster.setInstanceOption('10.186.63.67:3310','memberWeight',25)
// 查看集群的参数配置(包括memberWeight优先级配置)
cluster.options()
// 在集群创建时配置
dba.createCluster('zhenxing', {memberWeight:75}) // 第一个节点配置方式
var cluster = dba.getCluster()
cluster.addInstance('10.186.63.66:3310',{memberWeight:50})
cluster.addInstance('10.186.63.67:3310',{memberWeight:25})
3. 将节点重新加入集群
状态为mssing的节点,通常是组复制关闭或中断状态,可以用
cluster.rejoinInstance()
重新加入集群,会重新对该节点设置MGR相关参数(持久化到mysqld-auto.conf中)
3.1 rejoinInstance
cluster.status().defaultReplicaSet.topology
{
"10.186.63.65:3310": {
"address": "10.186.63.65:3310",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.22"
},
"10.186.63.66:3310": {
"address": "10.186.63.66:3310",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "(MISSING)" // MISSING
},
"10.186.63.67:3310": {
"address": "10.186.63.67:3310",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.22"
}
}
// 重新加入集群
cluster.rejoinInstance("root@10.186.63.66:3310")
3.2 removeInstance && addInstance
如果一些参数做了修改,如server_uuid变更,导致rejoin失败,则需要将节点从集群中删除后重新加入
cluster.removeInstance("root@10.186.63.66:3310",{force:true})
cluster.rescan()
cluster.addInstance("root@10.186.63.66:3310")
4. 集群多数节点异常,恢复
当集群多个节点异常,则失去了仲裁机制,剩下的一个节点
// 将集群剥离为单节点运行
JS > cluster.forceQuorumUsingPartitionOf("root@10.186.63.67:3310")
// 重新加另外2个节点加入
JS > cluster.rejoinInstance("root@10.186.63.65:3310")
JS > cluster.rejoinInstance("root@10.186.63.66:3310")
5. 完整关闭的集群如何恢复
每个节点都是正常的停止且正常的触发stop group_replication停止的,当重新启动时如果手工我们需要做以下操作才可拉起集群
- 判断哪个节点的GTID最新
- 将最新的GTID节点group_replication_bootstrap_group设置为on表示以这个节点为基础启动组复制
- 再将其他节点的组复制启动,集群恢复
我们也可以用mysqlsh的功能来让其自行判断最新数据的节点,再采用dba.rebootClusterFromCompleteOutage()
方式启动集群
// 通过65节点触发命令
MySQL 10.186.63.65:3310 ssl JS > dba.rebootClusterFromCompleteOutage()
Restoring the default cluster from complete outage...
// 检测到66服务器也包含在集群中,确认添加
The instance '10.186.63.66:3310' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
// 检测到67服务器也包含在集群中,确认添加
The instance '10.186.63.67:3310' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
// 检测到65不是最新的节点,66才是
Dba.rebootClusterFromCompleteOutage: The active session instance (10.186.63.65:3310) isn't the most updated in comparison with the ONLINE instances of the Cluster's metadata. Please use the most up to date instance: '10.186.63.66:3310'. (RuntimeError)
// 重新连接到66上执行相同操作
MySQL 10.186.63.65:3310 ssl JS > \connect "root@10.186.63.66:3310"
MySQL 10.186.63.65:3310 ssl JS > dba.rebootClusterFromCompleteOutage()
// 完成后查看集群状态是否正常
var cluster = dba.getCluster()
cluster.status()
6. 集群节点角色切换
在MGR的管理下提供了一下3中方式进行角色切换,mysqlsh对其进行了封装调用
- group_replication_set_as_primary(member_uuid);
cluster.setPrimaryInstance("IP:PORT")
- group_replication_switch_to_single_primary_mode()
cluster.switchToSinglePrimaryMode("IP:PORT")
- group_replication_switch_to_multi_primary_mode()
cluster.switchToMultiPrimaryMode()
6.1 单主模式-指定主节点切换
var cluster = dba.getCluster()
cluster.setPrimaryInstance('10.186.63.66:3310')
cluster.status()
6.2 单主模式和多主模式相互切换
// 切换为多主模式
var cluster = dba.getCluster()
cluster.switchToMultiPrimaryMode()
// 指定明确的主节点将多主模式切换为单主模式
cluster.switchToSinglePrimaryMode("10.186.63.65:3310")
7. 参数配置
可以用
cluster.options()
查看当前集群的配置属性,集群参数配置分为两种方式
cluster.setOption()
用来设置所有节点的参数cluster.setInstanceOption()
用来对指定节点配置属性
// 将所有节点的权重都改为50/重新加入集群重试次数改为5次
var cluster = dba.getCluster()
cluster.setOption("memberWeight",50)
cluster.setOption("autoRejoinTries",5)
// 将其中一个节点的权重改为75/重新加入集群重试次数改为10次
cluster.setInstanceOption("10.186.63.65:3310","memberWeight",75)
cluster.setInstanceOption("10.186.63.65:3310","autoRejoinTries",10)
8. 销毁集群
删除与群集关联的所有元数据和配置,并禁用实例上的组复制,但不会删除在实例之间复制的任何数据。要再次创建集群,使用,dba.createCluster()
var cluster = dba.getCluster()
cluster.dissolve()
错误记录
getCluster() 报错
// 获取集群报错,集群中的该节点MGR功能未启动
MySQL 10.186.63.65:3310 ssl JS > var cluster = dba.getCluster()
Dba.getCluster: This function is not available through a session to a standalone instance (metadata exists, instance belongs to that metadata, but GR is not active) (RuntimeError)
// 可以切换到sql模式查看集群当前状态
// 可以看到当前集群只有一个节点且处于OFFLINE状态
MySQL 10.186.63.65:3310 ssl SQL > \sql select * from performance_schema.replication_group_members\G
*************************** 1. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: 67c08e33-92c4-11eb-803d-02000aba3f41
MEMBER_HOST: 10.186.63.65
MEMBER_PORT: 3310
MEMBER_STATE: OFFLINE
MEMBER_ROLE:
MEMBER_VERSION:
1 row in set (0.0038 sec)
-- 可以将该节点作为引导节点启动集群
set global group_replication_bootstrap_group=on;
start group_replication;
-- 启动完毕后需关闭该参数
set global group_replication_bootstrap_group=off;
-- 再次查看可看到第一个节点恢复正常
MySQL 10.186.63.65:3310 ssl SQL > select * from performance_schema.replication_group_members\G
*************************** 1. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: 67c08e33-92c4-11eb-803d-02000aba3f41
MEMBER_HOST: 10.186.63.65
MEMBER_PORT: 3310
MEMBER_STATE: ONLINE
MEMBER_ROLE: PRIMARY
MEMBER_VERSION: 8.0.22
1 row in set (0.0016 sec)
addInstance 报错 Unknown MySQL server host
报这个错误需要配置/etc/hosts将主机名和IP对应关系配置上
Cluster.addInstance: Unknown MySQL server host '10-186-63-65' (2) (MySQL Error 2005)
// cluster.status() 命令也会有如下显示
"shellConnectError": "MySQL Error 2005 (HY000): Unknown MySQL server host '10-186-63-65' (2)",
清理残留的mgr信息
dba.dropMetadataSchema()
Dba.checkInstanceConfiguration: Dba.checkInstanceConfiguration: This function is not available through a session to a standalone instance (metadata exists, instance belongs to that metadata, but GR is not active) (RuntimeError)
克隆插件不能设置为force_plus_permanent
在多次对同一个实例做集群初始化时,不管是全量还是增量都会对克隆插件做一次重新加载,会如果对克隆插件开启了force_plus_permanent属性,则无法卸载,需要
Cluster.addInstance: error uninstalling plugin 'clone': 10.186.63.67:3310: Plugin 'clone' is force_plus_permanent and can not be unloaded (RuntimeError)