ITPub博客

首页 > 数据库 > 数据库开发技术 > Mysql8.0 InnoDB Cluster 高可用集群架构实战

Mysql8.0 InnoDB Cluster 高可用集群架构实战

原创 数据库开发技术 作者:Fox666 时间:2025-01-02 07:00:00 0 删除 编辑

基本概述

InnoDB Cluster是MySQL官方实现高可用+读写分离的架构方案,其中包含以下组件

  • MySQL Group Replication
    ,简称MGR,是MySQL的主从同步高可用方案,包括数据同步及角色选举
  • Mysql Shell
     是InnoDB Cluster的管理工具,用来创建和管理集群
  • Mysql Router
     是业务流量入口,支持对MGR的主从角色判断,可以配置不同的端口分别对外提供读写服务,实现读写分离

MySQL Router与组复制和MySQL Shell高度整合,只有将其与组复制和MySQL Shell共同使用,才能够称为InnoDB Cluster。

集群架构

InnoDB Cluster将三个MySQL数据库实例构成一个高可用集群。其中一个实例是具有读/写能力的主要成员,其他两个实例是具有只读能力的次要成员。组复制将数据从主要成员复制到次要成员。MySQL Router将客户端应用程序连接到集群的主要成员。


搭建一主两从InnoDB集群

1. 安装3个数据库实例

Docker 快速安装 MySQL8.0

可以利用docker快速部署3个MySQL实例



主机名(角色)

server_id

宿主机IP

容器固定IP

DB Port

mgr-node1(primary)

1

192.168.65.223

172.19.0.10

3321>3306

mgr-node2(Secondary)

2

192.168.65.223

172.19.0.11

3322>3306

mgr-node3(Secondary)

3

192.168.65.223

172.19.0.12

3323>3306


# 创建组复制的网络  保证三个mysql容器之间可以通过容器名访问docker network create --driver bridge --subnet 172.19.0.0/24 --gateway 172.19.0.1 mgr-networkmkdir -p /mysql/mgr/node1/data /mysql/mgr/node1/conf /mysql/mgr/node1/logmkdir -p /mysql/mgr/node2/data /mysql/mgr/node2/conf /mysql/mgr/node2/logmkdir -p /mysql/mgr/node3/data /mysql/mgr/node3/conf /mysql/mgr/node3/log#以mgr-node1配置为例,创建/mysql/mgr/node1/conf/custom.cnf,添加以下配置:vim /mysql/mgr/node1/conf/custom.cnf[mysql]# 设置mysql客户端默认编码default-character-set=utf8[mysqld]#指定sever_id,三个Mysql实例需要分别改为对应的sever_idserver_id=1# 必须开启GTID支持gtid_mode=ONenforce_gtid_consistency=ON# 启用二进制日志log-bin=mysql-bin#启用并行复制binlog_transaction_dependency_tracking=WRITESETreplica_preserve_commit_order=ONreplica_parallel_type=LOGICAL_CLOCKtransaction_write_set_extraction=XXHASH64#对于Group Replication,数据必须存储在InnoDB事务存储引擎中disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"# mgr-node2和mgr-node3同上,注意配置文件路径和修改server_idvim /mysql/mgr/node2/conf/custom.cnf[mysql]# 设置mysql客户端默认编码default-character-set=utf8[mysqld]#指定sever_id,三个Mysql实例需要分别改为对应的sever_idserver_id=2# 必须开启GTID支持gtid_mode=ONenforce_gtid_consistency=ON# 启用二进制日志log-bin=mysql-bin#启用并行复制binlog_transaction_dependency_tracking=WRITESETreplica_preserve_commit_order=ONreplica_parallel_type=LOGICAL_CLOCKtransaction_write_set_extraction=XXHASH64#对于Group Replication,数据必须存储在InnoDB事务存储引擎中disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"vim /mysql/mgr/node3/conf/custom.cnf[mysql]# 设置mysql客户端默认编码default-character-set=utf8[mysqld]#指定sever_id,三个Mysql实例需要分别改为对应的sever_idserver_id=3# 必须开启GTID支持gtid_mode=ONenforce_gtid_consistency=ON# 启用二进制日志log-bin=mysql-bin#启用并行复制binlog_transaction_dependency_tracking=WRITESETreplica_preserve_commit_order=ONreplica_parallel_type=LOGICAL_CLOCKtransaction_write_set_extraction=XXHASH64#对于Group Replication,数据必须存储在InnoDB事务存储引擎中disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"#运行mysql容器# 为了便于测试,启动容器时指定好IP、hostnamedocker run  -d  \--name mgr-node1 \--privileged=true \--restart=always \--ip 172.19.0.10 \--hostname mgr-node1 \--add-host  mgr-node2:172.19.0.11 \--add-host  mgr-node3:172.19.0.12 \--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=1docker run  -d  \--name mgr-node2 \--privileged=true \--restart=always \--ip 172.19.0.11 \--hostname mgr-node2 \--add-host  mgr-node1:172.19.0.10 \--add-host  mgr-node3:172.19.0.12 \--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=1docker run  -d  \--name mgr-node3 \--privileged=true \--restart=always \--ip 172.19.0.12 \--hostname mgr-node3 \--add-host  mgr-node1:172.19.0.10 \--add-host  mgr-node2:172.19.0.11 \--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# 在宿主机上配置mysql容器的ip和host映射vim /etc/hosts172.19.0.10  mgr-node1172.19.0.11  mgr-node2172.19.0.12  mgr-node3

