6. MySQL AdminAPI
本章介绍 MySQL Shell
提供的 MySQL AdminAPI
,它使您能够管理 MySQL
实例,使用它们创建 InnoDB Cluster
、InnoDB ClusterSet
和 InnoDB ReplicaSet
部署,以及集成 MySQL Router
。
6.1. 使用 MySQL AdminAPI
AdminAPI
由 MySQL Shell
提供。 AdminAPI
通过 dba
全局变量及其关联方法进行访问。 dba
变量的方法提供了使您能够部署、配置和管理 InnoDB Cluster
、InnoDB ClusterSet
和 InnoDB ReplicaSet
的操作。例如,使用 dba.createCluster()
方法创建 InnoDB Cluster
。此外,AdminAPI
支持管理一些 MySQL Router
相关任务,例如创建或升级与 InnoDB Cluster
、InnoDB ClusterSet
和 InnoDB ReplicaSet
配合使用的用户帐户。
AdminAPI
支持以下部署场景:
Production Deployment
(生产部署):如果想使用完整的生产环境,需要配置所需数量的服务器,然后将MySQL Server
实例部署到机器上。Sandbox Deployment
(沙箱部署):如果想在提交完整的生产部署之前测试部署过程,AdminAPI
提供的沙盒(sandbox
)功能使您能够在本地计算机上配置测试环境。系统会为您创建具有所需配置的沙箱(sandbox
)服务器实例,以尝试熟悉所使用的技术。AdminAPI
沙箱部署不适合在完整的生产环境中使用。
MySQL Shell
除了原生 SQL
模式外,还提供 JavaScript
和 Python
两种语言模式。在本指南中,MySQL Shell
主要在 JavaScript
模式下使用。 MySQL Shell
启动时默认处于 JavaScript
模式。通过为 JavaScript
模式发出 \js
和为 Python
模式发出 \py
来切换模式。通过发出 \js
确保您处于 JavaScript
模式。
:::alert-info
【Important】
MySQL Shell
可通过套接字连接连接到服务器,但 AdminAPI
需要使用到服务器实例的 TCP
连接。 因此,AdminAPI
不支持基于套接字的连接。
:::
本节假设您熟悉 MySQL Shell
;有关详细信息,请参阅 MySQL Shell 8.0。MySQL Shell
还为 AdminAPI
提供在线帮助。要列出所有可用的 dba
命令,请使用 dba.help()
方法。有关特定方法的在线帮助,请使用通用格式 object.help('methodname')
。例如,使用 JavaScript
:
mysql-js> dba.help('getCluster')
Retrieves a cluster from the Metadata Store.
SYNTAX
dba.getCluster([name][, options])
WHERE
name: Parameter to specify the name of the cluster to be returned.
options: Dictionary with additional options.
...
复制
或者使用 Python
:
mysql-py>dba.help('get_cluster')
NAME
get_cluster - Retrieves a cluster from the Metadata Store.
SYNTAX
dba.get_cluster([name][, options])
WHERE
name: Parameter to specify the name of the cluster to be returned.
options: Dictionary with additional options.
...
复制
除了本文档之外,MySQL Shell JavaScript API
参考或 MySQL Shell Python API
参考中还有所有 AdminAPI
方法的开发人员文档,可从 https://dev.mysql.com/doc/index-connectors.html 获取。
6.2. 安装 MySQL AdminAPI 组件
如何安装 AdminAPI
所需的软件组件取决于计划使用的部署类型:
- 对于生产部署(
Production Deployment
),请将组件安装到每台服务器。生产部署使用多台运行MySQL Server
实例的远程主机,因此需要使用SSH
等工具连接到每台服务器来执行组件安装等任务。 - 对于沙箱部署(
Sandbox Deployment
),请将组件安装到单台服务器上。沙箱部署是在单台服务器本地进行的,因此只需在本地服务器上安装一次。
:::alert-info
【Important】
始终使用可用的最新版本的 MySQL Shell
和 MySQL Router
,并确保其版本 ≥ MySQL Server
版本。较新版本的 MySQL Shell
和 MySQL Router
可以管理较旧版本的 MySQL Server
,但较旧版本的MySQL Shell
和 MySQL Router
产品无法管理较新版本的 MySQL Server
中的功能。
:::
使用以下文档下载并安装软件组件:
MySQL Server
- 请参阅 https://dev.mysql.com/doc/refman/8.0/en/installing.html。MySQL Shell
- 请参阅 安装 MySQL Shell 或 https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-install.htmlMySQL Router
- 请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-installation.html
安装所需的软件后,本节将提供有关使用 AdminAPI
的更多信息。按照步骤设置 第 7 章“MySQL InnoDB Cluster”、第 8 章“MySQL InnoDB ClusterSet”或第 9 章“MySQL InnoDB ReplicaSet”。
6.2.1. 使用运行 MySQL 5.7 的实例
本文档假设您使用运行最新版本 MySQL 8
和 MySQL Shell 8
的 MySQL
实例。AdminAPI
还支持使用运行 MySQL 5.7
的实例,但描述的许多功能需要运行 MySQL 8
的实例。运行 MySQL 5.7
的实例存在以下差异:
- 运行
MySQL Server 5.7
的实例不支持SET PERSIST
,因此与运行MySQL 8
的实例不同,无法远程配置或自动配置MySQL Server 5.7
实例。相反,在配置MySQL Server 5.7
实例时,每次都必须连接到实例并使用dba.configureLocalInstance()
操作。当实例选项文件(my.cnf
)在本地可用时,dba.configureLocalInstance()
操作会将设置保留到实例选项文件中。请参阅 “持久化设置”。 - 运行
MySQL Server 5.7
的实例不支持自动节点配置,因此在将它们加入集群之前,必须手动将它们与其他集群实例同步。这意味着要么依赖组复制的分布式恢复,这需要启用GTID
的Binlog
,并且在有大量事务需要恢复时可能需要很长时间的等待;要么使用MySQL Enterprise Backup
等工具手动复制数据。
随着MySQL Server 8.0
版本中添加MySQL Clone
插件,可以通过AdminAPI
自动配置实例。当添加支持MySQL Clone
的8.0
版本实例时,AdminAPI
会自动选择使加入实例与现有实例同步的最佳方式。例如,如果集群包含大量事务,则直接使用MySQL Clone
来恢复数据,然后使用分布式恢复来同步集群在克隆操作期间处理的事务。可以直接从MySQL Shell
监控操作进度,无需其他工具。这使得添加实例以扩展InnoDB Cluster
和提高高可用性等任务变得毫不费力。有关更多信息,请参阅 第 7.4.6 节“将 MySQL 克隆与 InnoDB 集群结合使用”。 - 运行
MySQL Server 5.7
的实例与InnoDB ReplicaSet
不兼容。 - 运行
MySQL Server 5.7
的实例与InnoDB ClusterSet
不兼容。 - 使用
MySQL Server 5.7
时,InnoDB Cluster
拓扑(无论是在单主模式还是多主模式下运行)无法动态更改。有关更多信息,请参阅 更改集群的拓扑。 - 运行
MySQL Server 5.7
实例与并行复制应用线程不兼容。有关详细信息,请参阅 第 7.5.6 节“配置并行复制应用程序”。 - 运行
MySQL Server 5.7
实例不支持autoRejoinTries
和exitStateAction
选项,这些选项配置实例尝试重新加入集群的次数以及实例离开时会发生什么情况。有关更多信息,请参阅 第 7.5.5 节“配置实例的自动重新加入”。 - 运行
MySQL Server 5.7
实例不支持consistency
选项。有关更多信息,请参阅 第 7.5.4 节“配置故障转移一致性”。 - 运行
MySQL Server 5.7
实例不支持expelTimeout
选项,该选项配置InnoDB Cluster
在驱逐与集群失去联系的实例之前等待的时间。
要使用上述这些功能,请将 MySQL Server
实例升级到 MySQL 8
。
对于运行 MySQL Server 5.7
的实例,请确保在将实例添加到 InnoDB Cluster
之前使用 dba.configureInstance()
以保留配置更改。对于 MySQL Server 5.7
上的非沙箱服务器实例,如果不使用 dba.configureInstance()
操作,MySQL Shell
无法将任何 InnoDB ClusterSet
配置更改保留在 MySQL Server
实例的配置文件中。这会导致以下一种或两种情况:
- 组复制配置不会持久保存在
MySQL Server
实例的配置文件中,并且MySQL Server
重启后,不会重新加入集群。 - 该实例对于
InnoDB Cluster
使用无效。尽管可以使用dba.checkInstanceConfiguration()
验证实例,并且MySQL Shell
会进行所需的配置更改以使实例准备好供InnoDB Cluster
使用,但这些更改不会保留在配置文件中,因此一旦MySQL Server
重新启动就会丢失。
如果这两种情况都发生,则无法使用 dba.rebootClusterFromCompleteOutage()
操作使 InnoDB Cluster
重新联机。这是因为,如果没有 dba.configureInstance()
操作,实例将丢失 MySQL Shell
所做的配置更改,并且由于这些更改未持久化,因此 MySQL Server
实例将恢复到为 InnoDB Cluster
集群进行配置之前的状态。这会导致组复制停止响应,并最终命令超时。
6.2.2. 配置主机名
在生产部署中,使用的 MySQL Server
实例在单独的服务器上运行,因此每台服务器必须具有唯一的主机名,并且能够解析运行 MySQL Server
实例的其他服务器的主机名。可通过如下方式之一实现这些需求:
- 配置每台服务器以将其他服务器的
IP
映射到主机名。有关详细信息,请参阅您的操作系统文档。此配置是推荐的解决方案。 - 设置域名系统 (
DNS
) 服务。 - 将每个
MySQL Server
实例的MySQL
配置中的report_host
变量配置为合适的外部可访问地址。
AdminAPI
支持使用 IP
地址替代主机名。从 MySQL Shell 8.0.18
开始,如果目标 MySQL Server > 8.0.13
,则 AdminAPI
支持 IPv6
地址。
使用 MySQL Shell ≥ 8.0.18
时,如果集群所有 MySQL Server ≥ 8.0.14
,则可以在连接字符串中使用 IPv6
地址或解析为 IPv6
地址的主机名,并使用 localAddress
和 ipAllowlist
等选项(以前的版本仅支持 IPv4
)。有关使用 IPv6
的详细信息,请参阅 https://dev.mysql.com/doc/refman/8.0/en/group-replication-ipv6.html。
要验证是否正确配置了 MySQL Server
的主机名,请处理以下查询。此查询显示 MySQL Server
实例如何向其他服务器报告其地址并尝试使用返回的地址从其他主机连接到该 MySQL Server
:
SELECT coalesce(@@report_host, @@hostname);
复制
6.2.3. 连接到服务器实例
MySQL Shell 使您能够使用各种 API,并支持指定连接,如使用类 URI 字符串或键值对连接到服务器中所述。您可以使用类似 URI 的字符串或键值对来指定连接。 AdminAPI 不支持附加连接参数。本文档演示了使用类似 URI 的连接字符串的 AdminAPI。
MySQL Shell
使你能使用各种 API
,并支持指定连接,如《使用类似 URI 的字符串或“键值对”连接到服务器》》中所述。你可以使用类似 URI
的字符串或 键值对
指定连接。AdminAPI
不支持附加连接参数。本文档演示了使用类 URI
连接字符串的 AdminAPI
。
对于 AdminAPI
操作,只能使用 TCP/IP
和传统的 MySQL
协议连接到 InnoDB Cluster
中的 MySQL Server
实例。AdminAPI
操作不支持使用 Unix
套接字和命名管道(pipes
),并且 AdminAPI
操作不支持使用 X
协议。同样的限制也适用于 MySQL Server
实例本身之间的连接。
:::alert-info
【Note】
- 上述这些限制仅适用于使用
AdminAPI
命令的管理操作以及InnoDB Cluster
中MySQL Server
实例之间的连接。 - 客户端应用程序可以使用
X
协议、Unix
套接字和命名管道来连接到InnoDB Cluster
集群中的实例。
:::
如,要以用户 myuser
身份连接到位于 www.example.com
的 MySQL Server
实例(端口:3306
),请使用连接字符串:
myuser@www.example.com:3306
复制
要将此连接字符串与 AdminAPI
操作(如 dba.configureInstance()
)一起使用,需要用单引号 ('
) 或双引号 ("
) 括住连接字符串。
如果使用 JavaScript
的 AdminAPI
实现:
mysql-js> > dba.configureInstance('myuser@www.example.com:3306')
复制
如果使用 Python
的 AdminAPI
实现:
mysql-py> dba.configure_instance('myuser@www.example.com:3306')
复制
如果在默认交互模式下运行 MySQL Shell
,系统会提示输入密码。AdminAPI
支持 MySQL Shell
的 第 4.4 节“可插入密码存储”,一旦存储了用于连接到 MySQL Server
实例的密码,系统将不再提示输入该密码。
MySQL Shell
默认尝试 X
协议连接到 MySQL Server
实例。如果在为 AdminAPI
操作建立连接时未指定连接类型,则 MySQL Shell
的自动协议检测会在创建传统的 MySQL
协议会话之前短暂创建 X
协议会话。
除非您使用 MySQL Router
管理的端口连接到仅具有两个辅助(只读)实例的 InnoDB Cluster
,否则该行为不会产生任何影响。在这种情况下,两个实例之间的负载均衡无法正确管理,并且始终使用同一实例。为了避免这种副作用,可以通过添加 --mc
或 --mysql
选项来显式指定传统的 MySQL
协议会话。
当一台或多台 MySQL Server
确实无法访问时,打开多个服务器连接的某些操作可能需要很长时间才能执行,例如 cluster.status()
命令。连接超时可能无法提供足够的时间进行响应。
从 MySQL Shell 8.0.28
开始,可以使用 MySQL Shell
配置选项 dba.connectTimeout
为使用 AdminAPI
的会话设置默认连接超时时长(以秒为单位)。
6.2.4. 持久化设置
用于处理 InnoDB Cluster
、InnoDB ClusterSet
、InnoDB ReplicaSet
以及这些部署中的各个成员服务器实例的 AdminAPI
命令会修改实例上的 MySQL Server
配置。根据 MySQL Shell
连接实例的方式以及实例上的 MySQL Server
的版本,这些配置更改可以自动持久化到 MySQL Server
实例。
通过对 MySQL Server
实例进行持久化设置,可以确保实例重启后,配置更改得以保留。有关背景信息,请参阅 SET PERSIST。这种持久化对于可靠使用至关重要。如,如果配置更改不是持久化的,则添加到 InnoDB Cluster
集群的实例在重启后不会重新加入 InnoDB Cluster
集群,因为配置更改会丢失。
满足以下要求的 MySQL Server
实例支持自动持久化配置更改:
- 实例需运行
MySQL Server ≥ 8.0.11
。 persisted_globals_load
设置为ON
。- 实例尚未使用
--no-defaults
选项启动。
不满足上述要求的 MySQL Server
实例不支持自动持久化配置更改,并且当 AdminAPI
操作导致 MySQL Server
实例设置更改被持久化时,会收到警告,如:
WARNING: On instance 'localhost:3320' membership change cannot be persisted since MySQL version 5.7.21 does not support the SET PERSIST command (MySQL version >= 8.0.5 required). Please use the <Dba>.configureLocalInstance command locally to persist the changes.
复制
当针对当前运行 MySQL Shell
的 MySQL Server
实例(即本地实例)发出 AdminAPI
命令时,MySQL Shell
会将配置更改直接保留到该实例(持久化到 mysqld-auto.cnf
文件),并且配置更改不需要任何进一步的步骤。
必须在不支持自动持久化配置更改的本地实例上用 dba.configureLocalInstance()
进行本地更改。有关更多信息,请参阅使用 dba.configureLocalInstance() 配置实例。
当针对远程 MySQL Server
实例(换句话说,当前正在运行 MySQL Shell
的实例以外的实例)运行时,如果该实例支持自动持久化保存配置更改,则 AdminAPI
命令会将配置更改持久保存到 MySQL Server
实例的 mysql-auto.conf
选项文件中。
如果远程 MySQL Server
实例不支持自动持久化配置更改,则 AdminAPI
命令无法自动配置实例的选项文件。因此,AdminAPI
命令可以从 MySQL Server
实例中读取信息,如显示当前配置。但对配置的更改无法持久保存到该远程 MySQL Server
实例的选项文件中。在这种情况下,需要在本地保存更改。有关更多信息,请参阅使用 dba.configureLocalInstance() 配置实例。
6.3. 获取 Handler 对象
当使用 AdminAPI
时,使用代表 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
的处理(handler
)对象。可以将此对象分配给一个变量,然后使用可用的操作来监视和管理 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
。
要获取处理(handler
)对象,请建立与属于 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
的活动实例之一的连接。如,当使用 dba.createCluster()
创建 InnoDB Cluster
集群时,该操作会返回一个可以分配给变量的 Cluster
对象。可以使用此处理(handler
)对象来管理 InnoDB Cluster
集群。如,添加实例或检查集群的状态。如果想在以后再次获取 Cluster
对象,如在重启 MySQL Shell
后,可使用 dba.getCluster([name],[options])
函数。如,在 JavaScript
模式中可以这样:
mysql-js> var cluster1 = dba.getCluster()
复制
在 Python
模式中可以这样:
mysql-py> cluster1 = dba.get_cluster()
复制
要获取表示 InnoDB ClusterSet
部署的 ClusterSet
对象,请使用 dba.getClusterSet()
或 cluster.getClusterSet()
函数。如,在 JavaScript
模式中可以这样:
mysql-js> myclusterset = dba.getClusterSet()
复制
在 Python
模式中可以这样:
mysql-py> myclusterset = dba.get_cluster_set()
复制
:::alert-info
【Note】
当使用 ClusterSet
对象时,从中获取它的 MySQL Server
实例必须在 InnoDB ClusterSet
中仍然在线。如果该 MySQL Server
实例离线,该对象将不再工作,需要从 InnoDB ClusterSet
中仍然在线的 MySQL Server
再次获取它。
:::
使用 dba.getReplicaSet()
获取 ReplicaSet
对象。如,在 JavaScript
模式中可以这样:
mysql-js> var replicaset1 = dba.getReplicaSet()
复制
在 Python
模式中可以这样:
mysql-py> replicaset1 = dba.get_replica_set()
复制
如果不指定名称,则返回默认对象。返回的对象返回一个独立于 MySQL Shell
全局会话的新会话。这可以确保,如果更改 MySQL Shell
全局会话,Cluster
、ClusterSet
或 ReplicaSet
对象仍会维护其与 MySQL Server
实例的会话连接。
默认情况下,当获取处理(handler
)对象时,MySQL Shell
会尝试连接到 Primary
实例。可设置 connectToPrimary
选项来配置此行为。
- 如果
connectToPrimary=true
,并且活动的MySQL Shell
全局会话不是Primary
实例,则MySQL Shell
会查询Primary
实例。如果集群中没有仲裁,则操作失败。 - 如果
connectToPrimary=false
,则获取的处理(handler
)对象使用为活动会话指定的MySQL Server
实例。换句话说,与当前的MySQL Shell
全局会话相同的实例。 - 如果未指定
connectToPrimary
,MySQL Shell
会将connectToPrimary
视为true
;如果连接失败,并回退到connectToPrimary
为false
。
要强制连接到辅助实例,请建立到辅助实例的连接,并通过在 JavaScript
中发出以下命令来使用 connectToPrimary:false
选项:
mysql-js> shell.connect(secondary_member)
mysql-js> var cluster1 = dba.getCluster(testCluster, {connectToPrimary:false})
WARNING: You are connected to an instance in state 'Read Only'
Write operations on the InnoDB cluster will not be allowed.
<Cluster:testCluster>
复制
或者,通过在 Python
中发出以下命令:
mysql-py> shell.connect(secondary_member)
mysql-py> cluster1 = dba.get_cluster(testCluster, connectToPrimary='false')
WARNING: You are connected to an instance in state 'Read Only'
Write operations on the InnoDB cluster will not be allowed.
<Cluster:testCluster>
复制
:::alert-info
【Note】
辅助实例的 super_read_only=ON
,因此无法向它们写入更改。
:::
6.4. 为 AdminAPI 创建用户帐户
用于配置和管理 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
部署中的成员 MySQL Server
实例的用户账户,除了需要具有完整的 MySQL administrator
权限(如 SUPER, GRANT OPTION, CREATE, DROP
等)之外,还需要对元数据表(metadata tables
)具有完整的读写权限。详见 https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html。
为此,可以使用 MySQL Server
上的 root
帐户,但如果这样做,部署中每个成员服务器上的 root
帐户必须具有相同的密码。出于安全原因,不建议使用 root
帐户。
相反,推荐的方法是使用 AdminAPI
的 JavaScript
模式的 dba.configureInstance()
和 cluster.setupAdminAccount()
操作来设置用户帐户。这些操作接受的用户名格式遵循标准 MySQL
帐户名格式,请参阅 https://dev.mysql.com/doc/refman/8.0/en/account-names.html。
如果希望设置用户帐户,则 手动配置 InnoDB Cluster 管理员帐户 中列出了所需的权限。如果仅需要读取操作(如出于监控目的),可以使用具有受限权限的帐户,如本主题中详细介绍的。
:::alert-info
【Important】
用于配置或管理 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
部署的每个帐户必须存在于部署中的所有成员 MySQL Server
实例上,并且具有相同的用户名和密码。
:::
6.4.1. Server 服务器配置账户
要加入 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
部署的每个 MySQL Server
实例上都需要一个服务器配置帐户。可以使用 JavaScript
模式的 dba.configureInstance()
命令或 Python
模式的 dba.configure_instance()
命令以及 clusterAdmin
选项来设置此帐户。
为了获得更好的安全性,请在交互式提示符处指定密码,否则可使用 clusterAdminPassword
选项指定密码。在将成为部署一部分的每个 MySQL Server
实例(连接以创建部署的实例以及之后将加入的实例)上以相同的方式使用相同的用户名和密码创建相同的帐户。
可以使用 clusterAdminPasswordExpiration
选项定义密码过期时间。此选项可以设置为天数、NEVER
永不过期或 DEFAULT
以使用系统默认值。
如果使用 SSL
证书进行身份验证,则可以分别使用 clusterAdminCertIssuer
和 clusterAdminCertSubject
选项添加证书颁发者(issuer
)和 subject
。
使用 dba.configureInstance()
操作创建的服务器配置帐户不会复制到 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
部署中的其他成员服务器。MySQL Shell
自动禁用 dba.configureInstance()
操作的 Binlog
记录。因此,必须在每个 MySQL Server
实例上单独创建帐户。
clusterAdmin
选项必须与基于用户的 MySQL Shell
连接一起使用,该用户必须有权创建具有适当权限的用户。在此 JavaScript
示例中,使用 root
用户:
mysql-js> dba.configureInstance('root@ic-1:3306', {clusterAdmin: "'icadmin'@'ic-1%'"});
复制
同样,在此 Python
示例中使用 root
用户:
mysql-py> dba.configure_instance('root@ic-1:3306', clusterAdmin="'icadmin'@'ic-1%'");
复制
6.4.2. Administrator 管理员帐户
https://dev.mysql.com/doc/mysql-shell/8.0/en/creating-user-accounts-for-admin-api.html
完成配置过程后,管理员帐户(可设置多个)可用于管理部署。要创建管理员帐户,请在将所有实例添加到 InnoDB Cluster
或 InnoDB ReplicaSet
后,在 JavaScript
模式中发出 cluster.setupAdminAccount()
命令。或者在 Python
模式发出命令:<Cluster>setup_admin_account()
。
该命令使用指定的用户名和密码创建一个帐户,并具有所有必需的权限。使用 cluster.setupAdminAccount()
创建帐户的事务将写入 Binlog
并发送到集群中的所有其他 MySQL Server
实例以在其上创建帐户。
要使用 setupAdminAccount()
操作,必须以具有创建用户权限的 MySQL
用户(如 root
)身份连接。setupAdminAccount(user)
还可以在执行 dba.upgradeMetadata()
( JavaScript
模式)或 dba.upgrade_metadata()
( Python
模式)操作之前,升级现有 MySQL
帐户以使其具有必要的权限。
setupAdminAccount()
的必选参数是 user
,其接受的用户名格式遵循标准 MySQL
帐户名格式( username[@host]
)其中 host
是可选的,如果未提供,则默认为 %
通配符。有关详细信息,请参阅 https://dev.mysql.com/doc/refman/8.0/en/account-names.html。
如,创建名为 icadmin
的用户来管理分配给变量 myCluster
的 InnoDB Cluster
,在 JavaScript
模式中,可以这样:
mysql-js> myCluster.setupAdminAccount('icadmin')
Missing the password for new account icadmin@%. Please provide one.
Password for new account: ********
Confirm password: ********
Creating user icadmin@%.
Setting user password.
Account icadmin@% was successfully created.
复制
或在 Python
模式中,可以这样:
mysql-py> myCluster.setup_admin_account('icadmin')
Missing the password for new account icadmin@%. Please provide one.
Password for new account: ********
Confirm password: ********
Creating user icadmin@%.
Setting user password.
Account icadmin@% was successfully created.
复制
从 MySQL Shell 8.0.33
开始,setupAdminAccount()
中添加了以下选项:
- requireCertIssuer:帐户的可选
SSL
证书颁发者(issuer
)。 - requireCertSubject:帐户的可选 SSL 证书主题(
Subject
)。 - passwordExpiration: numberOfDays | Never | Default:帐户的密码过期设置。
:::alert-info
【Note】
如果设置了 requireCertIssuer
或 requireCertSubject
或两者,则现有密码将变为可选。
:::
6.4.3. 更新旧帐户
如果有使用 MySQL Shell < 8.0.20
创建的服务器配置帐户或管理员帐户,请使用 setupAdminAccount()
操作的 update
选项来升级现有用户的权限。这是与升级相关的,以确保用户帐户的兼容性。如,要使用 JavaScript
升级名为 icadmin
的用户,请发出:
mysql-js> myCluster.setupAdminAccount('icadmin', {'update':1})
Updating user icadmin@%.
Account icadmin@% was successfully updated.
复制
或者使用 Python
:
mysql-py> myCluster.setup_admin_account('icadmin',update=1})
Updating user icadmin@%.
Account icadmin@% was successfully updated.
复制
这是 cluster.setupAdminAccount()
命令的特殊用途,不会写入 Binlog
。而 Administrator 管理员帐户 中的创建管理员操作,会写入 Binlog
。
6.5. 详细日志记录
在进行生产部署时,为 MySQL Shell
配置详细日志记录会很有用。如,日志中的信息可以帮助查找并解决在准备 MySQL Server
实例作为 InnoDB Cluster
的一部分时,可能出现的问题。要以详细日志记录级别启动 MySQL Shell
,请使用 --log-level
选项:
$> mysqlsh --log-level=DEBUG3
复制
推荐使用 DEBUG3
级别。有关更多信息,请参阅 https://dev.mysql.com/doc/mysql-shell/8.0/en/mysqlsh.html#option_mysqlsh_log-level。当设置 DEBUG3
时,MySQL Shell
日志文件包含诸如 Debug:execute_sql( ... )
之类的行,其中包含作为每个 AdminAPI
调用的一部分执行的 SQL
查询。对于基于 Unix
的系统,MySQL Shell
生成的日志文件位于 ~/.mysqlsh/mysqlsh.log
中;在 Microsoft Windows
系统上,它位于 %APPDATA%\MySQL\mysqlsh\mysqlsh.log
中。有关更多信息,请参阅 第 12 章,MySQL Shell 日志记录和调试。
除了启用 MySQL Shell
日志级别之外,还可以配置 AdminAPI
在发出每个命令后在 MySQL Shell
中提供的输出量。要启用 AdminAPI
输出量,请在 MySQL Shell
中发出:
mysql-js> dba.verbose=2
复制
这样就能从 AdminAPI
调用中获得最大输出。可用的输出级别有
0
或OFF
为默认值:这提供了最小的输出,并且是不进行故障排除时的推荐级别。1
或ON
:添加每次调用 AdminAPI 的详细输出。2
将调试输出添加到详细输出中,提供有关每次调用执行AdminAPI
的完整信息。
MySQL Shell
可以选择记录 AdminAPI
操作使用的 SQL
语句(沙箱操作除外),并且还可以在执行时在终端中显示它们。有关更多信息,请参阅 第 12.5 节“记录 AdminAPI 操作”。
6.6. 查找 Primary
当使用单主 InnoDB Cluster
或 InnoDB ReplicaSet
时,需要连接到 Primary
实例来执行管理任务,以便将配置更改写入元数据。要查找当前的 Primary
实例,可以:
-
在
MySQL Shell
启动时使用--redirect-primary
选项可确保目标服务器是InnoDB Cluster
或InnoDB ReplicaSet
的一部分。如果目标实例不是Primary
实例,MySQL Shell
会找到Primary
实例实例并连接到它。 -
使用
shell.connectToPrimary([instance, password])
操作(8.0.20
版本中添加),该操作检查目标MySQL Server
实例是否属于InnoDB Cluster
或InnoDB ReplicaSet
:- 如果属于
InnoDB Cluster
或InnoDB ReplicaSet
,则MySQL Shell
将打开一个到Primary
实例的新会话,将活动的MySQL Shell
全局会话设置为已建立的会话并将其返回。 - 如果不属于
InnoDB Cluster
或InnoDB ReplicaSet
,则shell.connectToPrimary([instance, password])
操作将失败并提示错误。 - 如果未提供
instance
,则shell.connectToPrimary([instance, password])
操作将尝试使用活动的MySQL Shell
全局会话。 - 如果未提供
instance
且没有活动的MySQL Shell
全局会话,则会引发异常。
- 如果属于
-
使用
cluster.status()
操作,在结果中找到Primary
实例,然后手动连接到该实例。
https://dev.mysql.com/doc/mysql-shell/8.0/en/admin-api-connect-primary.html
6.7. 编写 AdminAPI 脚本
除了本节中介绍的交互模式外,MySQL Shell
还支持以批处理模式运行脚本。这使您能使用 AdminAPI
以及用 JavaScript
或 Python
编写的脚本来自动化处理,这些脚本可以使用 MySQL Shell
的 --file
选项运行。例如:
$> mysqlsh --file setup-innodb-cluster.js
复制
在脚本文件名(如上述代码中的 setup-innodb-cluster.js
)后指定的命令行选项会传递给脚本,而不会传递给 MySQL Shell
。可以使用 JavaScript
中的 os.argv
数组或 Python
中的 sys.argv
列表访问这些选项。在这两种情况下,数组中的第 1
个选项为脚本名称。
此处显示了使用 JavaScript
的示例脚本文件的内容:
print('InnoDB Cluster sandbox set up\n');
print('==================================\n');
print('Setting up a MySQL InnoDB Cluster with 3 MySQL Server sandbox instances,\n');
print('installed in ~/mysql-sandboxes, running on ports 3310, 3320 and 3330.\n\n');
var dbPass = shell.prompt('Please enter a password for the MySQL root account: ', {type:"password"});
try {
print('\nDeploying the sandbox instances.');
dba.deploySandboxInstance(3310, {password: dbPass});
print('.');
dba.deploySandboxInstance(3320, {password: dbPass});
print('.');
dba.deploySandboxInstance(3330, {password: dbPass});
print('.\nSandbox instances deployed successfully.\n\n');
print('Setting up InnoDB Cluster...\n');
shell.connect('root@localhost:3310', dbPass);
var cluster = dba.createCluster("prodCluster");
print('Adding instances to the Cluster.');
cluster.addInstance({user: "root", host: "localhost", port: 3320, password: dbPass});
print('.');
cluster.addInstance({user: "root", host: "localhost", port: 3330, password: dbPass});
print('.\nInstances successfully added to the Cluster.');
print('\nInnoDB Cluster deployed successfully.\n');
} catch(e) {
print('\nThe InnoDB Cluster could not be created.\n\nError: ' +
+ e.message + '\n');
}
复制
或者使用 Python
:
print('InnoDB Cluster sandbox set up\n');
print('==================================\n');
print('Setting up a MySQL InnoDB Cluster with 3 MySQL Server sandbox instances,\n');
print('installed in ~/mysql-sandboxes, running on ports 3310, 3320 and 3330.\n\n');
dbPass = shell.prompt('Please enter a password for the MySQL root account: ', type ="password");
try:
print('\nDeploying the sandbox instances.');
dba.deploy_sandbox_instance(3310, password = dbPass);
print('.');
dba.deploy_sandbox_instance(3320, password = dbPass);
print('.');
dba.deploy_sandbox_instance(3330, password = dbPass);
print('.\nSandbox instances deployed successfully.\n\n');
print('Setting up InnoDB Cluster...\n');
shell.connect('root@localhost:3310', dbPass);
cluster = dba.create_cluster("prodCluster");
print('Adding instances to the Cluster.');
cluster.add_instance('root@localhost:3320', password = dbPass);
print('.');
cluster.add_instance('root@localhost:3330', password = dbPass);
print('.\nInstances successfully added to the Cluster.');
print('\nInnoDB Cluster deployed successfully.\n');
except ValueError:
print('\nThe InnoDB Cluster could not be created.\n\nError.\n');
复制
6.7.1. AdminAPI 命令行集成
MySQL Shell
的 第 5.8 节“API 命令行集成” 也支持 AdminAPI
。此命令行集成使您能够轻松地将 AdminAPI
集成到您的环境中。如,要使用监听端口 1234
的沙箱实例检查 InnoDB Cluster
的状态:
$ mysqlsh root@localhost:1234 -- cluster status
复制
该命令将对应于 MySQL Shell
中的如下命令:
mysql-js> cluster.status()
复制
6.8. AdminAPI MySQL 沙箱
本节介绍如何使用 AdminAPI
设置沙箱部署。部署和使用 MySQL
的本地沙箱实例是开始探索 AdminAPI
的好方法。可以先在本地测试功能,然后再部署到生产服务器中。AdminAPI
内置有创建沙箱实例的功能,这些沙箱实例经过正确配置,可应用于本地场景 InnoDB Cluster
、InnoDB ClusterSet
和 InnoDB ReplicaSet
的部署。
:::alert-info
【Important】
沙箱实例仅适合在本地服务器上部署和运行,以用于测试目的。在生产环境中,MySQL Server
实例需单独部署到网络上的每台服务器中。有关更多信息,请参阅 第 7.4 节“部署生产 InnoDB 集群”。
:::
生产部署中,需要通过连接字符串为 MySQL Shell
指定连接的目标 MySQL Server
实例。而在沙箱部署中,沙箱实例在与运行 MySQL Shell
的同一台本地主机上运行,连接沙箱实例只需为 MySQL Shell
提供 MySQL
沙箱实例的端口号即可。
~]$ mysqlsh -h localhost -uroot
MySQL localhost JS > dba.deploySandboxInstance(4401)
复制
6.8.1. 部署 Sandbox 沙箱实例
AdminAPI
提供了 dba.deploySandboxInstance(port_number)
操作,而不是使用生产设置(每个实例在单独的主机上运行)。port_number
参数是 MySQL Server
实例的 TCP
监听端口。要新部署一个绑定到 3310
端口的沙箱实例,请执行以下操作
mysql-js> dba.deploySandboxInstance(3310)
复制
默认情况下,对于 Unix
系统,沙箱创建在名为 $HOME/mysql-sandboxes/port
的目录中。对于 Microsoft Windows
系统,创建在名为 %userprofile%\MySQL\mysql-sandboxes/port
的目录中。每个沙箱实例都存储在以 port_number
命名的目录中。
系统会提示输入 MySQL Server
实例的 root
用户密码。
:::alert-info
【Important】
每个沙箱实例都使用 root
用户和密码,而且所有应一起工作的沙箱实例都必须使用相同的用户和密码。在生产中不建议这样做。
:::
要部署另一个沙盒服务器实例,请重复 3310
端口沙箱实例的步骤,为每个 MySQL Server
实例选择不同的端口号。
要更改存储沙箱的目录,如为了测试目的在一台主机上运行多个沙箱,请使用 MySQL Shell sandboxDir
选项。如,要使用 /home/user/sandbox1
目录中的沙箱,请发出:
mysql-js> shell.options.sandboxDir='/home/user/sandbox1'
复制
然后,针对 /home/user/sandbox1
中找到的沙箱实例执行所有后续与沙箱相关的操作。
当部署沙箱时,MySQL Shell
会搜索 mysqld
执行文件,然后使用它来创建沙箱实例。可以通过配置 PATH
环境变量来配置 MySQL Shell
搜索 mysqld
执行文件的路径。这对于在将新版本的 MySQL
部署到生产环境之前在本地测试它非常有用。如,要在路径 /home/user/mysql-latest/bin/
中使用 mysqld
执行文件,请执行以下操作:
export PATH=/home/user/mysql-latest/bin/:$PATH
复制
然后,从设置了 PATH
环境变量的终端运行 MySQL Shell
。部署的任何沙箱都将使用在配置路径下找到的 mysqld
执行文件。
dba.deploySandboxInstance()
操作支持以下选项:
allowRootFrom
:配置root
用户可以从哪个主机进行连接。默认为root@%
。ignoreSslError
:在沙箱实例上配置安全连接。当ignoreSslError
为true
(默认值)时,如果无法提供SSL
支持并且在不支持SSL
的情况下部署服务器实例,则在操作期间不会发出错误。当ignoreSslError
设置为false
时,沙箱实例将部署为支持SSL
,如果无法配置SSL
支持,则会发出错误。mysqldOptions
:在沙箱实例上配置其他选项。默认为空字符串,并接受指定选项和值的字符串列表。如mysqldOptions: ["lower_case_table_names=1", "report_host="10.1.2.3"]}
。指定的选项将写入沙箱实例的选项文件(my.cnf
)中。portX
:配置用于X
协议连接的端口。默认值为port × 10
得出的。该值是1024 - 65535
之间的整数。
6.8.2. 管理 Sandbox 沙箱实例
沙箱实例运行后,可以使用以下命令随时更改其状态。指定实例的端口号,以便识别:
dba.stopSandboxInstance(instance)
:使用JavaScript
优雅地停止沙箱实例。dba.stop_sandbox_instance(instance)
:使用Python
优雅地停止沙箱实例。dba.startSandboxInstance(instance)
:使用JavaScript
启动沙箱实例。dba.start_sandbox_instance(instance)
:使用Python
启动沙箱实例。dba.killSandboxInstance(instance)
:使用JavaScript
终止(kill
)沙箱实例。在模拟意外停止时很有用。dba.kill_sandbox_instance(instance)
:使用Python
终止(kill
)沙箱实例。在模拟意外停止时很有用。dba.deleteSandboxInstance(instance)
:使用JavaScript
删除沙箱实例,将从文件系统中完全删除沙箱实例。dba.delete_SandboxInstance(instance)
:使用Python
删除沙箱实例,将从文件系统中完全删除沙箱实例。
沙箱实例被认为是临时性的,并非为生产使用而设计。因此,不支持版本升级。在沙箱部署中,每个沙箱实例都使用本地 mysql-sandboxes
目录中 $PATH
下的 mysqld
执行文件的副本。如果 mysqld
的版本发生变化,如升级后,基于先前版本的沙箱将无法启动。这是因为与 basedir
下找到的依赖项相比,沙箱执行文件已经过时。
如果希望在 mysqld
升级后保留沙箱实例,一种变通方法是手动将升级后的 mysqld
执行文件复制到每个沙箱的 bin
目录中。然后,发出 dba.startSandboxInstance()
命令启动沙箱。该操作会因超时而失败,错误日志中包含:
2020-03-26T11:43:12.969131Z 5 [System] [MY-013381] [Server] Server upgrade from '80019' to '80020' started. 2020-03-26T11:44:03.543082Z 5 [System] [MY-013381] [Server] Server upgrade from '80019' to '80020' completed.
复制
虽然操作似乎因超时而失败,但沙箱已成功启动。
6.8.3. 设置 InnoDB Cluster 与 MySQL Router
在以下示例中,我们使用沙箱部署和 AdminAPI
来部署具有 MySQL Router
的 InnoDB Cluster
,完成以下任务。
部署和使用 MySQL
的本地沙箱实例可以在部署到生产环境之前,在本地测试功能。AdminAPI
内置了创建沙箱实例的功能,这些沙箱实例经过预先配置,可用于 InnoDB Cluster
、InnoDB ClusterSet
和 InnoDB ReplicaSet
的本地部署场景中。
该示例包含以下部分:
- 组件安装
- 创建
InnoDB Cluster
- 引导
MySQL Router
- 测试
MySQL Router
配置
6.8.3.1. 组件安装
安装以下组件:
MySQL Server
:有关详细信息,请参阅 https://dev.mysql.com/doc/refman/8.0/en/installing.htmlMySQL Shell
:有关更多信息,请参阅 安装 MySQL Shell 或 https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-install.htmlMySQL Router
:有关更多信息,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-installation.html
6.8.3.2. 创建 InnoDB Cluster 沙箱配置
为了提供对一次故障的容错能力,请创建一个包含 3
个实例的 InnoDB Cluster
。在本例中,将使用运行在同一台机器上的 3
个沙箱实例。在生产部署中,这 3
个实例将运行在的不同主机上。
- 要启动
MySQL Shell
,请发出:
> mysqlsh
复制
- 要创建并启动
MySQL
沙箱实例,请使用X AdminAPI
中的dba.deploySandboxInstance()
函数。在MySQL Shell
中发出以下3
个语句并为每个实例输入root
密码(对所有实例使用相同的root
密码):
mysql-js> dba.deploySandboxInstance(3310)
mysql-js> dba.deploySandboxInstance(3320)
mysql-js> dba.deploySandboxInstance(3330)
复制
6.8.3.3. 创建 InnoDB Cluster
要创建 InnoDB Cluster
,请完成以下步骤:
- 通过发出以下命令连接到想要成为
InnoDB Cluster
中Primary
实例的MySQL
实例:
mysql-js> shell.connect('root@localhost:3310')
复制
- 发出
dba.createCluster()
命令来创建InnoDB Cluster
,并使用分配的变量cluster
保存返回的值:
mysql-js> cluster = dba.createCluster('devCluster')
复制
该命令的输出如下:
A new InnoDB cluster will be created on instance 'localhost:3310'.
Validating instance configuration at localhost:3310...
NOTE: Instance detected as a sandbox.
Please note that sandbox instances are only suitable for deploying test clusters for use within the same host.
This instance reports its own address as 127.0.0.1:3310
Instance configuration is suitable.
NOTE: Group Replication will communicate with other members using '127.0.0.1:33101'.
Use the localAddress option to override.
Creating InnoDB cluster 'devCluster' on '127.0.0.1:3310'...
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:devCluster>
复制
- 通过使用分配的变量
cluster
和cluster.status()
函数,验证创建是否成功:
mysql-js> cluster.status()
复制
输出内容如下:
{
“clusterName”: “devCluster”,
“defaultReplicaSet”: {
"name": "default",
"primary": "127.0.0.1:3310",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures.",
"topology": {
"127.0.0.1:3310": {
"address": "127.0.0.1:3310",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
}
},
"topologyMode": "Single-Primary"
}, “groupInformationSourceMember”:
“127.0.0.1:3310” }
复制
InnoDB Cluster
已启动并正在运行,但尚不能容忍故障。使用<Cluster>.addInstance()
函数将另1
个MySQL Server
实例添加到集群中:
mysql-js> cluster.addInstance('root@localhost:3320')
NOTE: The target instance '127.0.0.1:3320' 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 '127.0.0.1:3320' 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):
mysql-js> cluster.addInstance('root@localhost:3330')
复制
- 从提示中选择恢复方法。选项有:
- Clone:克隆要添加到
Primary
集群的实例,删除该实例已包含的所有事务。MySQL Clone
插件会自动安装。如果要添加空实例(未处理任何事务)或包含不希望保留的事务的实例,请选择克隆选项。 - Incremental recovery:使用异步复制将集群已处理的所有事务恢复到新加入的实例。如果确定
InnoDB Cluster
处理的所有更新都是在启用GTID
的情况下完成的,则适合使用增量恢复。没有被清除的事务,并且新实例包含与InnoDB Cluster
相同的GTID
集或其子集。
在此示例中,选择C
代表Clone
:
- Clone:克隆要添加到
Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): C
Validating instance configuration at localhost:3320...
NOTE: Instance detected as a sandbox.
Please note that sandbox instances are only suitable for deploying test clusters for
use within the same host.
This instance reports its own address as 127.0.0.1:3320
Instance configuration is suitable.
NOTE: Group Replication will communicate with other members using '127.0.0.1:33201'.
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: 127.0.0.1:3320 is being cloned from 127.0.0.1:3310
** Stage DROP DATA: Completed
** Clone Transfer
FILE COPY ############################################################ 100% Completed
PAGE COPY ############################################################ 100% Completed
REDO COPY ############################################################ 100% Completed
NOTE: 127.0.0.1:3320 is shutting down...
* Waiting for server restart... ready
* 127.0.0.1:3320 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 '127.0.0.1:3320'
The instance '127.0.0.1:3320' was successfully added to the cluster.
复制
- 添加创建的第
3
个实例,并再次选择C
代表Clone
恢复方法:
mysql-js> cluster.addInstance('root@localhost:3330')
复制
- 通过发出
cluster.status()
命令检查集群的状态:
mysql-js> cluster.status()
复制
输出如下:
{
"clusterName": "devCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "127.0.0.1:3310",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"127.0.0.1:3310": {
"address": "127.0.0.1:3310",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
},
"127.0.0.1:3320": {
"address": "127.0.0.1:3320",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
},
"127.0.0.1:3330": {
"address": "127.0.0.1:3330",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "127.0.0.1:3310"
}
The setup of the InnoDB Cluster was successful!
复制
- 集群现在最多可以容忍
1
次故障。通过发出\q
命令退出MySQL Shell
。
6.8.3.4. 引导 MySQL Router
MySQL InnoDB Cluster
搭建完成后,测试 Cluster
的高可用性。为此,请使用 MySQL Router
。如果 1
个实例发生故障,MySQL Router
会自动更新其路由配置,并确保新的连接被路由到其它实例。
在 MySQL Router
执行路由操作之前,请使其了解新的 InnoDB Cluster
。为此,请使用 –bootstrap
选项并将 MySQL Router
指向 InnoDB Cluster
的当前 R/W MySQL Server
实例(Primary
实例)。使用 -d
选项将路由配置存储在名为 mysql-router
的文件夹中。
- 在
home
目录中打开终端:- 在 Linux 系统上,发出:
[demo-user@losthost]$> mysqlrouter --bootstrap root@localhost:3310 -d mysqlrouter
- 在 Windows 系统上,发出:
C:\Users\demo-user> mysqlrouter --bootstrap root@localhost:3310 -d mysql-router
然后,MySQL Router
打印将用于路由连接的TCP/IP
端口。有关更多信息,请参阅 https://dev.mysql.com/doc/mysql-shell/8.0/en/admin-api-deploy-router.html。
- 在 Linux 系统上,发出:
- 成功配置
MySQL Router
后,在后台线程中启动它:- 在使用 systemd 的 Linux 系统上,发出:
systemctl start mysqlrouter.service
- 在 Linux 系统上,调用之前创建的 mysqlrouter 文件夹中的 Shell 脚本:
/mysqlrouter/start.sh
- 在 Windows 系统上,使用 start /B 命令并将路由器指向使用 –bootstrap 选项生成的配置文件:
C:\> start /B mysqlrouter -c %HOMEPATH%\mysql-router\mysqlrouter.conf
- 调用之前创建的 mysqlrouter 文件夹中的 Windows PowerShell 脚本:
\mysqlrouter\start.ps1
- 在使用 systemd 的 Linux 系统上,发出:
6.8.3.5. 测试 MySQL Router 配置
现在 InnoDB Cluster
和 MySQL Router
正在运行,测试集群设置。不要直接连接到 MySQL Server
实例,而是通过 MySQL Router
进行连接。
- 发出以下连接命令,提供
root
密码以连接到InnoDB Cluster
:
> mysqlsh root@localhost:6446
复制
- 通过创建变量
cluster
并为其分配dba.getCluster()
的返回值,来检查InnoDB Cluster
的状态:
mysql-js> cluster = dba.getCluster() mysql-js> cluster.status()
复制
- 切换到
SQL
模式
mysql-js> \sql
复制
- 通过发出以下命令来查询实例正在运行的端口:
mysql-sql> SELECT @@port;
+--------+
| @@port |
+--------+
| 3310 |
+--------+
1 row in set (0.0007 sec)
复制
- 使用
dba.killSandboxInstance()
函数,强制停止MySQL Server
实例以模拟意外停止:
mysql-sql> \js
mysql-js> dba.killSandboxInstance(3310)
Killing MySQL instance...
Instance localhost:3310 successfully killed.
复制
- 通过对刚刚被杀死的实例运行
SELECT @@port
命令,检查MySQL Router
是否正确路由流量,并检查结果:
mysql-js> \sql mysql-sql> SELECT @@port;
复制
-
返回错误;
ERROR: 2013 (HY000): Lost connection to MySQL server during query.
。此错误意味着在端口3310
上运行的实例不再运行。 -
再次检查端口:
mysql-sql> SELECT @@port;
+--------+
| @@port |
+--------+
| 3320 |
+--------+
复制
此输出显示在端口 3320
上运行的实例已提升为新的 R/W Primary
实例。
- 返回
JavaScript
模式,查看Cluster
状态:
mysql-js> cluster.status()
{
"clusterName": "devCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "127.0.0.1:3320",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures. 1 member is not active.",
"topology": {
"127.0.0.1:3310": {
"address": "127.0.0.1:3310",
"memberRole": "SECONDARY",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"shellConnectError": "MySQL Error 2003: Could not open connection to '127.0.0.1:3310':
Can't connect to MySQL server on '127.0.0.1:3310' (10061)",
"status": "(MISSING)"
},
"127.0.0.1:3320": {
"address": "127.0.0.1:3320",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
},
"127.0.0.1:3330": {
"address": "127.0.0.1:3330",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "127.0.0.1:3320"
}
复制
在 3310
端口正式运行的 MySQL Server
实例为 MISSING
状态。
- 通过使用端口号发出
dba.startSandboxInstance()
操作来重启3310
实例:
mysql-js> dba.startSandboxInstance(3310)
复制
- 检查
InnoDB Cluster
的状态显示该实例已在集群中恢复为活动状态,但作为SECONDARY
成员:
mysql-js > cluster.status()
{
"clusterName": "devCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "127.0.0.1:3320",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"127.0.0.1:3310": {
"address": "127.0.0.1:3310",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
},
"127.0.0.1:3320": {
"address": "127.0.0.1:3320",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
},
"127.0.0.1:3330": {
"address": "127.0.0.1:3330",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.28"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "127.0.0.1:3320"
}
复制
所有实例均已恢复 ONLINE
,并且 InnoDB Cluster
可以再次容忍 1
次故障。
6.9. 标记元数据
从版本 8.0.21
开始,可配置的标记框架可用于为 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
的元数据标记附加信息。标签(Tags
)可以将自定义的 键值对
与 Cluster
、ReplicaSet
或实例相关联。标签已保留,以供 MySQL Router
使用,使兼容的 MySQL Router
能够支持对应用程序隐藏 MySQL Server
实例。为此目的,可保留以下标签(tags
):
hidden
:指示MySQL Router
,从客户端应用程序的可能目标列表中排除MySQL Server
实例。_disconnect_existing_sessions_when_hidden
:指示MySQL Router
断开与标记为hidden
的MySQL Server
实例的现有连接。
有关更多信息,请参阅 从路由中移除实例
此外,标签框架是用户可配置的。自定义标签可以由任何 ASCII
字符组成,并提供一个命名空间,该命名空间可用作字典 键值对
,可与 InnoDB Cluster
、InnoDB ReplicaSet
或其特定实例相关联。标签值可以是任何 JSON
值。通过此种配置,使您可以在元数据上添加自己的属性。
6.9.1. 显示标签
Cluster.options()
操作显示有关分配给集群各个实例以及集群本身的标签的信息。如,分配给 myCluster
的 InnoDB Cluster
可能会显示:
mysql-js> myCluster.options()
{
"cluster": {
"name": "test1",
"tags": {
"ic-1:3306": [
{
"option": "_disconnect_existing_sessions_when_hidden",
"value": true
},
{
"option": "_hidden",
"value": false
}
],
"ic-2:3306": [],
"ic-3:3306": [],
"global": [
{
"option": "location:",
"value": "US East"
}
]
}
}
}
复制
该集群有一个名为 location
的全局标签,其值为 US East
,并且实例 ic-1
已被标记。
6.9.2. 为集群实例设置标签
可以在实例级别设置标签,如,将实例标记为不可用,以便应用程序和 MySQL Router
将其视为 OFFLINE
。使用 Cluster.setInstanceOption(instance, option, value)
操作设置实例的标签值。
参数 instance
是目标 MySQL Server
实例的连接字符串。参数 option
必须是一个字符串,格式为 namespace: option
。参数 value
是指定命名空间中应分配给 option
的值。如果值为空,则会从指定的命名空间中删除该选项。对于属于集群的实例,setInstanceOption()
操作只接受标签名称空间。任何其他命名空间都会导致 ArgumentError
(参数错误)。
如,要使用 JavaScript
将 myCluster
实例 ic-1
上的标签 test
设置为 true
,请发出:
mysql-js> myCluster.setInstanceOption("icadmin@ic-1:3306", "tag:test", true);
复制
或者使用 Python
在 myCluster
实例 ic-1
上将标签 test
设置为 true
,发出:
mysql-py> myCluster.set_instance_option("icadmin@ic-1:3306", "tag:test", True);
复制
6.9.3. 从路由中移除实例
当 AdminAPI
与 MySQL Router
协同工作时,它们支持特定的标记,使你能够将实例标记为 hidden
,并从路由(routing
)中移除它们。这样,MySQL Router
就会从路由目标候选列表中排除此类标记实例。此功能可让你在执行服务器升级或配置更改等维护任务时,安全地将 MySQL Server
实例脱机,以便应用程序和 MySQL Router
忽略它。
当 _hidden
标记设置为 true
时,这将指示 MySQL Router
从客户端应用程序的可用目标列表中排除该实例。实例仍然在线,但不会接收新的路由连接。_disconnect_existing_sessions_when_hidden
标签控制着如何关闭与实例的现有连接。假定此标记为 true
,它会指示根据 InnoDB Cluster
、InnoDB ClusterSet
或 InnoDB ReplicaSet
引导的 MySQL Router
实例在 _hidden
标记为 true
时,断开与实例的现有连接。当 _disconnect_existing_sessions_when_hidden
为 false
时,如果 _hidden
为 true
,则不会关闭与实例的现有客户端连接。保留的 _hidden
和 _disconnect_existing_sessions_when_hidden
标签是特定于实例的标签,不能在集群级别使用。
:::alert-danger
【Warning】
当启用 MySQL Router
选项 use_gr_notifications
(默认为 60
秒)时,意味着当设置标签时,MySQL Router
最多需要 60
秒才能检测到更改。要减少等待时间,请将 use_gr_notifications
更改为较低的值。
:::
如,假设想要从路由目标中删除 ic-1
实例,该实例是分配给 myCluster
的 InnoDB Cluster
的一部分。在 JavaScript
中使用 setInstanceOption()
操作启用 _hidden
和 _disconnect_existing_sessions_when_hidden
标签:
mysql-js> myCluster.setInstanceOption("icadmin@ic-1:3306", "tag:_hidden", true);
复制
或者使用 Python
模式的 set_instance_option()
操作启用 _hidden
和 _disconnect_existing_sessions_when_hidden
标签:
mysql-py> myCluster.set_instance_option("icadmin@ic-1:3306", "tag:_hidden", true);
复制
可以通过检查选项来验证元数据中的更改。例如,对 ic-1
所做的更改将在选项中显示为:
mysql-js> myCluster.options()
{
"cluster": {
"name": "test1",
"tags": {
"ic-1:3306": [
{
"option": "_disconnect_existing_sessions_when_hidden",
"value": true
},
{
"option": "_hidden",
"value": true
}
],
"ic-2:3306": [],
"ic-3:3306": [],
"global": []
}
}
}
复制
可以通过查看日志文件来验证 MySQL Router
是否已检测到元数据中的更改。检测到对 ic-1
所做更改的 MySQL Router
将显示如下更改:
2020-07-03 16:32:16 metadata_cache INFO [7fa9d164c700] Potential changes detected in cluster 'testCluster' after metadata refresh 2020-07-03 16:32:16 metadata_cache INFO [7fa9d164c700] view_id = 4, (3 members) 2020-07-03 16:32:16 metadata_cache INFO [7fa9d164c700] ic-1:3306 / 33060 - mode=RW 2020-07-03 16:32:16 metadata_cache INFO [7fa9d164c700] ic-1:3306 / 33060 - mode=RO 2020-07-03 16:32:16 metadata_cache INFO [7fa9d164c700] ic-1:3306 / 33060 - mode=RO hidden=yes disconnect_when_hidden=yes 2020-07-03 16:32:16 routing INFO [7fa9d164c700] Routing routing:testCluster_x_ro listening on 64470 got request to disconnect invalid connections: metadata change 2020-07-03 16:32:16 routing INFO [7fa9d164c700] Routing routing:testCluster_x_rw listening on 64460 got request to disconnect invalid connections: metadata change 2020-07-03 16:32:16 routing INFO [7fa9d164c700] Routing routing:testCluster_rw listening on 6446 got request to disconnect invalid connections: metadata change 2020-07-03 16:32:16 routing INFO [7fa9d164c700] Routing routing:testCluster_ro listening on 6447 got request to disconnect invalid connections: metadata change
复制
要使 MySQL Server
实例重新上线,请使用 setInstanceOption()
操作删除标签,MySQL Router
会自动将实例添加回路由目的列表中。例如:
mysql-js> myCluster.setInstanceOption(icadmin@ic-1:3306, "tag:_hidden", false);
复制
通过再次检查选项来验证元数据的更改:
mysql-js> myCluster.options()
{
"cluster": {
"name": "test1",
"tags": {
"ic-1:3306": [
{
"option": "_disconnect_existing_sessions_when_hidden",
"value": true
},
{
"option": "_hidden",
"value": false
}
],
"ic-2:3306": [],
"ic-3:3306": [],
"global": []
}
}
}
复制
从 8.0.32
开始,还可以使用以下命令输出(hiddenFromRouter
属性) 查看成员的 _hidden
状态:
Cluster.status()
Cluster.describe()
ReplicaSet.status()
对于使用_hidden
元数据标记对MySQL Router
流量隐藏的成员,hiddenFromRouter
属性为true
。
6.9.4. 设置集群标签
通过 Cluster.setOption(option, value)
操作,可以更改整个集群的命名空间选项值。参数 option
必须是一个格式为“namespace: option
”的字符串。
参数 value
是在指定的 namespace
中分配给 option
的值。如果值为空,则从指定的 namespace
中删除该选项。对于集群,setOption()
操作接受 tag
命名空间。其他命名空间都会导致 ArgumentError
(参数错误)。
:::alert-danger
【Tips】
在集群级别设置的标签不会覆盖在实例级别设置的标签。不能使用 Cluster.setOption()
删除在实例级别设置的所有标签。
:::
不要求所有实例都在线,只要集群有仲裁即可。要通过 myCluster
变量将 InnoDB Cluster
的 location
标签设置为 US East
,请在 JavaScript
中发出以下命令:
mysql-js> myCluster.setOption("tag:location", "US East")
mysql-js> myCluster.options()
{
"cluster": {
"name": "test1",
"tags": {
"ic-1:3306": [],
"ic-2:3306": [],
"ic-3:3306": [],
"global": [
{
"option": "location:",
"value": "US East"
}
]
}
}
}
复制
或者在 Python
中发出以下命令:
mysql-py> myCluster.set_option("tag:location", "US East")
mysql-pys> myCluster.options()
{
"cluster": {
"name": "test1",
"tags": {
"ic-1:3306": [],
"ic-2:3306": [],
"ic-3:3306": [],
"global": [
{
"option": "location:",
"value": "US East"
}
]
}
}
}
复制
6.9.5. 从集群中删除标签
要从 Cluster
中删除标签,请使用 Cluster.setOption(option, value)
操作,并在 JavaScript
中使用 null
值,在 Python
中使用 None
值。
要通过 myCluster
变量,从 InnoDB Cluster
中删除 location
标签,请在 JavaScript
中发出以下命令:
mysql-js> myCluster.setOption("tag:location", null)
复制
或使用 Python
:
mysql-js> myCluster.set_option("tag:location", None)
复制
6.9.6. 用户定义的标签
AdminAPI
支持标签命名空间,你可以将信息存储在与给定 Cluster
、ReplicaSet
或实例相关的 键值对
中。tag
命名空间下的选项不受限制,也就是说,标签可以是任何有效的 MySQL ASCII
标识符。
可以为 tag
标签使用任何名称和值,只要名称遵循以下语法即可: _
或字母后跟字母、数字和 _
字符。
namespace
选项是一个格式为冒号分隔的字符串,如 namespace: option
。其中,namespace
是命名空间的名称,option
是实际的选项名称。可以在实例级别、Cluster
、ReplicaSet
设置和删除标签。
标签(tag
)名称可以是任何值,只要以字母或下划线开头,后面可选择字母、数字和 _
字符,例如,^[a-zA-Z_][0-9a-zA-Z_]*
。只有内置标签允许以下划线 _
字符开头。
如何使用自定义标签取决于您。您可以在 Cluster
上设置自定义标签来标记 Cluster
的位置。如,在 Cluster
上设置名为 location
且值为 EMEA
的自定义标签。
6.10. 结合 AdminAPI、InnoDB Cluster 和 InnoDB ReplicaSet 使用 MySQL Router
本节介绍如何将 MySQL Router
与 InnoDB Cluster
和 InnoDB ReplicaSet
集成。有关将 MySQL Router
与 InnoDB ClusterSet
集成的说明,请参阅 “将 MySQL Router 与 InnoDB ClusterSet 集成”。
有关 MySQL Router
的背景信息,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/。
6.10.1. 引导 MySQL Router
可以针对 InnoDB ReplicaSet
或 InnoDB Cluster
引导 MySQL Router
以自动配置路由。bootstrap
进程是运行 MySQL Router
的一种特定方式,它不会启动通常的路由,而是根据元数据配置mysqlrouter.conf
文件。
要在命令行引导 MySQL Router
,请在启动 mysqlrouter
命令时传入 --bootstrap
选项,它会从元数据中检索拓扑信息并配置到 MySQL Server
实例的路由连接。或者,在 Windows
上使用 MySQL
安装程序来引导 MySQL Router
。有关更多信息,请参阅 https://dev.mysql.com/doc/refman/8.0/en/mysql-installer-workflow.html#mysql-installer-workflow-nonserver-products。
一旦 MySQL Router
启动,客户端应用程序就会连接到 MySQL Router
发布的端口。MySQL Router
根据传入端口自动将客户端连接重定向到 MySQL Server
实例。如默认使用 6646
用于使用传统 MySQL
协议的读写连接。
如果拓扑发生变化,如 MySQL Server
实例意外故障,MySQL Router
会检测到变化并自动调整到其余 MySQL Server
实例的路由。这种自动调整消除了客户端应用程序处理故障转移或了解底层拓扑的需要。有关更多信息,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-innodb-cluster.html。
:::alert-danger
【Note】
不要手动配置 MySQL Router
以重定向到 MySQL Server
实例。始终使用 --bootstrap
选项,可以确保 MySQL Router
从元数据中获取配置。请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-general-metadata.html。
:::
6.10.2. 配置 MySQL Router 用户
当 MySQL Router
连接到 Cluster
、ClusterSet
或 ReplicaSet
时,它需要具有正确权限的用户帐户。从 MySQL Router ≥ 8.0.19
,可以使用 --account
选项指定此内部用户。在以前的版本中,MySQL Router
在每次引导集群时创建内部帐户,这可能会导致随着时间的推移建立许多帐户。从 MySQL Shell ≥ 8.0.20
,可以使用 AdminAPI
设置 MySQL Router
所需的用户帐户。
使用 setupRouterAccount(user, [options])
操作创建 MySQL
用户帐户或升级现有帐户,以便 MySQL Router
使用它来操作 InnoDB Cluster
或 InnoDB ReplicaSet
。这是使用 InnoDB Cluster
和 InnoDB ReplicaSet
配置 MySQL Router
的推荐方法。
要将名为 myRouter1
的新 MySQL Router
帐户添加到变量 testCluster
引用的 InnoDB Cluster
,请发出:
mysqlsh> testCluster.setupRouterAccount('myRouter1')
复制
在此情况下,未指定域,因此使用通配符 (%
) 字符创建帐户,这确保创建的用户可以从任何区域进行连接。要将帐户限制为只能从 example.com
域进行连接,请在 JavaScript
中发出:
mysql-js> testCluster.setupRouterAccount('myRouter1@example.com')
复制
或者使用 Python
:
mysql-py> testCluster.setup_router_account('myRouter1@example.com')
复制
该操作会提示输入密码,然后为 MySQL Router
用户设置正确的权限。如果 InnoDB Cluster
或 InnoDB ReplicaSet
有多个 MySQL Server
实例,则创建的 MySQL Router
用户将传播到所有 MySQL Server
实例。
当已经配置了 MySQL Router
用户时,例如使用的是 8.0.20
之前的版本,可以在执行 setupRouterAccount()
操作时,指定 update
选项为 true
以重新配置现有用户。如,要重新配置 myOldRouter
用户,请在 JavaScript
中发出以下命令:
mysql-js> testCluster.setupRouterAccount('myOldRouter', {'update':1})
复制
或者使用 Python
:
mysql-py> testCluster.setup_router_account('myOldRouter', {'update':1})
复制
还可以使用字典选项 {password: "newPassword", update: 1}
更新 MySQL Router
用户的密码。以下 JavaScript
示例将 MySQL Router
用户 myRouter1
的密码更新为 newPassword1#
:
mysql-js> testCluster.setupRouterAccount('myRouter1', {password: "newPassword1#",'update':1})
复制
或者使用 Python
:
mysql-py> testCluster.setup_router_account('myRouter1', {password: "newPassword1#", 'update':1})
复制
从 MySQL Shell ≥ 8.0.33
,支持 SSL
证书。 setupRouterAccount()
中添加了以下选项:
requireCertIssuer
:帐户的可选SSL
证书颁发者(issuer
)。requireCertSubject
:帐户的可选SSL
证书主题(subject
)。passwordExpiration: numberOfDays | Never | Default
:帐户的密码有效期设置。numberOfDays
:密码有效期的天数。Never
:密码永不过期Default
:使用系统默认值
6.10.3. 部署 MySQL Router
建议将 MySQL Router
部署在与应用程序相同的主机上。使用沙箱部署时,所有内容都在单个主机上运行。因此,将 MySQL Router
部署到同一主机。使用生产部署时,建议将一个 MySQL Router
实例部署到用于托管客户端应用程序之一的每台计算机。还可以将 MySQL Router
部署到应用程序实例连接的公用计算机上。有关更多信息,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-installation.html。
要引导基于 InnoDB Cluster
或 InnoDB ReplicaSet
的 MySQL Router
,需要指向在线实例的类 URI
连接字符串。运行 mysqlrouter
命令并提供 --bootstrap=instance
选项,其中,instance
是指向在线实例的类 URI
连接字符串。 MySQL Router
连接到 MySQL Server
实例,并使用附带的元数据缓存插件来检索元数据,其中包含 MySQL Server
实例地址及其角色的列表。如:
$ mysqlrouter --bootstrap icadmin@ic-1:3306 --account=mysqlrouter
复制
系统会提示输入 MySQL Router
使用的实例密码和加密密钥。该密钥用于加密 MySQL Router
连接集群时使用的实例密码。还会显示可用于客户端连接的端口。有关其他引导相关选项,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysqlrouter.html#mysql-router-command-options-bootstrap。
:::alert-info
【Tips】
此时 MySQL Router
尚未启动,因此无法路由连接。引导是一个单独的过程。
:::
MySQL Router
引导进程创建一个 mysqlrouter.conf
文件,其设置基于从传递给 --bootstrap
选项的地址(上例中的 icadmin@ic-1:3306
)中获取的元数据 。根据检索到的元数据,MySQL Router
自动配置 mysqlrouter.conf
文件,包括 metadata_cache
部分。
如果使用 MySQL Router ≥ 8.0.14
,--bootstrap
选项自动配置 MySQL Router
跟踪并存储由 dynamic_state
配置的 MySQL
元数据服务器地址。这确保了当 MySQL Router
重新启动时,知道哪些 MySQL
元数据服务器地址是当前的。有关更多信息,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-conf-options.html#option_mysqlrouter_dynamic_state。
在早期的 MySQL Router
版本中,元数据服务器信息是在 MySQL Router
的初始引导操作过程中定义的,并以 bootstrap_server_addresses
的形式静态存储在配置文件中,其中包含集群中所有 MySQL Server
实例的地址。如:
[metadata_cache:prodCluster]
router_id=1
bootstrap_server_addresses=mysql://icadmin@ic-1:3306,mysql://icadmin@ic-2:3306,mysql://icadmin@ic-3:3306
user=mysql_router1_jy95yozko3k2
metadata_cluster=prodCluster
ttl=300
复制
:::alert-info
【Tips】
如果使用 MySQL Router ≥ 8.0.13
,当在引导 MySQL Router
后通过添加另一个 MySQL Server
实例来更改集群的拓扑时,需要根据更新的元数据更新 bootstrap_server_addresses
。使用 --bootstrap
选项重新启动 MySQL Router
,或者手动编辑 mysqlrouter.conf
文件的 bootstrap_server_addresses
部分并重新启动 MySQL Router
。
:::
生成的 MySQL Router
配置会创建用于连接到集群的 TCP
端口。默认情况下,会创建使用传统 MySQL
协议和 X
协议与集群通信的端口。要使用 X
协议,MySQL Server
实例必须安装并配置 X
插件,这是 MySQL ≥ 8.0
的默认设置。默认可用的 TCP
端口是:
- 6446 - 用于传统的
MySQL
协议读写会话,MySQL Router
将传入的连接重定向到Primary
服务器实例。 - 6447 - 用于传统的
MySQL
协议只读会话,MySQL Router
将传入的连接重定向到辅助(secondary
)服务器实例之一。 - 64460 - 用于
X
协议读写会话,MySQL Router
将传入的连接重定向到Primary
服务器实例。 - 64470 - 对于
X
协议只读会话,MySQL Router
将传入的连接重定向到辅助(secondary
)服务器实例之一。
如果使用了 --conf-base-port
选项或 group_replication_single_primary_mode
变量,端口号可能与上述不同。当启动 MySQL Router
时会列出确切的端口。
连接的重定向方式取决于所使用的底层拓扑。如,当使用单主集群时,默认情况下 MySQL Router
会发布 X
协议和传统 MySQL
协议端口,客户端连接到该端口进行读写会话,并重定向到单主集群。对于多主集群,读写会话会以轮询方式重定向到 Primary
实例之一。如,到端口 6446
的第 1
个连接被重定向到 ic-1
实例。到端口 6446
的第 2
个连接被重定向到 ic-2
实例,依此类推。
对于传入的只读连接,MySQL Router
也会以循环方式将连接重定向到辅助实例之一。要修改此行为,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-conf-options.html#option_mysqlrouter_routing_strategy。
引导并配置后,启动 MySQL Router
。如果你使用 --bootstrap
选项进行全系统安装,则发出:
$> mysqlrouter &
复制
如果使用 --directory
选项将 MySQL Router
安装到特定目录,请在安装目录中找到的 start.sh
脚本。
或者设置一个服务以在系统启动时自动启动 MySQL Router
,请参阅 https://dev.mysql.com/doc/mysql-router/8.0/en/mysql-router-server-starting.html。现在,可以将 MySQL
客户端(如 MySQL Shell
)连接到 MySQL Router
端口之一(如上所述),并查看客户端如何透明地连接到 MySQL Server
实例之一。
$> mysqlsh --uri root@localhost:6442
复制
要验证连接到哪个 MySQL Server
实例,请针对状态变量 port
发出 SQL
查询。例如:
mysql-js> \sql
Switching to SQL mode... Commands end with ;
mysql-sql> select @@port;
+--------+
| @@port |
+--------+
| 3310 |
+--------+
复制
或者,使用:
mysql-js> \sql
Switching to SQL mode... Commands end with ;
mysql-sql> SHOW VARIABLES WHERE Variable_name = 'port';
+--------+
| @@port |
+--------+
| 3310 |
+--------+
复制
6.10.4. 将 ReplicaSet 与 MySQL Router 一起使用
可以使用 MySQL Router ≥ 8.0.19
来针对 InnoDB ReplicaSet
进行引导。有关更多信息,请参阅 “将 MySQL Router 与 AdminAPI、InnoDB Cluster 和 InnoDB ReplicaSet 一起使用”。生成的 MySQL Router
配置文件的唯一区别是添加了 cluster_type
选项。当 MySQL Router
针对 ReplicaSet
进行引导时,生成的配置文件包括:
cluster_type=rs
复制
当将 MySQL Router
与 InnoDB ReplicaSet
一起使用时,请注意:
MySQL Router
的读写端口将客户端连接重定向到ReplicaSet
的Primary
实例。MySQL Router
的只读端口将客户端连接重定向到ReplicaSet
的辅助实例,尽管它也可以将它们定向到Primary
实例。MySQL Router
从Primary
实例获取有关ReplicaSet
拓扑的信息。- 当
Primary
实例不可用时,MySQL Router
会自动将不同的辅助实例提升为新的Primay
实例。
可以使用与 InnoDB Cluster
相同的方式使用针对 ReplicaSet
引导的 MySQL Router
实例。有关 ReplicaSet.listRouters()
和 ReplicaSet.removeRouterMetadata()
的更多信息,请参阅 “使用集群的 Router”第 6.10.6 节 。
6.10.5. 测试 InnoDB Cluster 高可用
要测试 InnoDB Cluster
高可用性是否有效,请通过终止(kill
)实例来模拟意外停止。InnoDB Cluster
检测到 MySQL Server
实例离开集群并重新配置自身。InnoDB Cluster
如何重新配置自身取决于使用的是单主集群还是多主集群,以及 MySQL Server
实例在集群中扮演的角色。
-
单主模式:详见 https://dev.mysql.com/doc/refman/8.0/en/group-replication-single-primary-mode.html
- 如果当前
Primary
实例离开InnoDB Cluster
,则辅助实例之一将被选为新的Primary
实例,实例按最低server_uuid
优先。MySQL Router
将读写连接重定向到新选出的Primary
节点。 - 如果当前辅助节点离开
InnoDB Cluster
,MySQL Router
会停止将只读连接重定向到该实例。
- 如果当前
-
多主模式:详见 https://dev.mysql.com/doc/refman/8.0/en/group-replication-multi-primary-mode.html
- 如果当前的“
R/W
”实例离开InnoDB Cluster
,MySQL Router
会将读写连接重定向到其他Primary
实例。如果离开的实例是InnoDB Cluster
中最后一个Primary
实例,则InnoDB Cluster
完全消失,无法连接到任何MySQL Router
端口。
- 如果当前的“
有多种方法可以模拟 MySQL Server
实例离开集群,如,可以强制停止 MySQL Server
,或者在沙箱部署时使用 AdminAPI dba.killSandboxInstance()
杀掉沙箱实例。在此示例中,有一个包含 3
个服务器实例的单主沙箱集群部署,并且端口 3310
的实例是当前的 Primary
实例。通过杀死 3310
实例来模拟实例意外离开集群:
在 JavaScript
模式,执行如下命令:
mysql-js> dba.killSandboxInstance(3310)
复制
或在 Python
模式,执行如下命令:
mysql-py> dba.kill_sandbox_instance(3310)
复制
InnoDB Cluster
检测到更改并自动选择新的 Primary
节点。
假设会话连接到端口 6446
(默认的读写传统 MySQL
协议端口),MySQL Router
在检测到集群拓扑变化时,将会话重定向到新选举的 Primary
节点。要验证这一点,请将 MySQL Shell
切换到 SQL
模式,然后查看状态变量 port
以检查您的会话已重定向到哪个实例。
mysql-js> \sql
Switching to SQL mode... Commands end with ;
mysql-sql> SELECT @@port;
ERROR: 2013 (HY000): Lost connection to MySQL server during query
The global session got disconnected.
Attempting to reconnect to 'root@localhost:6446'...
The global session was successfully reconnected.
mysql-sql> SELECT @@port;
+--------+
| @@port |
+--------+
| 3330 |
+--------+
1 row in set (0.00 sec)
复制
第 1
个 SELECT
语句失败,因为与原来的 Primay
数据库连接丢失,这意味着当前会话已关闭。 MySQL Shell
会自动重新连接,当再次发出命令时,新端口将得到确认。
在此示例中,端口 3330
的实例已被选为新的 Primay
实例。这次选举表明 InnoDB Cluster
已经提供了自动故障转移,并且 MySQL Router
已经自动将连接重定向到新的 Primay
实例,具有高可用性。
6.10.6. 使用 Cluster’s Routers
可以针对 InnoDB Cluster
或 InnoDB ReplicaSet
引导多个 MySQL Router
实例。从版本 8.0.19
开始,要显示所有已注册的 MySQL Router
实例列表,请发出:
mysql-js> Cluster.listRouters()
{
"clusterName": "example",
"routers": {
"ic-1:3306": {
"hostname": "ic-1:3306",
"lastCheckIn": "2020-01-16 11:43:45",
"roPort": 6447,
"roXPort": 64470,
"rwPort": 6446,
"rwXPort": 64460,
"version": "8.0.19"
}
}
}
复制
结果提供有关每个注册的 MySQL Router
实例的信息,如元数据中的名称、主机名、端口等。也可发出以下 Python
命令:
mysql-py> Cluster.list_routers()
{
"clusterName": "example",
"routers": {
"ic-1:3306": {
"hostname": "ic-1:3306",
"lastCheckIn": "2020-01-16 11:43:45",
"roPort": 6447,
"roXPort": 64470,
"rwPort": 6446,
"rwXPort": 64460,
"version": "8.0.19"
}
}
}
复制
返回的信息显示:
MySQL Router
实例的名称。- 最后签入(
lastCheckIn
)时间戳,由存储在元数据中的MySQL Router
的定期ping
生成。 - 运行
MySQL Router
实例的主机名。 - 传统协议的只读(
roPort
)和读写(rwPort
)端口。MySQL Router
为传统MySQL
协议连接发布这些端口。 X
协议的只读(roXPort
)和读写(rwXPort
)端口,MySQL Router
为X
协议连接发布这些端口。- 此
MySQL Router
实例的版本。MySQL Router ≥ 8.0.19
中增加了返回版本的支持。MySQL Router < 8.0.19
中,则version
字段为空。
此外,Cluster.listRouters()
操作可以显示不支持 MySQL Shell
支持的元数据版本的实例列表。使用 onlyUpgradeRequired
选项。如,通过发出 Cluster.listRouters({'onlyUpgradeRequired':'true'})
。
返回的列表仅显示在 InnoDB Cluster
中注册的 MySQL Router
实例,这些实例需要升级其元数据。有关更多信息,请参阅 “升级 Metadata Schema”。
MySQL Router
实例不会自动从元数据中删除,因此,当引导更多实例时,InnoDB Cluster
元数据会包含越来越多的实例引用。要从 InnoDB Cluster
的元数据中删除已注册的 MySQL Router
实例,请使用版本 8.0.19
中添加的 Cluster.removeRouterMetadata(router)
操作。
使用 Cluster.listRouters()
操作获取要删除的 MySQL Router
实例的名称,并将其作为 router
参数传入 Cluster.removeRouterMetadata()
。如,假设向 InnoDB Cluster
注册的 MySQL Router
实例是:
mysql-js> Cluster.listRouters(){
"clusterName": "testCluster",
"routers": {
"myRouter1": {
"hostname": "example1.com",
"lastCheckIn": null,
"routerId": "1",
"roPort": "6447",
"rwPort": "6446"
"version": null
},
"myRouter2": {
"hostname": "example2.com",
"lastCheckIn": "2019-11-27 16:25:00",
"routerId": "3",
"roPort": "6447",
"rwPort": "6446"
"version": "8.0.19"
}
}
}
复制
基于名为“myRouter1
”的实例的“lastCheckIn
”和“version
”为空这一事实。通过发出以下 JavaScript
命令从元数据中删除此旧实例:
mysql-js> cluster.removeRouterMetadata('myRouter1')
复制
或者,发出以下 Python
命令:
mysql-py> cluster.remove_router_metadata('myRouter1')
复制
通过从 InnoDB Cluster
元数据中删除指定的 MySQL Router
实例,将其从 InnoDB Cluster
中取消 MySQL Router
实例的注册。
6.11. 升级 Metadata Schema
随着 AdminAPI
的发展,某些版本可能要求您升级现有 InnoDB ClusterSet
、InnoDB ReplicaSet
和 InnoDB Cluster
的元数据,以确保它们与较新版本的 MySQL Shell
兼容。如,8.0.19
版本中增加了InnoDB ReplicaSet
,意味着元数据架构已经升级到 2.0
版本。无论是否计划使用 InnoDB ReplicaSet
,要在使用早期版本的 MySQL Shell
部署的集群中使用 MySQL Shell ≥ 8.0.19
,必须升级集群的元数据。
:::alert-danger
【Warning】
如果不升级元数据,将无法使用 MySQL Shell
更改使用早期版本创建的集群的配置。如,只能对集群执行读取操作,例如:
Cluster.status()
Cluster.describe()
Cluster.options()
:::
dba.upgradeMetadata()
操作将 MySQL Shell
当前连接到的 ClusterSet
、ReplicaSet
或 InnoDB Cluster
上找到的 Metadata Schema
版本与该 MySQL Shell
支持的 Metadata Schema
版本进行比较。如果发现的 Metadata
版本较低,则启动升级过程。然后 dba.upgradeMetadata()
函数会更新自动创建的 MySQL Router
用户以获得正确的权限。手动创建的名称不以 mysql_router_
开头的 MySQL Router
用户不会自动升级。这是升级 ClusterSet
、ReplicaSet
或 InnoDB Cluster
的重要一步,只有这样才能升级 MySQL Router
元数据。要查看有关哪些注册到 ClusterSet
、ReplicaSet
或 Cluster
的 MySQL Router
实例需要元数据升级的信息,请使用 .listRouters()
函数。如,要列出与集群关联的 MySQL Router
实例,请通过变量 cluster
获取:
cluster.listRouters({'onlyUpgradeRequired':'true'})
{
"clusterName": "mycluster",
"routers": {
"example.com::": {
"hostname": "example.com",
"lastCheckIn": "2019-11-26 10:10:37",
"roPort": 6447,
"roXPort": 64470,
"rwPort": 6446,
"rwXPort": 64460,
"version": "8.0.18"
}
}
}
复制
在此示例中, listRouters()
函数的 onlyUpgradeRequired
选项,指定只有支持旧版本 Metadata Schema
且需要升级的 MySQL Router
实例才会包含在返回的 JSON
对象中。
要升级 ClusterSet
、ReplicaSet
或 Cluster
的元数据,请将 MySQL Shell
的全局会话连接到 ClusterSet
、ReplicaSet
或 Cluster
,并使用 dba.upgradeMetadata()
操作将 ClusterSet``、ReplicaSet
或 Cluster
的元数据升级到新版本。例如:
mysql-js> shell.connect('user@example.com:3306')
mysql-js> dba.upgradeMetadata()
InnoDB Cluster Metadata Upgrade
The cluster you are connected to is using an outdated metadata schema version
1.0.1 and needs to be upgraded to 2.0.0.
Without doing this upgrade, no AdminAPI calls except read only operations will
be allowed.
The grants for the MySQL Router accounts that were created automatically when
bootstrapping need to be updated to match the new metadata version's
requirements.
Updating router accounts...
NOTE: 2 router accounts have been updated.
Upgrading metadata at 'example.com:3306' from version 1.0.1 to version 2.0.0.
Creating backup of the metadata schema...
Step 1 of 1: upgrading from 1.0.1 to 2.0.0...
Removing metadata backup...
Upgrade process successfully finished, metadata schema is now on version 2.0.0
复制
如果已安装的元数据版本较低,则启动升级过程。
dba.upgradeMetadata()
函数接受以下选项:
dryRun
:是否试运行元数据升级。如果设置为true
,则dba.upgradeMetadata()
函数会确定是否需要元数据升级或恢复并通知您,而无需实际执行操作。interactive
:是否已交互模式执行元数据升级。默认值等于MySQL Shell
向导模式。
如果遇到与 ClusterSet
、ReplicaSet
或 Cluster
管理用户缺少权限相关的错误,请使用带有 update
选项的 .setupAdminAccount()
操作来授予用户正确的权限:
- 创建或升级具有管理
InnoDB Cluster
所需权限的MySQL
用户帐户:<Cluster>.setupAdminAccount(user, options)
。参阅 InnoDB Cluster 管理员账户 - 创建或升级具有管理
InnoDB ReplicaSet
所需权限的MySQL
用户帐户:<ReplicaSet>.setupAdminAccount(user, options)
6.12. AdminAPI 的锁机制
从版本 8.0.33
开始,AdminAPI
将 8.0.20
中为 InnoDB ReplicaSet
引入的锁定机制扩展到 InnoDB Cluster
和 InnoDB ClusterSet
。
以前,MySQL Shell
的不同实例可以同时连接和处理同一资源上的 AdminAPI
操作。如,如果同时处理 Cluster.addInstance()
和 Cluster.setPrimaryInstance()
。
6.12.1. 锁类型
AdminAPI
使用 MySQL Locking Service
提供以下锁类型:
- 读锁或共享锁:允许并发执行某些操作,同时阻止独占操作。如果某个操作尝试获取共享锁,但由于存在排他锁而无法获取共享锁,则该操作将中止,而不进行任何更改。如果当前操作有共享锁,并且新操作需要共享锁,则允许新操作访问。
- 写锁或排它锁:阻止所有其他操作的执行,直到当前操作完成并释放排它锁。如果某个操作尝试获取独占锁,但由于存在现有锁而无法获取独占锁,则该操作将中止,而不进行任何更改。
有关更多信息,请参阅 https://dev.mysql.com/doc/refman/8.0/en/locking-service.html。
实际上,如果尝试执行一个操作,而另一个无法并发执行的操作仍在运行,则会收到一条错误,指示无法获取所需资源的锁。在这种情况下,应该等待正在运行的持有锁的操作完成,然后才尝试处理下一个操作。如:
mysql-js> rs.addInstance("admin@rs2:3306");
ERROR: The operation cannot be executed because it failed to acquire the lock on
instance 'rs1:3306'. Another operation requiring exclusive access to the
instance is still in progress, please wait for it to finish and try again.
ReplicaSet.addInstance: Failed to acquire lock on instance 'rs1:3306' (MYSQLSH
51400)
复制
在此示例中,ReplicaSet.addInstance()
失败,因为无法获取 Primary
实例 (rs1:3306
) 上的锁,因为 ReplicaSet.setPrimaryInstance()
操作(或其他类似操作)仍在运行。
:::alert-info
【Note】
如果实例作为克隆操作或请求重启的一部分重新启动,则锁将被释放。因此,在短时间内(以毫秒为单位),另一个 Shell
会话可能会在重启时访问该实例并锁定它。但是,Cluster
和/或 ClusterSet
上的原始锁仍然存在,因此可能锁定新重启实例的新命令无法请求 Cluster
或 ClusterSet
锁。
:::
6.12.2. DBA 锁
本节列出了 dba.operationName
操作的锁。
操作 | 锁类型 |
---|---|
configureInstance() |
在目标实例上独占 |
configureLocalInstance() |
在目标实例上独占 |
createCluster() |
在目标实例上独占 |
rebootClusterFromCompleteOutage() |
对所有可联系的集群成员独占。 如果集群是 replica cluster ,并且是 ClusterSet 的一部分,则它也会作为操作的一部分重新加入 ClusterSet 。在这种情况下,该操作也会获取与 clusterset.rejoinCluster() 相同的锁。 |
upgradeMetadata() |
* Cluster :集群和目标实例上的排他锁。* ClusterSet :ClusterSet 、Primary Cluster 和目标实例上的独占锁。 |
createReplicaSet() |
目标实例上的独占锁。 |
configureReplicaSetInstance() |
目标实例上的独占锁。 |
upgradeMetadata() |
如果拓扑是 ClusterSet ,则在 ClusterSet 和 Primary Cluster 上独占锁定;如果拓扑是 InnoDB Cluster ,则在目标实例上独占锁定。 |
6.12.3. Cluster 锁
本节列出了 cluster.operationName
操作的锁
操作 | Cluster 锁类型 |
目标实例锁类型 |
---|---|---|
addInstance() |
独占锁 | 独占锁 |
createClusterSet() |
独占锁 | |
dissolve() |
独占锁 | |
fenceAllTraffic() |
独占锁 | |
fenceWrites() |
独占锁 | |
forceQuorum() |
独占锁 | |
rejoinInstance() |
共享锁 | 独占锁 |
removeInstance() |
独占锁 | 独占锁 |
rescan() |
独占锁 | |
resetRecoveryAccountsPassword() |
独占锁 | |
setInstanceOption() |
独占锁 除 tag 和 clusterName 选项之外。 |
|
setOption() |
独占锁 除 tag 和 clusterName 选项之外。 |
|
setPrimaryInstance() |
独占锁 | |
setupAdminAccount() |
共享锁 | |
setupRouterAccount() |
共享锁 | |
switchToMultiPrimaryMode() |
独占锁 | |
switchToSinglePrimaryMode() |
独占锁 | |
unfenceWrites() |
独占锁 |
6.12.4. ClusterSet 锁
本节列出了 clusterSet.operationName
操作的锁。
操作 | ClusterSet 锁类型 |
Primary 集群锁类型 |
目标集群锁类型 |
---|---|---|---|
createReplicaCluster() |
共享锁 | 在用于创建新 replica cluster 的实例上独占。 |
|
forcePrimaryCluster() |
在所有 replica cluster 上独占。 |
||
rejoinCluster() |
共享锁 | 共享锁 | 独占锁 |
removeCluster() |
独占锁 | 独占锁 | 独占锁 |
setOption() |
独占锁 | 独占锁(仅当设置了 replicationAllowedHost 时) |
|
setPrimaryCluster() |
独占锁 | 独占锁 |
6.12.5. InnoDB ReplicaSet 锁
本节列出了 replicaSet.operationName
操作的锁。
操作 | Primary 实例锁类型 |
目标实例锁类型 |
---|---|---|
forcePrimaryInstance() |
操作时目标实例和 ReplicaSet 的所有可联系成员上独占。 |
|
setPrimaryInstance() |
独占锁 | 操作时目标实例和 ReplicaSet 的所有可联系成员上独占。 |
addInstance() |
共享锁 | 独占锁 |
rejoinInstance() |
共享锁 | 独占锁 |
removeInstance() |
共享锁 | 独占锁 |
removeRouterMetadata() |
共享锁 |