快速部署 InnoDB ClusterSet
文章目录
- 快速部署 InnoDB ClusterSet
- 前言
- 前期准备
- 架构设计
- 部署过程
- 1. 使用配置账号通过 MySQL Shell 连接到 InnoDB Cluster 任一成员
- 2. 为主 InnoDB Cluster 实例设置变量
- 3. 创建以当前集群作为主集群的 ClusterSet
- 4. 为每个独立服务器实例添加配置账户
- 5. 在集群集中创建副本集群
- 6. 向副本集群中添加两个副本成员实例
- 7. 验证集群集的状态
- 8. (可选)向集群集中添加新的副本集群
- 9. 重新引导路由
- 10. (可选)在另一台服务器上新建并引导一个路由
- 11. 测试使用路由对 MySQL 经典协议的实际影响
- 12. 测试使用路由对 MySQL X 协议的实际影响
- (解释)正常配置的测试结果差异
- 结论
前言
回顾前面我们搭建了 InnoDB Cluster 集群,基于组复制技术可以提供容错、高可用、弹性伸缩、故障转移、负载均衡、连接重定向、自动选主等功能,而且支持单主、多主两种模式,适合在稳定、低延迟网络下部署集群,不支持(集群级)容灾。(一般来讲,容灾是指跨地域容灾;当然一些架构将实例部署在不同机架、机房也可以提供实例级、机房级容灾,但不适合对网络的时效性、延迟要求高的系统)InnoDB Cluster 的核心亮点是高可用。 此外,一个 InnoDB 集群最多支持 9 个成员实例,横向扩展受限。
为了弥补 InnoDB Cluster 不支持容灾的不足,出现了 InnoDB ClusterSet 。为了提供最大程度的容灾能力,InnoDB ClusterSet 将可用性置于一致性之上。而且 InnoDB ClusterSet 因不限制副本集群数量而可无限 横向扩展(Scale-out)。InnoDB ClusterSet 在主集群与副本集群间建立名为 clusterset_replication 的 ClusterSet 复制通道来异步复制数据,该复制过程由底层的组复制技术管理。在遇到问题或故障时,首先主集群尝试在组内自我协调问题;如若不行,主发生故障,主集群内部会自动选主, clusterset_replication 也会将复制源切换到主集群的新主上;某些情况下如有必要可以进行 **受控切换(Controled Switchover)**到某一可选的副本集群;如果主集群无法满足 多数派(法定人数,Quorum),先尝试手动修复,如若不行,万不得已之下才选择 紧急切换(Emergency Failover)。InnoDB ClusterSet 虽提供了读取流量的无限横向扩展,却未解决单点写入的问题,甚至主集群还不支持多主模式。InnoDB ClusterSet 的核心亮点是容灾。
前期准备
- 一个满足 8.1 节 InnoDB ClusterSet 要求 的已部署的至少三节点的 InnoDB Cluster 集群,作为 InnoDB ClusterSet 的主集群。
- 一些独立的(推荐至少三个)额外服务器实例,按你的规划组成 InnoDB ClusterSet 的副本集群。
- (可选)一个额外服务器实例,作为第二个副本集群,仅提供备份和有限读取能力。
- 所有实例的 MySQL 版本在 8.0.27 以上。这是 InnoDB ClusterSet 的要求。我选择的是最新的 GA 版 8.0.31 。
- MySQL 账号,配置账号、管理员账号、路由账号,均已在部署 InnoDB Cluster 时配置过了。
- 其他软件要求,MySQL Shell 8.0.31,MySQL Router 8.0.31 (要求最低为 8.0.27)。
有关用 Systemd在一台服务器上运行多个 MySQL 服务器实例的方法,详见 使用 systemd 管理多个 MySQL 服务器实例 ,及 2.5.9 节 使用 systemd 管理 MySQL 服务器实例 。
架构设计
部署过程
1. 使用配置账号通过 MySQL Shell 连接到 InnoDB Cluster 任一成员
[root@ic-source ~]$ mysqlsh --quiet-start=2 ic_config@ic-source
2. 为主 InnoDB Cluster 实例设置变量
MySQL ic-source:33060+ ssl JS > cluster=dba.getCluster()
提示
在确保主集群可用时,也可以直接在 mysqlsh 命令中使用--cluster
选项完成前两步的等价操作:[root@ic-source ~]$ mysqlsh --quiet-start=2 --cluster ic_config@ic-source
3. 创建以当前集群作为主集群的 ClusterSet
MySQL ic-source:33060+ ssl JS > cs = cluster.createClusterSet('myClusterSet',{clusterSetReplicationSslMode: 'REQUIRED'})
验证 ClusterSet 状态为HEALTHY
。
MySQL ic-source:33060+ ssl JS > cs.status()
查看 ClusterSet 状态的扩展信息。该扩展信息展示 InnoDB ClusterSet 拓扑中的每个集群的详细状态,即包含集群集内每一个集群中的每个成员实例的信息。
MySQL ic-source:33060+ ssl JS > cs.status({extended: 1})
注意
请记住这两个命令,此后不再赘述,只截图对照。
4. 为每个独立服务器实例添加配置账户
分别为每个独立服务器实例添加配置账户,这些独立服务器实例之后将按你的期望组成集群集中的一个或多个副本集群。注意,这里使用的是 root@`%`
账号,初始没有,需要创建。 为什么使用它?因为初始化的实例只有 4 个本地账号,需要建立一个远程连接账号,一般首选 root@`%`
账号,注意授权时必须使用WITH GRANT OPTION
选项 。
MySQL ic-source:33060+ ssl JS > dba.configureInstance('root@ic-source:3307',{clusterAdmin: 'ic_config'})
MySQL ic-source:33060+ ssl JS > dba.configureInstance('root@ic-replica1:3307',{clusterAdmin: 'ic_config'})
MySQL ic-source:33060+ ssl JS > dba.configureInstance('root@ic-replica2:3307',{clusterAdmin: 'ic_config'})
MySQL ic-source:33060+ ssl JS > dba.configureInstance('root@ic-replica1:3308',{clusterAdmin: 'ic_config'})
5. 在集群集中创建副本集群
MySQL ic-source:33060+ ssl JS > rs1 = cs.createReplicaCluster('ic-source:3307','replicaCluster01',{recoveryProgress: 1, recoveryMethod: 'CLONE'})
这里recoveryProgress: 1
显示详细静态进度信息,默认值为 2 显示动态进度条。recoveryMethod: 'CLONE'
表示恢复方式为 克隆 ,初始实例没有用户数据,选择该方式最快。
6. 向副本集群中添加两个副本成员实例
向副本集群中添加两个副本成员实例,以组成一个三节点的副本集群。
MySQL ic-source:33060+ ssl JS > rs1.addInstance('ic_config@ic-replica1:3307', {recoveryMethod: 'CLONE'})
MySQL ic-source:33060+ ssl JS > rs1.addInstance('ic_config@ic-replica2:3307', {recoveryMethod: 'CLONE'})
7. 验证集群集的状态
MySQL ic-source:33060+ ssl JS > cs.status({extended: 1})
8. (可选)向集群集中添加新的副本集群
这次我们创建一个只有一个成员的副本集群,我在测试环境做这步的目的是验证是否支持创建只有一个成员的副本集群。生产环境这么做的目的是只做备份和少量分担读取流量,供本地应用使用。
MySQL ic-source:33060+ ssl JS > cs.createReplicaCluster('ic-replica1:3308','replicaCluster02',{recoveryProgress: 1, recoveryMethod: 'CLONE'})
检查集群集状态。
或
MySQL ic-source:33060+ ssl JS > cs.describe()
9. 重新引导路由
因为我在部署 InnoDB Cluster (即现在的主集群)时已经引导过了路由,现在因集群拓扑由 InnoDB Cluster 升级为 ** InnoDB ClusterSet** ,路由配置也需要相应地改变。
[root@ic-source mysql]$ mysqlrouter --bootstrap ic_config@ic-source:3306 --account myRouter1 --user=mysqlrouter --conf-use-sockets --name 'routerSource' --force
MySQL ic-source:33060+ ssl JS > cs.listRouters()
10. (可选)在另一台服务器上新建并引导一个路由
mysqlrouter --bootstrap ic_config@ic-replica1:3306 --account myRouter2 --user=mysqlrouter --conf-use-sockets --name 'routerReplica1' --force
ic-source 上的 mysqlrouter 状态:
ic-replica1 上的 mysqlrouter 状态:
这里遇到两个 Bug ,一是不能用ClusterSet.setRoutingOption()
设置全局选项;二是以前引导的路由历史记录永远存在,路由器名称还出现重名,虽然拼接完主机名后并不重名。我这误操作一共搞了 5 个路由器。。。
MySQL ic-source:33060+ ssl JS > cs.listRouters()
查看 mysql 内的表mysql_innodb_cluster_metadata.routers
发现的确有 5 条,这元数据也是不动态更新的!
为了矫正它,我需要截断这张表,并重新引导路由器并重启。
mysql> truncate table mysql_innodb_cluster_metadata.routers;
在 ic-source 上重新引导两个路由,第二个路由需要使用-d 目录
选项创建自包含目录结构:
[root@ic-source ~]$ mysqlrouter --bootstrap ic_config@ic-source:3306 --account myRouter1 --user=mysqlrouter --conf-use-sockets --name 'pcPriRouter' --force
[root@ic-source ~]$ mysqlrouter --bootstrap ic_config@ic-source:3307 --account myRouter1 --user=mysqlrouter --conf-use-sockets -d /var/lib/mysqlrouter/rc01PriRouter --name 'rc01PriRouter' --force
在 ic-replica1 上重新引导两个路由:
[root@ic-replica1 ~]# mysqlrouter --bootstrap ic_config@ic-replica1:3306 --account myRouter2 --user=mysqlrouter --conf-use-sockets --name 'pcRep1Router' --force
[root@ic-replica1 ~]# mysqlrouter --bootstrap ic_config@ic-replica1:3307 --account myRouter2 --user=mysqlrouter --conf-use-sockets -d /var/lib/mysqlrouter/rc01Rep1Router --name 'rc01Rep1Router' --force
选取 5 个空闲端口,我选的是 [6456-6459](对应默认的 MySQL 4 个端口 [6446-6459]),8445(对应默认的 HTTP 端口 8443)。在自包含目录内的mysqlrouter.conf
文件内搜索port
,将 5 个 port
以此改为如上 5 个端口,保存退出。
改之前记得使用如下命令验证一下端口是否空闲。
lsof -i:端口
然后,使用 mysqlsh 配置各路由的选项。
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("replica1::pcRep1Router","stats_updates_frequency",15)
Routing option 'stats_updates_frequency' successfully updated in router 'replica1::pcRep1Router'.
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("replica1::rc01Rep1Router","stats_updates_frequency",30)
Routing option 'stats_updates_frequency' successfully updated in router 'replica1::rc01Rep1Router'.
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("source::pcPriRouter","stats_updates_frequency",5)
Routing option 'stats_updates_frequency' successfully updated in router 'source::pcPriRouter'.
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("source::rc01PriRouter","stats_updates_frequency",10)
Routing option 'stats_updates_frequency' successfully updated in router 'source::rc01PriRouter'.
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("source::rc01PriRouter","target_cluster","replicaCluster01")
Routing option 'target_cluster' successfully updated in router 'source::rc01PriRouter'.
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("replica1::rc01Rep1Router","target_cluster","replicaCluster01")
Routing option 'target_cluster' successfully updated in router 'replica1::rc01Rep1Router'.
MySQL ic-source:33060+ ssl JS > cs.routingOptions()
注意,这里我将副本集群的主成员路由的目标集群设置为它自己所在的副本集群了。同样,也将副本集群的从成员路由的目标集群设置为它自己所在的副本集群了。简言之,设置副本集群的路由指向它自己。这会导致后面出现的问题,而这是我有意设计的。如果你想搭建一个健康合理的集群,请不要像我这样做。
使用各自创建的自包含目录中的start.sh
脚本启动那两个自包含路由。
root@ic-source ~]$ . /var/lib/mysqlrouter/rc01PriRouter/start.sh
[root@ic-replica1 ~]# sh /var/lib/mysqlrouter/pcRep1Router/start.sh
查验路由选项及配置信息。
11. 测试使用路由对 MySQL 经典协议的实际影响
使用 **mysql_config_editor ** 执行如下命令创建 login-path ,可用于快速启动 mysql 会话。
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6446 -h ic-source -G pcPriRw -p
Enter password:
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6447 -h ic-source -G pcPriRO -p
Enter password:
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6456 -h ic-source -G rc01PriRW -p
Enter password:
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6457 -h ic-source -G rc01PriRO -p
Enter password:
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6446 -h ic-replica1 -G pcRep1RW -p
Enter password:
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6447 -h ic-replica1 -G pcRep1RO -p
Enter password:
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6456 -h ic-replica1 -G rc01Rep1RW -p
Enter password:
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6457 -h ic-replica1 -G rc01Rep1RO -p
Enter password:
下面开始验证端口 6446,6447,6456,6457:
- 验证主集群主成员上的路由的 RW 端口(
--login-path=pcPriRW
)。三次均是一样的正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcPriRW -e 'select @@hostname,@@port;'
- 验证主集群主成员上的路由的 RO 端口(
--login-path=pcPriRO
)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcPriRO -e 'select @@hostname,@@port;'
- 验证副本集群主成员上的路由的 RW 端口(
--login-path=rc01PriRW
)。副本集群是只读的,不支持 RW 请求,是正确输出。
点我,跳转回正确结果
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01PriRW -e 'select @@hostname,@@port;'
查看日志可以发现路由信息说目标集群是副本集群,不接受 RW 连接。
- 验证副本集群主成员上的路由的 RO 端口(
--login-path=rc01PriRO
)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01PriRO -e 'select @@hostname,@@port;'
- 验证主集群从成员上的路由的 RW 端口(
--login-path=pcRep1RW
)。主集群从成员虽是只读的,但此路由的目标集群指向的是主集群,所以支持 RW 请求,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcRep1RW -e 'select @@hostname,@@port;'
- 验证主集群从成员上的路由的 RO 端口(
--login-path=pcRep1RO
)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcRep1RO -e 'select @@hostname,@@port;'
- 验证副本集群从成员上的路由的 RW 端口(
--login-path=rc01Rep1RW
)。副本集群是只读的,不支持 RW 请求了,是正确输出。
点我,跳转回正确结果
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01Rep1RW -e 'select @@hostname,@@port;'
- 验证主集群从成员上的路由的 RO 端口(
--login-path=rc01Rep1RO
)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01Rep1RO -e 'select @@hostname,@@port;'
12. 测试使用路由对 MySQL X 协议的实际影响
MySQL X 协议,即 MySQL Shell 、 MySQL AdminAPI 、MySQL Connectors 等使用的更贴近开发者的协议,对应于 X DevAPI 。在 CI/CD、DevOps 的浪潮下,MySQL 也积极跟进,下了一步好棋。未来 DBA 行业将迎来更多的挑战,传统的只会使用、管理某些数据库的 DBA 已经要被淘汰了,未来企业对 DBA 开发能力的要求可能不仅限于 Shell、Python 脚本,还要与开发人员联动,甚至在开发人员不在时自己亲身上阵手撸代码改 Bug 。。。好,闲话不多说了。
MySQL Shell 支持保存密码功能,所以仅首次登录时需要输入密码,默认不保存。故我这里就不需要像上面一样做额外的准备工作了。
下面开始验证端口 6448,6449,6458,6459
- 验证主集群主成员上的路由的 RW 端口(ic-source:6448)。三次均是一样的正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-source:6448
- 验证主集群主成员上的路由的 RO 端口(ic-source:6449)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-source:6449
- 验证副本集群主成员上的路由的 RW 端口(ic-source:6458)。副本集群是只读的,不支持 RW 请求,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-source:6458
但在/root/.mysqlsh/mysqlsh.log
日志里没有提现报错,有点意外。
- 验证副本集群主成员上的路由的 RO 端口(ic-source:6459)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-source:6459
- 验证主集群从成员上的路由的 RW 端口(ic-replica1:6448)。主集群从成员虽是只读的,但此路由的目标集群指向的是主集群,所以支持 RW 请求,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-replica1:6448
注意看主集群主成员上的日志里确实有相应的路由信息。
- 验证主集群从成员上的路由的 RO 端口(ic-replica1:6449)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-replica1:6449
- 验证副本集群从成员上的路由的 RW 端口(ic-replica1:6458)。副本集群是只读的,不支持 RW 请求了,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-replica1:6458
- 验证副本集群从成员上的路由的 RO 端口(ic-replica1:6459)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select @@hostname,@@port;' --uri=myRouter1@ic-replica1:6459
说明
其实 第 11 步 也可以改用 mysqlsh 使用--sqlc
选项来测试 MySQL 经典协议,而且操作起来更简单,因为只需要修改主机名和接口号就可以了,无需额外准备工作。
(解释)正常配置的测试结果差异
如果你没做 上面那步修改目标集群的操作 ,第 11 、12 步的测试结果将是不一样的。第 11 、12 步的各自的测试结果中都只有第 3 、第 7 条出现了异常,原因正是因为我将它们的路由器的目标集群修改为副本集群了。
测试结果具体差异如下(此处采用上面的编号以方便比较):
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("replica1::rc01Rep1Router","target_cluster",null);
Routing option 'target_cluster' successfully updated in router 'replica1::rc01Rep1Router'.
MySQL ic-source:33060+ ssl JS > cs.setRoutingOption("source::rc01PriRouter","target_cluster",null);
Routing option 'target_cluster' successfully updated in router 'source::rc01PriRouter'.
MySQL ic-source:33060+ ssl JS > cs.routingOptions()
{
"domainName": "myClusterSet",
"global": {
"invalidated_cluster_policy": "drop_all",
"stats_updates_frequency": 0,
"target_cluster": "primary"
},
"routers": {
"replica1::pcRep1Router": {
"stats_updates_frequency": 15
},
"replica1::rc01Rep1Router": {
"stats_updates_frequency": 30
},
"source::pcPriRouter": {
"stats_updates_frequency": 5
},
"source::rc01PriRouter": {
"stats_updates_frequency": 10
}
}
}
修改后的状态,也是正常应该配置的状态,我指的是target_cluster
选项。我之所以这么做的原因是想测试验证自己的想法。
第 11 步中的 3 和 7
3. 验证副本集群主成员上的路由的 RW 端口(--login-path=rc01PriRW
)。虽然副本集群是只读的,但路由将 RW 请求路由到目标集群——主集群上了,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01PriRW -e 'select @@hostname,@@port;'
- 验证副本集群从成员上的路由的 RW 端口(
--login-path=rc01Rep1RW
)。虽然副本集群是只读的,但路由将 RW 请求路由到目标集群——主集群上了,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01Rep1RW -e 'select @@hostname,@@port;'
想要对比,点这里 跳转到 3 ,跳转到 7 。
同样,第 12 步中的 3 和 7 也是如此。此处不再赘述,只给出测试结果。
第 12 步中的 3 :
第 12 步中的 7 :
结论
综上,我们通过部署 InnoDB ClusterSet 充分学习了如何部署、使用 InnoDB 集群集的知识,并且证明了集群集的确是只有一个主节点可以写入,其他节点均只能读取,而不管它是在主集群还是副本集群中。而且,主集群与副本集群间的复制是异步的,不支持半同步复制。
另外,在领略了 MySQL Router 在路由、故障转移、负载均衡、重定向方面的表现后,若真按照 MySQL 推荐的方案,在每个应用服务器上都部署一个 MySQL Router ,确实能最大程度地保证应用透明无感知的高可用服务。以上就是本篇的全部内容,不忙的话点个赞吧!