所有实例分别配置远程访问


# 以node1为例docker exec -it mgr-node1 /bin/bashmysql -u root -p123456#进入mysql执行ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';flush privileges;

2. 安装mysqlrouter和安装mysqlshell

MySQL Router是MySQL Proxy的后继产品,它提供了MySQL协议的路由器功能,可以用来实现读写分离、负载均衡和高可用性解决方案。

安装mysql-router

# 以centos7为例wget https://downloads.mysql.com/archives/get/p/41/file/mysql-router-community-8.0.27-1.el7.x86_64.rpmrpm -ivh mysql-router-community-8.0.27-1.el7.x86_64.rpm

安装mysql-shell

# 以centos7为例wget https://downloads.mysql.com/archives/get/p/43/file/mysql-shell-8.0.27-1.el7.x86_64.rpmrpm -ivh mysql-shell-8.0.27-1.el7.x86_64.rpm# 远程连接mysqlmysqlsh root@192.168.65.223:3321 --jsmysqlsh root@mgr-node1:3306 --js

3. InnoDB Cluster 初始化

3.1 参数及权限配置预需求检测

在添加实例到集群中前,使用该方法检查实例配置是否满足InnoDB 集群要求。

mysqlsh root@192.168.65.223:3321 --js// 检查实例是否符合InnoDB Cluster的参数及权限配置要求dba.checkInstanceConfiguration('root@mgr-node1:3306')dba.checkInstanceConfiguration('root@mgr-node2:3306')dba.checkInstanceConfiguration('root@mgr-node3:3306')

3. InnoDB Cluster 初始化

3.1 参数及权限配置预需求检测

在添加实例到集群中前,使用该方法检查实例配置是否满足InnoDB 集群要求。

mysqlsh root@192.168.65.223:3321 --js// 检查实例是否符合InnoDB Cluster的参数及权限配置要求dba.checkInstanceConfiguration('root@mgr-node1:3306')dba.checkInstanceConfiguration('root@mgr-node2:3306')dba.checkInstanceConfiguration('root@mgr-node3:3306')

如果验证通过返回ok。

如果验证没通过,比如出现下面的日志提示,需要mysql实例开启gtid和指定server_id

搭建InnoDB Cluster需要满足的要求如下:

