赞
踩
欢迎关注公众号:
一介IT
摘自个人网站,文章原文地址 https://l080l.com/mysql/ha/chapter11.html
本文
5.3万
字。建议收藏。
最初的 MySQL 版本只提供一种简单的主从异步复制,满足最基本的数据同步。为了提高复制性能,从单线程到组提交再到多线程复制,基本解决了复制延迟问题。为了解决从库与主库的一致性读问题,新增了半同步复制,而为了提供自动故障转移功能,又提供了组复制功能。要做到真正的高可用,失败切换必须对应用透明,于是在组复制的基础上,又发展出了InnoDB Cluster
。本文说明InnoDB Cluster
的相关概念、安装部署及管理维护。需要指出的一点是,在InnoDB Cluster
出现前,实现MySQL数据库的高可用性,除了原生的复制功能,通常还需要借助第三方中间件,如Keepalived
、MHA
等等。
InnoDB Cluster 主要由MySQL Shell
、MySQL Router
和MySQL服务器集群
组成,三者协同工作,共同为 MySQL 提供完整的高可用性解决方案。
InnoDB Cluster 以组复制为基础,集群中的每个 MySQL 服务器实例都是组复制的成员,提供了在 InnoDB Cluster 内复制数据的机制,并且具有内置的故障转移功能。MySQL Shell 在 InnoDB Cluster 中充当控制台角色,使用它包含的AdminAPI
,可以使安装、配置、管理、维护多个MySQL组复制实例的工作更加轻松。通过AdminAPI
的几条交互指令就可自动完成组复制配置。MySQL Router 可以根据集群部署信息自动生成配置,将客户端应用程序透明地连接到 MySQL 服务器实例。如果服务器实例意外故障,群集将自动重新配置。在默认的单主模式下,InnoDB Cluster 具有单个读写主服务器实例。多个辅助服务器实例是主服务器实例的副本。如果主服务器出现故障,则辅助服务器将自动升级为主服务器。MySQL Router 可以检测到这种情况并将客户端应用程序自动转发到新的主服务器。
结构图介绍
MySQL Servers
MySQL Group Replication
,简称MGR
, 是 MySQL 的主从同步高可用方案,包括数据同步及角色选举MySQL Router
MGR
的主从角色判断,可以配置不同的端口分别对外提供读写服务,实现读写分离(重要)MySQL Shell
(Cluster Admin)
Client App
(MYSQL Connector)
主机信息如下表所示
主机名 | IP:端口 | 角色 | 组件 |
---|---|---|---|
mysql80-router-01 | 192.168.2.80 | 管理节点MySQL Router | MySQL Router、MySQL Shell |
mysql80-01 | 192.168.2.81:3306 | cluster节点1:PRIMARY | MySQL 8.0、MySQL Shell |
mysql80-02 | 192.168.2.82:3306 | cluster节点2:SECONDARY | MySQL 8.0、MySQL Shell |
mysql80-03 | 192.168.2.83:3306 | cluster节点3:SECONDARY | MySQL 8.0、MySQL Shell |
# 1、系统安装(略)
# 2、计算机名、IP等(略)
# 3、禁用防火墙
systemctl stop firewalld
systemctl disable firewalld
# 4、安装Python
# MySQL Shell支持 JavaScript,Python,SQL脚本,在8.0.18版本之前必须保证当前的Linux操作系统安装了Python 2.7
[root@mysql80-router-01 ~]# python -V
Python 2.7.5
# 5、禁用SElinux
# vim /etc/selinux/config
SELINUX=disabled
# 6、在安装mysql数据库的节点服务设置,允许远程登录,
[root@MySQL80 ~]# mysql -uroot -proot
use mysql;
select Host,User from user;
update user set host = '%' where user ='root';
flush privileges;
# `%`是个 通配符 ,如果Host=192.168.1.%,那么就表示只要是IP地址前缀为“192.168.1.”的客户端都可以连接。
# 如果`Host=%`,表示所有IP都有连接权限。
# 7、配置hosts文件
# 8、重启
通过yum在线安装。
根据https://dev.mysql.com/downloads/repo/yum获取最新rpm包。
# 下载安装MySQL的rpm包,截止目前rpm版本是el7-7
wget https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm
yum -y localinstall mysql80-community-release-el7-7.noarch.rpm
在服务器三个节点安装mysql server。
yum install -y mysql-community-server
# 初始化密码
[root@mysql80-01 ~]# mysqld --initialize --user=mysql
[root@mysql80-01 ~]# cat /var/log/mysqld.log
2022-10-08T15:29:20.937483Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.30) initializing of server in progress as process 55360
2022-10-08T15:29:20.966525Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2022-10-08T15:29:21.362763Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2022-10-08T15:29:23.216780Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: #uv._rfYp4T4
#首次登录,启动服务,登录输入临时密码
[root@mysql80-01 ~]# systemctl start mysqld
[root@mysql80-01 ~]# mysql -hlocalhost -P3306 -uroot -p
#修改密码
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'root';
Query OK, 0 rows affected (0.01 sec)
# 如果不设置远程登录,管理服务器登录节点会报错
Shell.connect: Host '192.168.2.80' is not allowed to connect to this MySQL server (MySQL Error 1130)
#允许远程登陆
use mysql;
select Host,User from user;
update user set host = '%' where user ='root';
flush privileges;
#`%`是个 通配符 ,如果Host=192.168.1.%,那么就表示只要是IP地址前缀为“192.168.1.”的客户端都可以连接。
# 如果`Host=%`,表示所有IP都有连接权限。生产禁用。
在任意一台安装MySQL Shell。
# 安装 MySQL Shell
yum -y install mysql-shell
#=====安装完成信息========
#-----略----
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Installing : mysql-shell-8.0.30-1.el7.x86_64 1/1
Verifying : mysql-shell-8.0.30-1.el7.x86_64 1/1
Installed:
mysql-shell.x86_64 0:8.0.30-1.el7
Complete!
# 版本
[root@mysql80-router-01 ~]# whereis mysqlsh
mysqlsh: /usr/bin/mysqlsh /usr/lib/mysqlsh /usr/libexec/mysqlsh /usr/share/mysqlsh
[root@mysql80-router-01 ~]# mysqlsh --version
mysqlsh Ver 8.0.30 for Linux on x86_64 - for MySQL 8.0.30 (MySQL Community Server (GPL))
MySQL Shell 连接 MySQL Server 有如下方法。
#1、mysqlsh
#直接在命令行打mysqlsh命令进入mysql-shell交互模式(默认是JavaScript交互模式,可以切换模式)
[root@mysql80-router-01 ~]# mysqlsh
MySQL Shell 8.0.30
Copyright (c) 2016, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type '\help' or '\?' for help; '\quit' to exit.
MySQL JS >
#2、shell.connect
#在交互模式中使用 shell.connect('root@192.168.2.81:3306') 连接MySQL服务器
#提示输入root密码,登陆成功后提示是否保存密码
MySQL JS > shell.connect('root@192.168.2.81:3306')
MySQL JS >
#2.1 shell : JS的核心对象
#2.2 connect : 连接方法,参数是MySQL服务器的地址
#注意事项:
# 1. 如果MySQL服务器的主机防火墙没有关闭,或端口没有放行,连接失败
# 2. 如果MySQL的root用户的host没有设置成%,连接失败
# 登陆成功后的交互界面
MySQL 192.168.2.81:3306 ssl JS >
#采用直接连接的方式,如果想要连接那个MySQL实例直接使用命令而不是需要`shell.connect()`函数
[root@mysql80-router-01 ~]# mysqlsh -h192.168.2.81 -P3306 -uroot -proot
MySQL Shell 8.0.30
Copyright (c) 2016, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type '\help' or '\?' for help; '\quit' to exit.
WARNING: Using a password on the command line interface can be insecure.
Creating a session to 'root@192.168.2.81:3306'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 14
Server version: 8.0.30 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL 192.168.2.81:3306 ssl JS >
# shell下使用【mysqlsh \c】连接登陆
[root@mysql80-router-01 ~]# mysqlsh \c "root@192.168.2.81:3306"
MySQL Shell 8.0.30
Copyright (c) 2016, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'root@192.168.2.81:3306'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 18
Server version: 8.0.30 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL 192.168.2.81:3306 ssl JS >
# 登陆成功后,直接【\c】连接服务器即可
MySQL 192.168.2.81:3306 ssl JS > \c "root@192.168.2.82:3306"
Creating a session to 'root@192.168.2.82:3306'
Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 14
Server version: 8.0.30 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL 192.168.2.82:3306 ssl JS > \c "root@192.168.2.83:3306"
Creating a session to 'root@192.168.2.83:3306'
Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 14
Server version: 8.0.30 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL 192.168.2.83:3306 ssl JS >
#-----1、切换交互模式-------
#MySQL Shell提供了三种脚本语言的交互模式 JS、Pyhton、SQL
#切换方式使用斜线加上语言缩写
#切换到python模式
MySQL 192.168.2.83:3306 ssl JS > \py
Switching to Python mode...
MySQL 192.168.2.83:3306 ssl Py >
#切换到SQL
MySQL 192.168.2.83:3306 ssl Py > \sql
Switching to SQL mode... Commands end with ;
MySQL 192.168.2.83:3306 ssl SQL >
#切换到JS
MySQL 192.168.2.83:3306 ssl SQL > \js
Switching to JavaScript mode...
MySQL 192.168.2.83:3306 ssl JS >
#-----2、退出交互模式-------
# 方法一:输入【\q】或者 【\quit】
MySQL 192.168.2.83:3306 ssl JS > \q
Bye!
# 方法二:快捷键【Ctrl+D】
在管理节点安装 MySQL Router。
yum -y install mysql-router
#=====安装完成信息========
#-----略----
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Installing : mysql-router-community-8.0.30-1.el7.x86_64 1/1
Verifying : mysql-router-community-8.0.30-1.el7.x86_64 1/1
Installed:
mysql-router-community.x86_64 0:8.0.30-1.el7
Complete!
# 版本
[root@mysql80-router-01 ~]# whereis mysqlrouter
mysqlrouter: /usr/bin/mysqlrouter /usr/lib64/mysqlrouter /etc/mysqlrouter /usr/share/man/man1/mysqlrouter.1.gz
[root@mysql80-router-01 ~]# mysqlrouter --version
MySQL Router Ver 8.0.30 for Linux on x86_64 (MySQL Community - GPL)
# 每个节点服务器上,给远程管理用户授权,这里为方便,使用root用户
mysql> grant all on *.* to root with grant option;
mysql> flush privileges;
# mysql shell端,检查节点配置命令
[root@mysql80-router-01 ~]# mysqlsh
MySQL JS > dba.checkInstanceConfiguration('root@192.168.2.81:3306')
[root@mysql80-router-02 ~]# mysqlsh
MySQL JS > dba.checkInstanceConfiguration('root@192.168.2.82:3306')
[root@mysql80-router-03 ~]# mysqlsh
MySQL JS > dba.checkInstanceConfiguration('root@192.168.2.83:3306')
# 如果MySQL服务器不满足要求会打印出类似于下面这样的文字
Validating MySQL instance at mysql80-01:3306 for use in an InnoDB cluster...
This instance reports its own address as mysql80-01:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Checking whether existing tables comply with Group Replication requirements...
No incompatible tables detected
Checking instance configuration...
NOTE: Some configuration options need to be fixed:
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| Variable | Current Value | Required Value | Note |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| binlog_transaction_dependency_tracking | COMMIT_ORDER | WRITESET | Update the server variable |
| enforce_gtid_consistency | OFF | ON | Update read-only variable and restart the server |
| gtid_mode | OFF | ON | Update read-only variable and restart the server |
| server_id | 1 | <unique ID> | Update read-only variable and restart the server |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
Some variables need to be changed, but cannot be done dynamically on the server.
NOTE: Please use the dba.configureInstance() command to repair these issues.
{
"config_errors": [
{
"action": "server_update",
"current": "COMMIT_ORDER",
"option": "binlog_transaction_dependency_tracking",
"required": "WRITESET"
},
{
"action": "server_update+restart",
"current": "OFF",
"option": "enforce_gtid_consistency",
"required": "ON"
},
{
"action": "server_update+restart",
"current": "OFF",
"option": "gtid_mode",
"required": "ON"
},
{
"action": "server_update+restart",
"current": "1",
"option": "server_id",
"required": "<unique ID>"
}
],
"status": "error"
}
MySQL JS >
当针对 MySQL Shell 当前运行的本地 MySQL 实例发出dba.configureInstance()
时,它会尝试自动配置实例。对远程实例发出dba.configureInstance()
时,如果实例支持自动保持配置更改,则可以选择执行此操作。满足以下要求的实例会自动支持持久配置更改:
persisted_globals_load
设置为 ON (缺省值)。如果远程实例不支持持久化更改以配置 InnoDB 群集使用,则必须在本地配置实例。
表格里面的配置提示告诉用户不满足的配置项,并给出了要求的值,有两种方法修改这些配置项。
# 每个节点修改配置文件
[root@mysql80-01 ~]# cat /etc/my.cnf
[mysqld]
# ==========本文遇到的需要修改的=============
# MySQL集群的唯一标志,每一个MySQL实例都不相同(每个MySQL实例的server_id都不相同,数字自定义)
server_id=81
# 主从复制并行化开启,相比于MySQL5.7来说性能显著提升
binlog_transaction_dependency_tracking=WRITESET
# 开启全局事务ID,保证主从数据库数据一致性
enforce_gtid_consistency=ON
# 开启全局事务ID
gtid_mode=ON
# ==========其他也要满足的配置说明=============
# 从库并行复制(基于锁的并发控制)
slave_parallel_type=LOGICAL_CLOCK
# 保证在从库事务的执行顺序与主库相同
slave_preserve_commit_order=ON
AdminAPI
提供了dba.configureInstance()
函数,用于检查实例是否针对InnoDB Cluster
进行了适当配置,并在发现任何与InnoDB Cluster
不兼容的设置时配置实例。如果实例不需要更改配置,dba.configureInstance()
命令输出确认实例已准备好使用InnoDB Cluster
。根据 MySQL Shell 连接到实例的方式以及在实例上运行的 MySQL 版本,能够通过将这些更改自动保存到远程实例的配置文件 (缺省为 MySQL 实例数据目录下的 mysqld-auto.cnf 文件) 来持久化更改,或者手动更改实例的配置文件。无论进行配置更改的方式如何,必须重新启动实例以确保服务器检测到配置更改。
# 使用mysql shell远程设置实例配置
MySQL JS > dba.configureInstance('root@192.168.2.81:3306')
Configuring MySQL instance at mysql80-01:3306 for use in an InnoDB cluster...
This instance reports its own address as mysql80-01:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
applierWorkerThreads will be set to the default value of 4.
NOTE: Some configuration options need to be fixed:
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| Variable | Current Value | Required Value | Note |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| binlog_transaction_dependency_tracking | COMMIT_ORDER | WRITESET | Update the server variable |
| enforce_gtid_consistency | OFF | ON | Update read-only variable and restart the server |
| gtid_mode | OFF | ON | Update read-only variable and restart the server |
| server_id | 1 | <unique ID> | Update read-only variable and restart the server |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
Some variables need to be changed, but cannot be done dynamically on the server.
Do you want to perform the required configuration changes? [y/n]: y
Do you want to restart the instance after configuring it? [y/n]: y
Configuring instance...
The instance 'mysql80-01:3306' was configured to be used in an InnoDB cluster.
Restarting MySQL...
NOTE: MySQL server at mysql80-01:3306 was restarted.
一旦针对实例发出dba.configureInstance()
,该命令首先检查实例的当前设置是否适合InnoDB Cluster
使用,并将在一个报告中显示InnoDB Cluster
所需的设置。
dba.configureInstance()
时,它会尝试自动配置实例。dba.configureInstance('user@IP:port')
时,如果实例支持自动保持配置更改,则可以选择执行此操作。满足以下要求的实例会自动支持持久配置更改:
persisted_globals_load
设置为 ON (缺省值)。对于动态服务器变量,在dba.configureInstance()
后不需要重新启动实例,但对只读服务器变量,需要重启实例。此信息显示在发出dba.configureInstance()
之后生成的报告中。如果实例支持 RESTART 语句,则 MySQL Shell 可以关闭然后启动实例。这可确保 mysqld 检测到对实例配置文件所做的更改。
# 执行dba.configureInstance()后,再次检查实例配置的输出如下:
MySQL JS > dba.checkInstanceConfiguration('root@192.168.2.81:3306')
Validating MySQL instance at mysql80-01:3306 for use in an InnoDB cluster...
This instance reports its own address as mysql80-01:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Checking whether existing tables comply with Group Replication requirements...
No incompatible tables detected
Checking instance configuration...
Instance configuration is compatible with InnoDB cluster
The instance 'mysql80-01:3306' is valid to be used in an InnoDB cluster.
{
"status": "ok"
}
MySQL JS >
# 对集群中的每个服务器实例重复配置过程
dba.configureInstance('root@192.168.2.82:3306')
dba.configureInstance('root@192.168.2.83:3306')
# 检查
dba.checkInstanceConfiguration('root@192.168.2.82:3306')
dba.checkInstanceConfiguration('root@192.168.2.83:3306')
直接使用机器名代替 IP 地址,方便记忆,也是官网推荐方式。
# 所有服务器配置host解析
[root@mysql80-router-01 ~]# vim /etc/hosts
192.168.2.80 mysql80-router-01
192.168.2.81 mysql80-01
192.168.2.82 mysql80-02
192.168.2.83 mysql80-03
# 使用主机名代替IP登陆
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
Please provide the password for 'root@mysql80-01:3306': ****
Save password for 'root@mysql80-01:3306'? [Y]es/[N]o/Ne[v]er (default No): y
MySQL Shell 8.0.30
Copyright (c) 2016, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'root@mysql80-01:3306'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 36
Server version: 8.0.30 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL mysql80-01:3306 ssl JS >
准备好实例后,使用dba.createCluster()
函数创建集群,参数为自定义的集群名称。在创建集群之前,MySQL Shell 必须连接到一个实例,并将该实例用作群集的种子实例 (即主节点,在这个节点上执行创建命令,第一台机器都会被设置成主节点)。种子实例将被复制到添加到群集的其它实例,从而使它们成为种子实例的副本。当发出dba.createCluster(name)
时,MySQL Shell 会创建与连接到的服务器实例的会话。
MySQL mysql80-01:3306 ssl JS > dba.createCluster('myCluster')
A new InnoDB Cluster will be created on instance 'mysql80-01:3306'.
Validating instance configuration at mysql80-01:3306...
This instance reports its own address as mysql80-01:3306
Instance configuration is suitable.
NOTE: Group Replication will communicate with other members using 'mysql80-01:3306'. Use the localAddress option to override.
Creating InnoDB Cluster 'myCluster' on 'mysql80-01:3306'...
Adding Seed Instance...
Cluster successfully created. Use Cluster.addInstance() to add MySQL instances.
At least 3 instances are needed for the cluster to be able to withstand up to one server failure.
<Cluster:myCluster>
# 创建成功,这里提示:At least 3 instances
# 要能够容忍一个实例的失败,集群至少包含三个MySQL实例,添加更多实例会增加对实例失败的容忍度。下面添加另外两个实例
MySQL mysql80-01:3306 ssl JS >
使用Cluster.addInstance(instance)
函数向集群添加更多实例,其中 instance 是已配置实例的连接信息。
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster()
MySQL mysql80-01:3306 ssl JS > cluster.addInstance('root@mysql80-02:3306')
NOTE: The target instance 'mysql80-02:3306' has not been pre-provisioned (GTID set is empty). The Shell is unable to decide whether incremental state recovery can correctly provision it.
The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of 'mysql80-02:3306' with a physical snapshot from an existing cluster member. To use this method by default, set the 'recoveryMethod' option to 'clone'.
The incremental state recovery may be safely used if you are sure all updates ever executed in the cluster were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the cluster or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'.
Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): C
Validating instance configuration at mysql80-02:3306...
This instance reports its own address as mysql80-02:3306
Instance configuration is suitable.
NOTE: Group Replication will communicate with other members using 'mysql80-02:3306'. Use the localAddress option to override.
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Adding instance to the cluster...
Monitoring recovery process of the new cluster member. Press ^C to stop monitoring and let it continue in background.
Clone based state recovery is now in progress.
NOTE: A server restart is expected to happen as part of the clone process. If the
server does not support the RESTART command or does not come back after a
while, you may need to manually start it back.
* Waiting for clone to finish...
NOTE: mysql80-02:3306 is being cloned from mysql80-01:3306
** Stage DROP DATA: Completed
** Clone Transfer
FILE COPY ############################################################ 100% Completed
PAGE COPY ############################################################ 100% Completed
REDO COPY ############################################################ 100% Completed
NOTE: mysql80-02:3306 is shutting down...
* Waiting for server restart... ready
* mysql80-02:3306 has restarted, waiting for clone to finish...
** Stage RESTART: Completed
* Clone process has finished: 72.61 MB transferred in about 1 second (~72.61 MB/s)
State recovery already finished for 'mysql80-02:3306'
The instance 'mysql80-02:3306' was successfully added to the cluster.
# 添加成功
首先调用dba.getCluster()
函数获取集群对象,并赋予一个变量。如果未指定群集名称,则返回默认群集。缺省情况下使用dba.getCluster()
时,MySQL Shell 会尝试连接到群集的主实例。设置connectToPrimary
选项以配置此行为。
如果connectToPrimary
为 true 且活动的全局 MySQL Shell 会话不是主实例,则会查询主节点的集群,并且集群对象将连接到该集群。
如果connectToPrimary
为 false,则集群对象使用活动会话,就是与 MySQL Shell 当前全局会话相同的实例。
要在获取群集时强制连接到辅助节点,请建立与群集的辅助成员的连接,并通过发出以下命令来使用connectToPrimary
选项:
mysql-js> shell.connect(secondary_member)
mysql-js> var cluster1 = dba.getCluster(testCluster, {connectToPrimary:false})
注意辅助实例的super_read_only = ON
,因此无法对其进行更改。如果使用的是 MySQL 8.0.17 或更高版本,可以选择实例如何恢复与群集同步所需的事务,不同版本缺省值不同。
# MySQL 8.0.17
Please select a recovery method [I]ncremental recovery/[A]bort (default Incremental recovery):
# MySQL 8.0.30
Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone)
只有当加入实例恢复了先前由集群处理的所有事务时,它才能作为在线实例加入并开始处理事务。同样在 8.0.17 及更高版本中,可以使用waitRecovery
选项控制Cluster.addInstance()
的行为方式,让恢复操作在后台进行或监视 MySQL Shell 中的不同级别的进度。如果要在一个已经包含大量数据 (几十 G 会数百 G) 的集群中添加实例,推荐的方法是使用xtrabackup
复制一个主节点副本,然后添加该副本实例时使用增量恢复。这样做有两个明显的优点,一是可以在对主节点影响最小的情况下联机执行,二是减少实例恢复所需时间。
#以同样的方法添加第三个实例:
cluster.addInstance('root@mysql80-03:3306')
集群对象提供status()
方法,可以检查集群状态。在检查InnoDB Cluster
的状态之前,需要通过连接到集群中的任何实例来获取对InnoDB Cluster
对象的引用。但如果要更改群集的配置,则必须连接到可读写实例。status()
从所连接的服务器实例的集群视图检索集群的状态,并输出状态报告。所连接实例的状态直接影响状态报告中提供的信息,因此应确保连接实例的状态为 ONLINE。
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster()
MySQL mysql80-01:3306 ssl JS > cluster.status()
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "mysql80-01:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-02:3306": {
"address": "mysql80-02:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-03:3306": {
"address": "mysql80-03:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "mysql80-01:3306"
}
MySQL mysql80-01:3306 ssl JS >
Cluster.status()
的输出提供以下信息:
clusterName
:在dba.createCluster
期间分配给此集群的名称。defaultReplicaSet
:属于InnoDB Cluster
并包含数据集的服务器实例。primary
:仅在群集以单主模式运行时显示。显示当前主实例的地址。如果未显示此字段,则群集将以多主模式运行。ssl
:群集是否使用安全连接。显示REQUIRED
或DISABLED
的值,缺省为REQUIRED
,具体取决于在createCluster()
或addInstance()
期间如何配置memberSslMode
选项。此参数返回的值对应于实例上的group_replication_ssl_mode
服务器变量的值。status
:集群状态,反映了此群集可提供的高可用性。实例状态为以下之一:
ONLINE
,实例在线并参与群集;OFFLINE
,实例已失去与其它实例的连接;RECOVERING
,实例尝试通过在成为 ONLINE 成员之前检索所需的事务来尝试与集群同步;UNREACHABLE
,实例已丢失与群集的通信;ERROR
,实例在恢复阶段或应用事务时遇到错误。实例进入ERROR
状态后,super_read_only
选项设置为 ON,要退出ERROR
状态,必须手动设置super_read_only = OFF
。MISSING
,实例的状态,它是已配置群集的一部分,但当前不可用。MISSING
状态特定于InnoDB Cluster
,它不是Group Replication
生成的状态。MySQL Shell 使用此状态来指示在元数据中注册但在实时群集视图中找不到的实例。topology
:已添加到集群的实例。role
:此实例在集群中提供的功能,目前只有HA。mode
:服务器是读写 (“R/W”) 还是只读 (“R/O”) 。从版本 8.0.17 开始,从实例中 super_read_only 变量的当前状态以及群集是否具有仲裁派生的。在之前版本中,mode 的值来自实例是作为主实例还是辅助实例。通常,如果实例是主要的,则模式为 “R/W”,如果为辅助实例,则模式为 “R/O”。无论 super_read_only 变量的状态如何,群集中没有可见仲裁的任何实例都标记为 “R/O”。groupInformationSourceMember
:用于获取有关集群的信息的内部连接,显示为类似URI的连接字符串,通常为最初用于创建集群的连接。Cluster.status({'extended':value})
提供附加信息,value 值控制输出信息:
以上创建集群结束,这是另一种方法:如果在已经配置好的组复制上创建InnoDB Cluster
,并且希望使用它来创建集群,可将adoptFromGR
选项传递给dba.createCluster()
函数。创建的InnoDB Cluster
会匹配复制组是以单主数据库还是多主数据库运行。
要采用现有的组复制组,使用 MySQL Shell 连接到组成员。
# 以下示例采用单主组复制,mysql80-01为主实例,mysql80-02和mysql80-03为两个辅助实例。
# 这里连接到mysql80-01创建集群。
MySQL mysql80-01:3306 ssl JS > var cluster = dba.createCluster('mycluster', {adoptFromGR: true});
新群集与组复制的模式匹配。如果组复制以单主模式运行,则会创建单主群集。如果组复制以多主模式运行,则会创建多主集群。
MySQL Router 可以使用--bootstrap
选项基于InnoDB Cluster的元数据进行自我配置
。如果需要,会提示输入密码。如果用户名没有作为URI的一部分提供,那么将使用默认用户名 “root”。这会自动配置 MySQL Router 以将连接路由到群集的服务器实例。客户端应用程序连接到 MySQL Router 提供的端口,无需了解 InnoDB Cluster 拓扑。如果发生意外故障,InnoDB Cluster 会自动调整,MySQL Router 会检测到更改。这消除了客户端应用程序处理故障转移的需求。MySQL 不建议手动配置 MySQL Router 以重定向到 InnoDB Cluster 的端口,而是建议始终使用--bootstrap
选项,因为这可确保MySQL Router
从InnoDB Cluster
的元数据中获取其配置。使用生产部署时,建议将 MySQL Router 部署于客户端应用程序所在的每台计算机上。
MySQL Router 使用包含的元数据缓存插件来检索 InnoDB Cluster 的元数据,该元数据由构成 InnoDB Cluster 的服务器实例地址列表及其在集群中的角色组成。
https://dev.mysql.com/doc/mysql-router/8.0/en/mysqlrouter.html#option_mysqlrouter_bootstrap
# 查看当前MySQL集群被哪些路由连接
MySQL mysql80-01:3306 ssl JS > cluster.listRouters()
{
"clusterName": "myCluster",
"routers": {}
}
MySQL mysql80-01:3306 ssl JS >
# 还没有使用MySQL Router连接集群,所以routers为空
# mysqlrouter: 启动router路由命令
# --bootstrap: 指定连接哪台机器
# --user: 使用哪个用户用于运行路由,如果没有指定,会默认使用root
[root@mysql80-router-01 ~]# mysqlrouter --bootstrap root@mysql80-01:3306 --user=root
Please enter MySQL password for root:
# Bootstrapping system MySQL Router instance...
- Creating account(s) (only those that are needed, if any)
- Verifying account (using it to run SQL queries that would be run by Router)
- Storing account in keyring
- Adjusting permissions of generated files
- Creating configuration /etc/mysqlrouter/mysqlrouter.conf
Existing configuration backed up to '/etc/mysqlrouter/mysqlrouter.conf.bak'
# MySQL Router configured for the InnoDB Cluster 'myCluster'
After this MySQL Router has been started with the generated configuration
$ /etc/init.d/mysqlrouter restart
or
$ systemctl start mysqlrouter
or
$ mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf
# 生成的配置文件
InnoDB Cluster 'myCluster' can be reached by connecting to:
## MySQL Classic protocol
#(MySQL协议可读写端口号和只读端口号)
- Read/Write Connections: localhost:6446
- Read/Only Connections: localhost:6447
## MySQL X protocol
#(MySQL X 协议可读写端口号和只读端口号)
- Read/Write Connections: localhost:6448
- Read/Only Connections: localhost:6449
# 其他节点
mysqlrouter --bootstrap root@mysql80-02:3306 --user=root
mysqlrouter --bootstrap root@mysql80-03:3306 --user=root
基于检索到的 InnoDB Cluster 元数据,MySQL Router 自动配置mysqlrouter.conf
文件,包括带有bootstrap_server_addresses
的metadata_cache
部分,其中包含集群中所有服务器实例的地址。
[root@mysql80-router-01 ~]# cat /etc/mysqlrouter/mysqlrouter.conf
# File automatically generated during MySQL Router bootstrap
[DEFAULT]
name=system
user=root
keyring_path=/var/lib/mysqlrouter/keyring
master_key_path=/etc/mysqlrouter/mysqlrouter.key
connect_timeout=5
read_timeout=30
dynamic_state=/var/lib/mysqlrouter/state.json
client_ssl_cert=/var/lib/mysqlrouter/router-cert.pem
client_ssl_key=/var/lib/mysqlrouter/router-key.pem
client_ssl_mode=PREFERRED
server_ssl_mode=AS_CLIENT
server_ssl_verify=DISABLED
unknown_config_option=error
[logger]
level=INFO
[metadata_cache:bootstrap]
cluster_type=gr
router_id=1
user=mysql_router1_hypsc7un3kgg
metadata_cluster=myCluster
ttl=0.5
auth_cache_ttl=-1
auth_cache_refresh_interval=2
use_gr_notifications=0
[routing:bootstrap_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://myCluster/?role=PRIMARY
routing_strategy=first-available
protocol=classic
[routing:bootstrap_ro]
bind_address=0.0.0.0
bind_port=6447
destinations=metadata-cache://myCluster/?role=SECONDARY
routing_strategy=round-robin-with-fallback
protocol=classic
[routing:bootstrap_x_rw]
bind_address=0.0.0.0
bind_port=6448
destinations=metadata-cache://myCluster/?role=PRIMARY
routing_strategy=first-available
protocol=x
[routing:bootstrap_x_ro]
bind_address=0.0.0.0
bind_port=6449
destinations=metadata-cache://myCluster/?role=SECONDARY
routing_strategy=round-robin-with-fallback
protocol=x
[http_server]
port=8443
ssl=1
ssl_cert=/var/lib/mysqlrouter/router-cert.pem
ssl_key=/var/lib/mysqlrouter/router-key.pem
[http_auth_realm:default_auth_realm]
backend=default_auth_backend
method=basic
name=default_realm
[rest_router]
require_realm=default_auth_realm
[rest_api]
[http_auth_backend:default_auth_backend]
backend=metadata_cache
[rest_routing]
require_realm=default_auth_realm
[rest_metadata_cache]
require_realm=default_auth_realm
通过在引导 MySQL Router 之后添加另一个服务器实例来更改群集的拓扑时,需要根据更新的元数据更新bootstrap_server_addresses
。可以使用--bootstrap
选项重新引导 MySQL Router 并重启 MySQL Router 达到此目的。
生成的 MySQL Router 配置会创建用于连接到群集的 TCP 端口,包括使用经典 MySQL 协议和X协议与群集通信的端口,缺省值如下:
mysql 版本不同,端口可能有差异 (8.0.30
端口如下顺序:6446、6447、6448、6449):
传入连接的重定向方式取决于所使用的群集类型。使用单主群集时,默认情况下,MySQL Router 会发布X协议和经典协议端口,客户端连接到这些端口 (如 6446 或 64460 (6648)) 以进行读写会话,并重定向到群集的单个主节点。使用多主群集时,读写会话将以循环方式重定向到其中一个主实例。例如,到端口 6446 的第一个连接将被重定向到主实例1,到端口 6446 的第二个连接将被重定向到主实例 2,依此类推。对于传入的只读连接,MySQL Router 以循环方式将连接重定向到其中一个辅助实例。
使用--bootstrap
选项进行引导后,执行下面的命令后台启动 MySQL Router:
# 启动
[root@mysql80-router-01 ~]# mysqlrouter &
[1] 64593
# 重启,结束mysqlrouter进程
[root@mysql80-router-01 ~]# kill `ps -ef | grep router | grep -v grep | awk '{print $2}'`
[root@mysql80-router-01 ~]# mysqlrouter &
# mysql客户端连接检查如下命令,6446返回主节点,6447返回副节点
[root@mysql80-01 ~]#
mysql -uroot -proot -hmysql80-router-01 -P 6446 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -proot -hmysql80-router-01 -P 6446 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -proot -hmysql80-router-01 -P 6447 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -proot -hmysql80-router-01 -P 6447 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -proot -hmysql80-router-01 -P 6447 --protocol=TCP -N -r -B -e"select @@hostname"
# mysql shell连接检查如下命令,6448返回主节点,6449返回副节点
[root@mysql80-router-01 ~]#
mysqlsh --sql -uroot -proot -P6448 -e"select @@hostname"
mysqlsh --sql -uroot -proot -P6448 -e"select @@hostname"
mysqlsh --sql -uroot -proot -P6449 -e"select @@hostname"
mysqlsh --sql -uroot -proot -P6449 -e"select @@hostname"
mysqlsh --sql -uroot -proot -P6449 -e"select @@hostname"
# 可以将以上命令写入脚本router_connect_test.sh
# 将执行结果导入txt文件中
# ./router_connect_test.sh > result.txt
要测试高可用性是否有效,可以通过终止实例来模拟意外停止。群集检测到实例离开群集并重新配置自身,重新配置的确切方式取决于使用的是单主群集还是多主群集,以及实例在群集中的角色。在单主模式下:
server_uuid
区分优先级。MySQL Router 将读写连接重定向到新选择的主节点。# 停止主节点,在mysql80-01上执行
[root@mysql80-01 ~]# mysqladmin -uroot -proot shutdown
# 在管理服务器上查看新的主节点
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6448 -e"select @@hostname"
WARNING: Using a password on the command line interface can be insecure.
@@hostname
mysql80-02
# 主节点已经变成 mysql80-02
# 在管理服务器上查看副节点
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6449 -e"select @@hostname"
WARNING: Using a password on the command line interface can be insecure.
@@hostname
mysql80-03
# 或通过cluster.status()函数查看状态
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-02:3306"
MySQL mysql80-02:3306 ssl JS > var cluster = dba.getCluster()
MySQL mysql80-02:3306 ssl JS > cluster.status()
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "mysql80-02:3306",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE_PARTIAL",
"statusText": "Cluster is NOT tolerant to any failures. 1 member is not active.",
"topology": {
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "SECONDARY",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"shellConnectError": "MySQL Error 2003: Could not open connection to 'mysql80-01:3306': Can't connect to MySQL server on 'mysql80-01:3306' (111)",
"status": "(MISSING)"
},
"mysql80-02:3306": {
"address": "mysql80-02:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-03:3306": {
"address": "mysql80-03:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "mysql80-02:3306"
}
可以看到如下状态:
mysql80-01:3306 “memberRole”: “SECONDARY”, “status”: “(MISSING)”
mysql80-02:3306 “memberRole”: “PRIMARY”, “status”: “ONLINE”,
mysql80-03:3306 “memberRole”: “SECONDARY”, “status”: “ONLINE”
节点2成为新主节点,节点1变成副节点,状态 missing.
# 原主节点,在mysql80-01上执行
[root@mysql80-01 ~]# systemctl start mysqld
# 通过mysql shell查看 mysql80-01变成副节点
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6449 -e"select @@hostname"
WARNING: Using a password on the command line interface can be insecure.
@@hostname
mysql80-01
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6449 -e"select @@hostname"
WARNING: Using a password on the command line interface can be insecure.
@@hostname
mysql80-03
# 或通过cluster.status()函数查看状态
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster()
MySQL mysql80-01:3306 ssl JS > cluster.status()
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
# 集群又成了一主两从,客户端访问正常
# 停止副节点,在mysql80-03上执行
[root@mysql80-03 ~]# mysqladmin -uroot -proot shutdown
# 在管理服务器上查看主节点:mysql80-02
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6448 -e"select @@hostname"
WARNING: Using a password on the command line interface can be insecure.
@@hostname
mysql80-02
# 在管理服务器上查看副节点:mysql80-01
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6449 -e"select @@hostname"
WARNING: Using a password on the command line interface can be insecure.
@@hostname
mysql80-01
# 或通过cluster.status()函数查看状态
# 节点03丢失
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
MySQL mysql80-02:3306 ssl JS > var cluster = dba.getCluster()
MySQL mysql80-02:3306 ssl JS > cluster.status()
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-02:3306": {
"address": "mysql80-02:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-03:3306": {
"address": "mysql80-03:3306",
"memberRole": "SECONDARY",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"shellConnectError": "MySQL Error 2003: Could not open connection to 'mysql80-03:3306': Can't connect to MySQL server on 'mysql80-03:3306' (111)",
"status": "(MISSING)"
[root@mysql80-03 ~]# systemctl start mysqld
#(略)
启动节点 3 后,集群又成为了一主两从,客户端访问正常。
运行MySQL 8.0.16
及更高版本的实例支持组复制自动重新加入功能,可以将实例配置为在被驱逐后自动重新加入群集。 AdminAPI
提供了autoRejoinTries
选项,用于配置在驱逐后重新加入群集的尝试次数。默认情况下,实例不会自动重新加入群集。可以使用以下命令在集群级别或单个实例上配置autoRejoinTries
选项:
dba.createCluster()
Cluster.addInstance()
Cluster.setOption()
Cluster.setInstanceOption()
autoRejoinTries
选项对应group_replication_autorejoin_tries
系统变量,例如将重连尝试次数由缺省的 0 改为 10:
::: tip
MySQL 8.0.16
之前缺省值为 0,即不尝试自动重新加入,当前版本MySQL 8.0.30
缺省值为 3,autoRejoinTries
选项接受介于 0 和 2016 之间的正整数值。自动重连适用于网络不稳定的场景。
:::
========= 查询 ==========
# 主节点查询缺省值3,-P 6446
[root@mysql80-01 ~]# mysql -uroot -proot -hmysql80-router-01 -P 6446 --protocol=TCP -N -r -B -e"select @@group_replication_autorejoin_tries"
3
# 副节点查询缺省值3,-P 6447
[root@mysql80-01 ~]# mysql -uroot -proot -hmysql80-router-01 -P 6447 --protocol=TCP -N -r -B -e"select @@group_replication_autorejoin_tries"
3
# 管理服务器上查询主节点值3
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6448 -e"select @@group_replication_autorejoin_tries"
3
# 管理服务器上查询副节点值3
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6449 -e"select @@group_replication_autorejoin_tries"
3
========= 更改 ==========
# 更改值,如改为10
[root@mysql80-router-01 ~]# mysqlsh -uroot root@mysql80-01:3306 -proot -e "var cluster=dba.getCluster(); cluster.setOption('autoRejoinTries',10)"
WARNING: Using a password on the command line interface can be insecure.
WARNING: Each cluster member will only proceed according to its exitStateAction if auto-rejoin fails (i.e. all retry attempts are exhausted).
Setting the value of 'autoRejoinTries' to '10' in all cluster members ...
Successfully set the value of 'autoRejoinTries' to '10' in the 'myCluster' cluster.
# 再次查询
[root@mysql80-router-01 ~]#
mysqlsh --sql -uroot -proot -P6448 -e"select @@group_replication_autorejoin_tries"
mysqlsh --sql -uroot -proot -P6449 -e"select @@group_replication_autorejoin_tries"
# 所有节点都已经修改
运行MySQL 8.0.12
及更高版本的实例具有group_replication_exit_state_action
变量,可以使用AdminAPI
exitStateAction
选项配置该变量。这可以控制在意外离开集群时实例执行的操作。
exitStateAction
选项为READ_ONLY
,这意味着意外离开集群的实例地变为只读。exiStateAction
是ABORT_SERVER
,那么在意外离开集群的情况下,实例会关闭 MySQL,并且必须先重新启动它才能重新加入集群。# 查询 READ_ONLY
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6446 -e"select @@group_replication_exit_state_action"
WARNING: Using a password on the command line interface can be insecure.
@@group_replication_exit_state_action
READ_ONLY
[root@mysql80-router-01 ~]#
# 修改 ABORT_SERVER
[root@mysql80-router-01 ~]# mysqlsh -uroot root@mysql80-01:3306 -proot -e "var cluster=dba.getCluster(); cluster.setOption('exitStateAction','ABORT_SERVER')"
WARNING: Using a password on the command line interface can be insecure.
Setting the value of 'exitStateAction' to 'ABORT_SERVER' in all cluster members ...
Successfully set the value of 'exitStateAction' to 'ABORT_SERVER' in the 'myCluster' cluster.
# 再次查询
[root@mysql80-router-01 ~]# mysqlsh --sql -uroot -proot -P6447 -e"select @@group_replication_exit_state_action"
WARNING: Using a password on the command line interface can be insecure.
@@group_replication_exit_state_action
ABORT_SERVER
:::warning 需要注意的是
在使用自动重新加入功能时,exitStateAction
选项配置的操作仅在所有尝试重新加入群集失败的情况下发生。
:::
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster();
MySQL mysql80-01:3306 ssl JS > cluster.removeInstance('root@mysql80-02:3306');
The instance will be removed from the InnoDB cluster. Depending on the instance
being the Seed or not, the Metadata session might become invalid. If so, please
start a new session to the Metadata Storage R/W instance.
* Waiting for instance 'mysql80-02:3306' to synchronize with the primary...
** Transactions replicated ############################################################ 100%
* Instance 'mysql80-02:3306' is attempting to leave the cluster...
The instance 'mysql80-02:3306' was successfully removed from the cluster.
# 查看集群状态,只剩下两个节点
MySQL mysql80-01:3306 ssl JS > cluster.status()
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "mysql80-01:3306",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures.",
"topology": {
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-03:3306": {
"address": "mysql80-03:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "mysql80-01:3306"
}
MySQL mysql80-01:3306 ssl JS >
cluster.removeInstance()
操作可确保从ONLINE
的所有集群成员和实例本身的元数据中删除实例。当要删除的实例具有仍需要应用的事务时,AdminAPI
会等待 MySQL Shell dba.gtidWaitTimeout
选项为要应用的事务GTID
配置的秒数。 MySQL Shell dba.gtidWaitTimeout
选项的默认值为 60 秒:
# 更改默认值 如120s
MySQL mysql80-01:3306 ssl JS > \option dba.gtidWaitTimeout
60
MySQL mysql80-01:3306 ssl JS > shell.options['dba.gtidWaitTimeout']=120
120
MySQL mysql80-01:3306 ssl JS > \option dba.gtidWaitTimeout
120
MySQL mysql80-01:3306 ssl JS >
如果在等待应用事务并且force
选项为false
(缺省)时达到dba.gtidWaitTimeout
定义的超时值,则会发出错误并中止删除操作。如果在等待应用事务并且force
选项设置为true时达到dba.gtidWaitTimeout
定义的超时值,则操作将继续而不会出现错误,并从群集中删除该实例。从群集中删除实例时忽略错误可能导致实例与群集不同步,从而阻止其稍后重新加入群集。只有当不再使用实例时在开启强制选项,在所有其它情况下,应该始终尝试恢复实例,并仅状态为ONLINE
时将其删除。
==== 关于重新加入 ====
# 如果从节点出现故障,他就会从集群中退出,但是在重启之后,会自动重新加入到集群中。
# 注意如果节点不自动加入到集群中,使用cluster.rejoinInstance()加入。
# 重新加入集群cluster.rejoinInstance()是在实例意外离开可用,上一步节点2是手动移除,无法使用rejoin函数加入。
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster();
MySQL mysql80-01:3306 ssl JS > cluster.rejoinInstance("root@mysql80-02:3306")
Cluster.rejoinInstance: The instance 'mysql80-02:3306' does not belong to the cluster: 'myCluster'. (RuntimeError)
==== 关于新增节点 ====
# 通过removeInstance移除的节点要重新加入,不能用rejoin
# 而是需要用cluster.addInstance()加入
MySQL mysql80-01:3306 ssl JS > cluster.addInstance("root@mysql80-02:3306")
The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of 'mysql80-02:3306' with a physical snapshot from an existing cluster member. To use this method by default, set the 'recoveryMethod' option to 'clone'.
The incremental state recovery may be safely used if you are sure all updates ever executed in the cluster were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the cluster or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'.
Incremental state recovery was selected because it seems to be safely usable.
Validating instance configuration at mysql80-02:3306...
This instance reports its own address as mysql80-02:3306
Instance configuration is suitable.
NOTE: Group Replication will communicate with other members using 'mysql80-02:3306'. Use the localAddress option to override.
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Adding instance to the cluster...
Monitoring recovery process of the new cluster member. Press ^C to stop monitoring and let it continue in background.
Incremental state recovery is now in progress.
* Waiting for distributed recovery to finish...
NOTE: 'mysql80-02:3306' is being recovered from 'mysql80-01:3306'
* Distributed recovery has finished
The instance 'mysql80-02:3306' was successfully added to the cluster.
# 查看状态
MySQL mysql80-01:3306 ssl JS > cluster.status()
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "mysql80-01:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-02:3306": {
"address": "mysql80-02:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-03:3306": {
"address": "mysql80-03:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "mysql80-01:3306"
}
MySQL mysql80-01:3306 ssl JS >
如果 MySQL 服务节点全部挂掉,那么在重新启动 MySQL 服务器之后,集群不会自动恢复,实验步骤:
MySQL Shell
连接任意一个 MySQL 实例;dba.rebootClusterFromCompleteOutage()
函数恢复集群。dba.rebootClusterFromCompleteOutage()
操作遵循以下步骤以确保正确重新配置集群:
dba.rebootClusterFromCompleteOutage()
。要手动检测哪个实例具有最多事务,请检查每个实例上的gtid_executed
变量:
show variables like 'gtid_executed';
如果此过程失败,并且群集元数据已严重损坏,则可能需要删除元数据并从头开始再次创建群集。可以使用dba.dropMetadataSchema()
删除集群元数据。dba.dropMetadataSchema
方法应仅用作无法还原群集时的最后手段,并且删除的元数据是不可恢复的。
# 模拟集群挂掉,停止所有节点
[root@mysql80-01 ~]# mysqladmin -uroot -proot shutdown
[root@mysql80-02 ~]# mysqladmin -uroot -proot shutdown
[root@mysql80-03 ~]# mysqladmin -uroot -proot shutdown
# 1.将挂掉的MySQL实例全部启动
[root@mysql80-01 ~]# systemctl start mysqld
[root@mysql80-02 ~]# systemctl start mysqld
[root@mysql80-03 ~]# systemctl start mysqld
# 2.使用MySQL Shell连接任意一个MySQL实例
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
# 因为集群挂掉,所以无法用以下命令查看集群状态
#var cluster = dba.getCluster()
#cluster.status()
# 3.使用dba.rebootClusterFromCompleteOutage()函数重启恢复集群
#重启很耗时
MySQL mysql80-01:3306 ssl JS > dba.rebootClusterFromCompleteOutage()
Restoring the cluster 'myCluster' from complete outage...
The instance 'mysql80-02:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
The instance 'mysql80-03:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
Validating instance configuration at mysql80-01:3306...
This instance reports its own address as mysql80-01:3306
Instance configuration is suitable.
* Waiting for seed instance to become ONLINE...
mysql80-01:3306 was restored.
Rejoining 'mysql80-02:3306' to the cluster.
Validating instance configuration at mysql80-02:3306...
This instance reports its own address as mysql80-02:3306
Instance configuration is suitable.
Rejoining instance 'mysql80-02:3306' to cluster 'myCluster'...
Re-creating recovery account...
NOTE: User 'mysql_innodb_cluster_3685009063'@'%' already existed at instance 'mysql80-01:3306'. It will be deleted and created again with a new password.
The instance 'mysql80-02:3306' was successfully rejoined to the cluster.
Rejoining 'mysql80-03:3306' to the cluster.
Validating instance configuration at mysql80-03:3306...
This instance reports its own address as mysql80-03:3306
Instance configuration is suitable.
Rejoining instance 'mysql80-03:3306' to cluster 'myCluster'...
Re-creating recovery account...
NOTE: User 'mysql_innodb_cluster_1097448896'@'%' already existed at instance 'mysql80-01:3306'. It will be deleted and created again with a new password.
The instance 'mysql80-03:3306' was successfully rejoined to the cluster.
The cluster was successfully rebooted.
<Cluster:myCluster>
# 查看集群状态
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster()
MySQL mysql80-01:3306 ssl JS > cluster.status()
解散 InnoDB Cluster 需要连接到状态为ONLINE
的读写实例,例如单主集群中的主实例,并使用Cluster.dissolve()
命令。这将删除与群集关联的所有元数据和配置,并禁用实例上的组复制,但不会删除在实例之间复制的任何数据。要再次创建集群,使用dba.createCluster()
。摘要如下:
# 登陆其中一台实例
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster()
# 解散集群命令
MySQL mysql80-01:3306 ssl JS > cluster.dissolve()
The cluster still has the following registered instances:
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"topology": [
{
"address": "mysql80-01:3306",
"label": "mysql80-01:3306",
"role": "HA"
},
{
"address": "mysql80-03:3306",
"label": "mysql80-03:3306",
"role": "HA"
},
{
"address": "mysql80-02:3306",
"label": "mysql80-02:3306",
"role": "HA"
}
],
"topologyMode": "Single-Primary"
}
}
WARNING: You are about to dissolve the whole cluster and lose the high availability features provided by it. This operation cannot be reverted. All members will be removed from the cluster and replication will be stopped, internal recovery user accounts and the cluster metadata will be dropped. User data will be maintained intact in all instances.
Are you sure you want to dissolve the cluster? [y/N]: y
* Waiting for instance 'mysql80-01:3306' to synchronize with the primary...
** Transactions replicated ############################################################ 100%
* Waiting for instance 'mysql80-03:3306' to synchronize with the primary...
** Transactions replicated ############################################################ 100%
* Waiting for instance 'mysql80-02:3306' to synchronize with the primary...
** Transactions replicated ############################################################ 100%
* Dissolving the Cluster...
* Waiting for instance 'mysql80-03:3306' to synchronize with the primary...
** Transactions replicated ############################################################ 100%
* Instance 'mysql80-03:3306' is attempting to leave the cluster...
* Waiting for instance 'mysql80-02:3306' to synchronize with the primary...
** Transactions replicated ############################################################ 100%
* Instance 'mysql80-02:3306' is attempting to leave the cluster...
* Instance 'mysql80-01:3306' is attempting to leave the cluster...
The cluster was successfully dissolved.
Replication was disabled but user data was left intact.
# 解散成功
MySQL mysql80-01:3306 ssl JS > cluster.status()
Cluster.status: Can't call function 'status' on an offline cluster (RuntimeError)
dba.gtidWaitTimeout
选项配置Cluster.dissolve()
操作在从群集中删除目标实例之前等待群集事务的时间,但仅限于目标实例为ONLINE
。如果在等待要删除的任何实例上应用集群事务超时,则会发出错误(除非使用force:true
)。
dba.createCluster()
或Cluster.addInstance()
方法中指定memberWeight
影响新主节点的选举结果。memberWeight
选项的值域为 0 到 100 之间的整数,缺省值为 50。group_replication_member_weight
系统变量。memberWeight
值的实例更有可能在单主群集中被选为主节点。memberWeight
值,则根据服务器UUID
的字典正序,选择第一个实例作为主节点。# 登陆
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
# 登陆节点1,默认将节点1设置为主节点
MySQL mysql80-01:3306 ssl JS > dba.createCluster('NewCluster', {memberWeight:35})
# 获取新集群
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster()
# 新曾其他节点
# 注意:memberWeight值是故障转移时自动选举主节点的百分比权重,并非重建时谁的值大谁做主节点
mycluster.addInstance('root@mysql80-03:3306', {memberWeight:25})
mycluster.addInstance('root@mysql80-02:3306', {memberWeight:50})
# 查看集群状态
MySQL mysql80-01:3306 ssl JS > cluster.status()
{
"clusterName": "NewCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "mysql80-01:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-02:3306": {
"address": "mysql80-02:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-03:3306": {
"address": "mysql80-03:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "mysql80-01:3306"
}
或者叫做更改组复制拓扑,InnoDB Cluster 默认以单主模式运行,其中集群具有一个接受读写 (R/W) 的主服务器,集群中的所有其余实例仅接受只读 (R/O) 。MySQL 提供了另外一种设计集群的方式(多主模式),将群集配置为在多主模式时,群集中的所有实例都是主节点,这意味着它们同时接受读取和写入查询 (R/W)。如果群集的所有实例都运行 MySQL 服务器版本 8.0.15 或更高版本,则可以在群集联机时更改群集的拓扑。
官网提供了两种模式切换的 API,可以轻松在两种模式中进行切换。
# 调用函数进行单主-多主的切换cluster.switchToMultiPrimaryMode()
# 登陆
[root@mysql80-router-01 ~]# mysqlsh \c "root@mysql80-01:3306"
MySQL mysql80-01:3306 ssl JS > var cluster = dba.getCluster()
# 切换多主命令
MySQL mysql80-01:3306 ssl JS > cluster.switchToMultiPrimaryMode()
Switching cluster 'NewCluster' to Multi-Primary mode...
Instance 'mysql80-02:3306' was switched from SECONDARY to PRIMARY.
Instance 'mysql80-03:3306' was switched from SECONDARY to PRIMARY.
Instance 'mysql80-01:3306' remains PRIMARY.
The cluster successfully switched to Multi-Primary mode.
# 切换完成之后查看当前集群的状态
MySQL mysql80-01:3306 ssl JS > cluster.status()
{
"clusterName": "NewCluster",
"defaultReplicaSet": {
"name": "default",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"mysql80-01:3306": {
"address": "mysql80-01:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-02:3306": {
"address": "mysql80-02:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
},
"mysql80-03:3306": {
"address": "mysql80-03:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": "applier_queue_applied",
"role": "HA",
"status": "ONLINE",
"version": "8.0.30"
}
},
"topologyMode": "Multi-Primary"
},
"groupInformationSourceMember": "mysql80-01:3306"
}
# 调用函数进行多主-单主的切换cluster.switchToSinglePrimaryMode()
# 切换回单主的时候要指定主实例
MySQL mysql80-01:3306 ssl JS > cluster.switchToSinglePrimaryMode("root@mysql80-01:3306")
Switching cluster 'NewCluster' to Single-Primary mode...
Instance 'mysql80-02:3306' was switched from PRIMARY to SECONDARY.
Instance 'mysql80-03:3306' was switched from PRIMARY to SECONDARY.
Instance 'mysql80-01:3306' remains PRIMARY.
WARNING: Existing connections that expected a R/W connection must be disconnected, i.e. instances that became SECONDARY.
The cluster successfully switched to Single-Primary mode.
MySQL mysql80-01:3306 ssl JS >
# 切换完成之后在查看当前集群状态
单主模式下,如果需要更换新主节点,使用Cluster.setPrimaryInstance()
函数指定一个新的主节点。
# 例如将节点3设置新主
MySQL mysql80-01:3306 ssl JS > cluster.setPrimaryInstance("root@mysql80-03:3306")
Setting instance 'mysql80-03:3306' as the primary instance of cluster 'NewCluster'...
Instance 'mysql80-02:3306' remains SECONDARY.
Instance 'mysql80-03:3306' was switched from SECONDARY to PRIMARY.
Instance 'mysql80-01:3306' was switched from PRIMARY to SECONDARY.
WARNING: The cluster internal session is not the primary member anymore. For cluster management operations please obtain a fresh cluster handle using dba.getCluster().
The instance 'mysql80-03:3306' was successfully elected as primary.
# 可以联机查看或修改集群设置。Cluster.options检查群集的当前设置:
MySQL mysql80-01:3306 ssl JS > cluster.options({all:true})
{
"clusterName": "NewCluster",
"defaultReplicaSet": {
"globalOptions": [
{
"option": "groupName",
"value": "b0f1b807-47c7-11ed-a0ee-000c291aaf5d",
"variable": "group_replication_group_name"
},
{
"option": "memberSslMode",
"value": "REQUIRED",
"variable": "group_replication_ssl_mode"
},
{
"option": "disableClone",
"value": false
},
{
"option": "replicationAllowedHost",
"value": "%"
},
{
"option": "communicationStack",
"value": "MYSQL",
"variable": "group_replication_communication_stack"
}
],
-- 省略部分输出----
}
}
Cluster.setOption(option, value)
用于全局更改所有群集实例的设置或群集全局设置:
# 例子:例如更改集群名
MySQL mysql80-01:3306 ssl JS > cluster.setOption('clusterName','ProductionCluster')
Setting the value of 'clusterName' to 'ProductionCluster' in the Cluster ...
Successfully set the value of 'clusterName' to 'ProductionCluster' in the Cluster: 'NewCluster'.
Cluster.setInstanceOption(instance, option, value)
用于更改各个集群实例的设置:
# 例子:将意外离开集群的实例设置为只读
MySQL mysql80-01:3306 ssl JS > cluster.setInstanceOption('mysql80-02:3306', 'exitStateAction', 'READ_ONLY')
Setting the value of 'exitStateAction' to 'READ_ONLY' in the instance: 'mysql80-02:3306' ...
Successfully set the value of 'exitStateAction' to 'READ_ONLY' in the cluster member: 'mysql80-02:3306'.
#1、登陆节点
mysqlsh -hmysql80-01 -P3306 -uroot -proot
mysqlsh \c "root@mysql80-01:3306"
#2、获取集群
cluster=dba.getCluster();
#3、查看集群状态
cluster.status();
#4、帮助命令
#会列出集群维护的指令
dba.help();
#列出详细指令的用法
dba.help('deploySandboxInstance')
#5、其他常用命令
dba.checkInstanceConfiguration("root@hostname:3306") #检查节点配置实例,用于加入cluster之前
dba.rebootClusterFromCompleteOutage('myCluster'); #重启集群
dba.dropMetadataSchema(); #删除schema
var cluster = dba.getCluster('myCluster') #获取当前集群
cluster.checkInstanceState("root@hostname:3306") #检查cluster里节点状态
cluster.rejoinInstance("root@hostname:3306") #重新加入意外离开集群的节点
addcluster.dissolve({force:true}) #删除集群
cluster.addInstance("root@hostname:3306") #增加节点
cluster.removeInstance("root@hostname:3306") #删除节点
cluster.removeInstance('root@host:3306',{force:true}) #强制删除节点
cluster.dissolve({force:true}) #解散集群
cluster.describe(); #集群描述
#6、停止集群
mysql-js> \sql
mysql-sql> stop group_replication;
#7、关机
# 查看节点
mysql-js> cluster.status()
# 从库停止同步
mysql-js> \sql
mysql-sql> stop group_replication;
Shell> systemctl stop mysqld
# 主库停止
Shell> systemctl stop mysqld
欢迎关注公众号:
一介IT
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。