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停止的,当重新启动时如果手工我们需要做以下操作才可拉起集群

  1. 判断哪个节点的GTID最新
  2. 将最新的GTID节点group_replication_bootstrap_group设置为on表示以这个节点为基础启动组复制
  3. 再将其他节点的组复制启动,集群恢复

我们也可以用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)
posted @ 2021-08-05 11:02  ZhenXing_Yu  阅读(3702)  评论(0编辑  收藏  举报