InnoDB集群使用了Group Replication,因此必须满足使用组复制的要求。具体可以参考https://dev.mysql.com/doc/refman/8.0/en/group-replication-requirements.html。其中比较重要的几点有:


  • 必须开启二进制日志,并且日志格式为ROW,即--log-bin和binlog_format=row(默认);
  • 必须开启副本更新日志,即log_replica_updates=ON(默认) ;
  • 必须开启GTID,即gtid_mode=ON和enforce_gtid_consistency=ON。
  • 存储引擎只能使用InnoDB。最好禁用其他存储引擎:
    disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
    从8.0.23开始,集群中的实例要启用并行复制。需要配置以下系统变量:
    binlog_transaction_dependency_tracking=WRITESETslave_preserve_commit_order=ONslave_parallel_type=LOGICAL_CLOCKtransaction_write_set_extraction=XXHASH64











3.2 初始化InnoDB Cluster相关配置   

// 对实例配置InnoDB Cluster相关参数dba.configureInstance('root@mgr-node1:3306')dba.configureInstance('root@mgr-node2:3306')dba.configureInstance('root@mgr-node3:3306')

4.创建一主两从InnoDB集群

集群常用命令

#会列出dba相关指令dba.help();#列出详细指令的用法dba.help('deploySandboxInstance');#检查节点配置实例,用于加入cluster之前dba.checkInstanceConfiguration("root@hostname:3306");  #节点初始化dba.configureInstance('root@hostname:3306'); #重启集群dba.rebootClusterFromCompleteOutage('myCluster');  #会列出集群相关指令cluster.help();       #创建集群var cluster = dba.createCluster('myCluster');                              #获取当前集群实例var cluster = dba.getCluster('myCluster');   查看集群状态cluster.status();             #检查cluster节点状态 cluster.checkInstanceState("root@hostname:3306") ;                             #增加节点 cluster.addInstance("root@hostname:3306") ;               #删除节点 cluster.removeInstance("root@hostname:3306") ;            #强制删除节点cluster.removeInstance('root@hostname:3306',{force:true});    #  状态为missing的节点可以重新加入集群 cluster.rejoinInstance("root@hostname:3306")#解散集群 cluster.dissolve({force:true}) ;                          #集群描述 cluster.describe();                                      

进入主节点创建集群

初始化完第一个实例后,就可以创建集群了。

# 进入主节点创建集群mysqlsh root@192.168.65.223:3321 --js# 创建一个 cluster,命名为 'myCluster'    var cluster = dba.createCluster('myCluster');# 创建成功后,查看cluster状态cluster.status();    

当前集群的状态如下

添加副本实例

添加副本实例到创建好的集群。

#初始化第二个和第三个实例:cluster.addInstance('root@mgr-node2:3306');cluster.addInstance('root@mgr-node3:3306');#查看cluster状态cluster.status();

如果提示副本实例的GTID与集群不一致,选择通过Clone方式覆盖副本实例上的数据即可。

小结:完整的集群创建步骤

#进入主节点mysqlsh root@mgr-node1:3306 --js#mgr-node1 # 参数权限检查dba.checkInstanceConfiguration('root@mgr-node1:3306');# 初始化dba.configureInstance('root@mgr-node1:3306');# 创建集群var cluster = dba.createCluster('myCluster');#查看cluster状态cluster.status();#mgr-node2# 参数权限检查dba.checkInstanceConfiguration('root@mgr-node2:3306');# 初始化dba.configureInstance('root@mgr-node2:3306');# 添加副本cluster.addInstance('root@mgr-node2:3306');#mgr-node3# 参数权限检查dba.checkInstanceConfiguration('root@mgr-node3:3306');# 初始化dba.configureInstance('root@mgr-node3:3306');# 添加副本cluster.addInstance('root@mgr-node3:3306');#查看cluster状态cluster.status();

搭建一主两从集群架构最终效果如下:

注意到集群状态已变为"status": "OK"和"statusText": "Cluster is ONLINE and can tolerate up to ONE failure."。

集群节点状态:

  • ONLINE       - 节点状态正常。
  • OFFLINE      - 实例在运行,但没有加入任何Cluster。
  • RECOVERING   - 实例已加入Cluster,正在同步数据。
  • ERROR        - 同步数据发生异常。
  • UNREACHABLE  - 与其他节点通讯中断,可能是网络问题,可能是节点crash。
  • MISSING      - 节点已加入集群,但未启动group replication


测试数据是否同步

#主节点 mgr-node1[root@192-168-65-223 ~]# docker exec -it mgr-node1 bashroot@mgr-node1:/# mysql -uroot -p123456mysql>create database test;use test;create table t(x int primary key auto_increment,y int);insert into t(x,y) value(1,1);

注意:如果创建表没有设置主键,会抛出错误:ERROR 3098 (HY000): The table does not comply with the requirements by an external plugin.

# 查看其他节点,数据是否同步[root@192-168-65-223 ~]# docker exec -it mgr-node2 bashroot@mgr-node2:/# mysql -uroot -p123456mysql> use test;Reading table information for completion of table and column namesYou can turn off this feature to get a quicker startup with -ADatabase changedmysql> select * from t;+---+------+| x | y    |+---+------+| 1 |    1 |+---+------+1 row in set (0.00 sec)

测试主从切换

#停掉主节点[root@192-168-65-223 ~]# docker stop mgr-node1# 连接到mgr-node2MySQL  mgr-node1:3306 ssl  JS > \connect root@mgr-node2:3306# 获取集群实例 MySQL  mgr-node2:3306 ssl  JS > var cluster=dba.getCluster();# 查看集群状态MySQL  mgr-node2:3306 ssl  JS > cluster.status(){    "clusterName": "myCluster",     "defaultReplicaSet": {        "name": "default",         "primary": "mgr-node2:3306",         "ssl": "REQUIRED",         "status": "OK_NO_TOLERANCE",         "statusText": "Cluster is NOT tolerant to any failures. 1 member is not active.",         "topology": {            "mgr-node1:3306": {                "address": "mgr-node1:3306",                 "memberRole": "SECONDARY",                 "mode": "n/a",                 "readReplicas": {},                 "role": "HA",                 "shellConnectError": "MySQL Error 2003: Could not open connection to 'mgr-node1:3306': Can't connect to MySQL server on 'mgr-node1:3306' (113)",                 "status": "(MISSING)"            },             "mgr-node2:3306": {                "address": "mgr-node2:3306",                 "memberRole": "PRIMARY",                 "mode": "R/W",                 "readReplicas": {},                 "replicationLag": null,                 "role": "HA",                 "status": "ONLINE",                 "version": "8.0.27"            },             "mgr-node3:3306": {                "address": "mgr-node3:3306",                 "memberRole": "SECONDARY",                 "mode": "R/O",                 "readReplicas": {},                 "replicationLag": null,                 "role": "HA",                 "status": "ONLINE",                 "version": "8.0.27"            }        },         "topologyMode": "Single-Primary"    },     "groupInformationSourceMember": "mgr-node2:3306"

可以看到mgr-node2升级为主节点

# 启动mgr-node1节点[root@192-168-65-223 ~]# docker start mgr-node1# 查看集群状态,发现mgr-node1正在恢复,最终正常 MySQL  mgr-node2:3306 ssl  JS > cluster.status(){    "clusterName": "myCluster",     "defaultReplicaSet": {        "name": "default",         "primary": "mgr-node2:3306",         "ssl": "REQUIRED",         "status": "OK_NO_TOLERANCE",         "statusText": "Cluster is NOT tolerant to any failures. 1 member is not active.",         "topology": {            "mgr-node1:3306": {                "address": "mgr-node1:3306",                 "instanceErrors": [                    "NOTE: group_replication is stopped."                ],                 "memberRole": "SECONDARY",                 "memberState": "OFFLINE",                 "mode": "R/O",                 "readReplicas": {},                 "role": "HA",                 "status": "(MISSING)",                 "version": "8.0.27"            },             "mgr-node2:3306": {                "address": "mgr-node2:3306",                 "memberRole": "PRIMARY",                 "mode": "R/W",                 "readReplicas": {},                 "replicationLag": null,                 "role": "HA",                 "status": "ONLINE",                 "version": "8.0.27"            },             "mgr-node3:3306": {                "address": "mgr-node3:3306",                 "memberRole": "SECONDARY",                 "mode": "R/O",                 "readReplicas": {},                 "replicationLag": null,                 "role": "HA",                 "status": "ONLINE",                 "version": "8.0.27"            }        },         "topologyMode": "Single-Primary"    },     "groupInformationSourceMember": "mgr-node2:3306"}MySQL  mgr-node2:3306 ssl  JS > cluster.status(){    "clusterName": "myCluster",     "defaultReplicaSet": {        "name": "default",         "primary": "mgr-node2:3306",         "ssl": "REQUIRED",         "status": "OK",         "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",         "topology": {            "mgr-node1:3306": {                "address": "mgr-node1:3306",                 "memberRole": "SECONDARY",                 "mode": "R/O",                 "readReplicas": {},                 "replicationLag": null,                 "role": "HA",                 "status": "ONLINE",                 "version": "8.0.27"            },             "mgr-node2:3306": {                "address": "mgr-node2:3306",                 "memberRole": "PRIMARY",                 "mode": "R/W",                 "readReplicas": {},                 "replicationLag": null,                 "role": "HA",                 "status": "ONLINE",                 "version": "8.0.27"            },             "mgr-node3:3306": {                "address": "mgr-node3:3306",                 "memberRole": "SECONDARY",                 "mode": "R/O",                 "readReplicas": {},                 "replicationLag": null,                 "role": "HA",                 "status": "ONLINE",                 "version": "8.0.27"            }        },         "topologyMode": "Single-Primary"    },     "groupInformationSourceMember": "mgr-node2:3306"}

更多的常见操作

参数配置

可以用cluster.options()查看当前集群的配置属性,集群参数配置分为两种方式:

  • cluster.setOption() 用来设置所有节点的参数
  • cluster.setInstanceOption() 用来对指定节点配置属性
# 将所有节点的权重都改为50var cluster = dba.getCluster()cluster.setOption("memberWeight",50)# 重新加入集群重试次数改为5次cluster.setOption("autoRejoinTries",5)# 将其中一个节点的权重改为75cluster.setInstanceOption("mgr-node2:3306","memberWeight",75)# 重新加入集群重试次数改为10次cluster.setInstanceOption("mgr-node2:3306","autoRejoinTries",10)

配置节点权重

memberWeight选项的值域为0到100之间的整数,缺省值为50。该值是故障转移时自动选举主节点的百分比权重,具有较高memberWeight值的实例更有可能在单主群集中被选为主节点

// 查看集群的参数配置(包括memberWeight优先级配置)cluster.options()// 在集群创建时配置dba.createCluster('myCluster', {memberWeight:75}) // 第一个节点配置方式var cluster = dba.getCluster() cluster.addInstance('mgr-node2:3306',{memberWeight:50})cluster.addInstance('mgr-node3:3306',{memberWeight:25})// 在集群创建完成后修改权重var cluster = dba.getCluster()cluster.setInstanceOption('mgr-node1:3306','memberWeight',100)cluster.setInstanceOption('mgr-node2:3306','memberWeight',50)cluster.setInstanceOption('mgr-node3:3306','memberWeight',25)

将节点重新加入集群

状态为mssing的节点,通常是组复制关闭或中断状态,可以用cluster.rejoinInstance()重新加入集群,会重新对该节点设置MGR相关参数(持久化到mysqld-auto.conf中)

cluster.removeInstance('root@hostname:3306',{force:true});    

如果一些参数做了修改,如server_uuid变更,导致rejoin失败,则需要将节点从集群中删除后重新加入

cluster.removeInstance("root@hostname:3306",{force:true})cluster.rescan()cluster.addInstance("root@hostname:3306")

集群多数节点异常,恢复

当集群多个节点异常,则失去了仲裁机制,剩下的一个节点

// 将集群剥离为单节点运行JS > cluster.forceQuorumUsingPartitionOf("root@hostname:3306")// 重新加另外2个节点加入JS > cluster.rejoinInstance("root@hostname2:3306")JS > cluster.rejoinInstance("root@hostname3:3306")

集群节点角色切换

在MGR的管理下提供了以下3种方式进行角色切换,mysqlsh对其进行了封装调用

  • group_replication_set_as_primary(member_uuid);
    • cluster.setPrimaryInstance("homename:3306")
  • group_replication_switch_to_single_primary_mode()
    • cluster.switchToSinglePrimaryMode("homename:3306")
  • group_replication_switch_to_multi_primary_mode()
    • cluster.switchToMultiPrimaryMode()


单主模式-指定主节点切换

var cluster = dba.getCluster()cluster.setPrimaryInstance('homename:3306')cluster.status()

单主模式和多主模式相互切换

// 切换为多主模式var cluster = dba.getCluster()cluster.switchToMultiPrimaryMode()// 指定明确的主节点将多主模式切换为单主模式cluster.switchToSinglePrimaryMode("homename:3306")

将单主模式切换为多主模式的效果


销毁集群

删除与群集关联的所有元数据和配置,并禁用实例上的组复制,但不会删除在实例之间复制的任何数据。要再次创建集群,使用dba.createCluster()

var cluster = dba.getCluster()cluster.dissolve()

创建集群管理用户

cluster.setupAdminAccount('fox')



# 经典MySQL协议连接mysqlsh --mysql -hmgr-node1 -ufox# X协议连接mysqlsh --mysqlx -hmgr-node1 -ufox

7.使用MySQL Router连接集群

配置路由器

mysqlrouter --bootstrap root@mgr-node2:3306 --force --user=root#或者指定hostmysqlrouter --bootstrap root@mgr-node2:3306 --force --user=root --report-host mgr

注意,如果用户之前为该实例配置过路由,则可以通过指定force选项强制引导启动。

上面的内容是引导启动路由器时输出的信息,信息提示,MySQL经典协议使用6446端口和6447端口,X协议使用6448端口和6449端口,每种协议使用的两个端口分别用于读写和只读。

在一个运行的集群中,AdminAPI 可以引导多个路由器。用户可以使用cluster.listRouters()方法显示所有注册的路由器列表。


启动路由器

mysqlrouter &

路由器已经成功启动。现在,使用MySQL Shell连接路由器进行验证。

#连接mysqlrouter[root@192-168-65-223 ~]# mysqlsh root@localhost:6446 --sqlMySQL  localhost:6446 ssl  SQL > use test;MySQL  localhost:6446 ssl  test  SQL > select * from t;

查看集群成员信息

MySQL  localhost:6446 ssl  test  SQL > select * from performance_schema.replication_group_members;

测试

测试读写端口6446

用户可以通过连接本机的6446端口连接到MySQL实例

6446为读写端口,也可以插入数据

测试只读端口6647:插入数据报错


前篇:
MySQL 8.0 主从复制原理分析与实战
Mysql高可用与高扩展的解决方案组复制MGR详解


原文作者:Fox爱分享;如有侵权,请联系管理员删除。

上一篇: Prometheus监控实战
请登录后发表评论 登录
全部评论
图灵Fox老师,曾任互联网公司架构师,精通Java技术体系,具有十多年中间件研发和大型互联网项目架构经验,喜欢分享中间件、消息队列、搜索引擎、分布式存储、微服务以及云原生等技术

注册时间:2020-12-01

  • 博文量
    30
  • 访问量
    24092