CentOS7上MySQL5.7 InnoDB Cluster安装(一主两从复制)
- 前导知识
平时最频繁用到的InnoDB集群管理AdminAPI:
- mysqlsh --uri icadmin@linux01:3306
- var cluster = dba.getCluster('myCluster');
- var cluster = dba.rebootClusterFromCompleteOutage(); --需要在最后更新的实例上执行!
- cluster.status();
- cluster.rejoinInstance('linux01:3306');
- cluster.removeInstance('linux01:3306');
- cluster.addInstance('linux01:3306');
MySQL InnoDB Cluster 为 MySQL 提供了一个完整的高可用解决方案。通过使用 MySQL Shell 提供的 AdminAPI,你可以轻松地配置和管理一组至少由3个MySQL服务器实例组成的 InnoDB 集群。
InnoDB 集群中的每个 MySQL 服务器实例都运行 MySQL Group Replication(组复制),该复制提供了在 InnoDB 集群中复制数据的机制,具有内置的故障切换功能。AdminAPI 使我们避免了直接在 InnoDB 集群中使用组复制。从 MySQL 8.0.27开始,你还可以通过链接一个主 InnoDB 集群及其一个或多个异地(例如不同数据中心)副本来组成 InnoDB ClusterSet,以此来提供异地容灾功能。
MySQL Router 可以根据你部署的集群自动配置自己,并将客户端应用程序透明地连接到 MySQL 服务器实例。如果 MySQL 服务器实例发生意外故障,集群将自动重新配置。在默认的单主模式下,InnoDB 集群只有一个读写服务器实例——主服务器,多个辅助服务器实例是主服务器的副本。如果主服务器故障,辅助服务器将自动升级为主服务器角色。MySQL Router 检测到这一点后,会将客户端应用程序的请求转发到新的 MySQL 主服务器上。高级用户还可以将集群配置为具有多个主节点,但大多情况都不必要这样做。
InnoDB 集群至少由三个 MySQL 服务器实例组成,并提供高可用性和可扩展性功能。将使用到以下 MySQL 技术:
- MySQL Shell,它是MySQL官方提供的高级客户端和代码编辑器。
- MySQL Server 和 Group Replication(组复制),它们配合工作可以使一组MySQL实例对外提供高可能性。InnoDB Cluster提供了另一种易于使用的编程方式来使用Group Replication(组复制)功能。
- MySQL Router,一个能在应用程序和InnoDB集群之间提供透明路由的轻量级中间件,是官方提供的MySQL实例负载均衡器(不再需要借助类似HAProxy的第三方负载均衡器了)。
如下图1展示了上述各组件是如何协同工作的:
基于MySQL的Group Replication(组复制)构建的集群,可提供自动成员管理、容错、自动故障切换等功能。InnoDB集群通常以单主模式(single-primary)运行,具有一个主实例(primary读写)和多个辅助实例(secondary只读)。高级用户还可以利用多主模式(multi-primary),其中所有实例都是主实例。
你可以使用 MySQL Shell 提供的 AdminAPI 来操作 InnoDB集群。AdminAPI 有 JavaScript 和 Python 两种版本,非常适合通过编写脚本来自动化部署MySQL,以实现高可用性和可扩展性。通过使用 MySQL Shell 的 AdminAPI,你可以避免手动配置许多实例。AdminAPI 为 MySQL 实例集(组)提供了一个高效的现代化接口,可以方便地在一个统一的工具中进行配置、管理和监控部署工作。
InnoDB Cluster支持MySQL Clone(克隆),这使你可以方便地向集群配置新的实例。在过去,一个新的实例加入到MySQL集群实例集之前,你可能需要通过某种方式手动将事务传输到待加入的新实例中。这可能涉及制作文件副本、手动复制它们等等。使用InnoDB Cluster,你只需向集群添加一个实例,它就会被自动配置。
类似地,InnoDB Cluster与 MySQL Router 也进行了紧密集成,你可以使用AdminAPI来使它们一起工作。MySQL Router可以基于InnoDB Cluster,通过一个被称为bootstrapping(引导)的过程自动化的配置它自己,你无需手动配置路由。然后 MySQL Router 便可透明地将客户端应用程序连接到InnoDB集群,为客户端连接提供路由和负载平衡。这种集成还允许你使用 AdminAPI 管理基于 InnoDB Cluster 引导的 MySQL Router 的某些方面。InnoDB Cluster 的集群状态信息包含了基于集群引导的 MySQL Router 的详细信息。Operations 使你可以在集群级别创建 MySQL Router 用户,以使用基于集群引导的 MySQL Router 等等。
备注:AdminAPI 与运行 MySQL 5.7 的实例兼容,但功能集有所减少。为了获得使用 AdminAPI 和 InnoDB集群的最佳体验,官方建议升级到MySQL 8.0。但鉴于国内大多数公司还在使用 MySQL 5.7 ,我们这里将先基于该版本进行集群搭建(MySQL Community Server 5.7.37),后续将会基于MySQL 8.0再搭一次(并实现可异地容灾的InnoDB ClusterSet)!
- 正式开始
- InnoDB集群要求
在进行 InnoDB Cluster 的生产部署安装之前,请确保要使用的 MySQL 服务器实例满足以下要求:
- InnoDB Cluster使用组复制,因此你的服务器实例必须满足相同的要求(参考组复制要求)。AdminAPI 提供 dba.checkInstanceConfiguration() 方法来验证某个实例是否满足组复制要求,以及 dba.configureInstance() 方法来配置某个实例以满足要求。(注意:使用沙箱部署时,实例会自动配置为满足这些要求。)
- 用于组复制的数据以及用于 InnoDB 集群的数据,必须存储在 InnoDB 事务存储引擎中。使用其他存储引擎(包括临时内存存储引擎)可能会导致组复制错误。在使用组复制和 InnoDB 集群之前,需要将使用其他存储引擎的所有表转换为使用 InnoDB 引擎。你可以通过在MySQL服务器实例上设置 disabled_storage_engines 系统变量来防止使用其他存储引擎,例如:disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
- 在安装配置集群时,任何MySQL服务器实例都不能有入站复制通道(到达型)。在一个复制组上,由Group Replication(组复制)自动创建的通道才允许被采用(group_replication_applier 和 group_replication_recovery)。InnoDB 集群不支持手工配置的异步复制通道,使用 AdminAPI 管理的除外。如果要将现有复制拓扑迁移到一个InnoDB集群部署,并且需要在安装过程中暂时跳过此验证,则可以在创建集群时使用force选项绕过它。
- 必须在要与 InnoDB Cluster 一起使用的所有实例上启用Performance Schema。
- MySQL Shell 用于配置 InnoDB 集群中的服务器时使用到的配置脚本需要访问Python。Windows版本的 MySQL Shell 中已经包含了 Python,不需要用户再做配置。在类 Unix 系统上,Python 必须配置为 shell 环境的一部份。要检查你的 Unix 类系统是否正确配置了Python,请执行以下操作:$ /usr/bin/env python ,如果Python解释器启动,则无需进一步操作。如果前面的命令失败,请在你的 Unix类系统上安装正确版本的Python,并在 /usr/bin/python 和你安装的 Python 二进制可执行文件之间创建一个软链接。
- 从8.0.17版开始,MySQL实例必须在 InnoDB 集群中使用唯一的 server_id 。当你使用 Cluster.addInstance(instance) 操作时,如果 instance 的 server_id 已经被集群中的其他实例使用,那么该操作将失败并报错。(我们这次使用的版本是5.7.37,但最好也将 server_id 进行唯一化)
- 从8.0.23版开始,应将MySQL实例配置为使用并行复制申请者(applier)。(后面进行异地容灾的 InnoDB ClusterSet 搭建时会讲如何配置)
- 在配置 InnoDB 集群的某个MySQL实例过程中,实例要使用的绝大部分系统变量都已经被配置好了。但是 AdminAPI 不会进行 transaction_isolation 系统变量的配置,这意味着它将保持默认值 REPEATABLE READ 。这不会影响单主模式(single-primary)集群,但是如果你使用的是多主模式(multi-primary)集群,那么除非你的应用程序确实依赖默认值 REPEATABLE READ ,否则我们强列建议将值设置为 READ COMMITTED 隔离级别。
- 实例的相关配置项,尤其是组复制配置项,必须位于单个 option 文件中。InnoDB Cluster只支持服务器实例的单个 option 文件,不支持使用--defaults-extra-file 选项指定附加的 option 文件。对于任何使用实例 option 文件的 AdminAPI 操作,必须指定主文件。 如果你想为与 InnoDB Cluster 无关的配置项使用多 option 文件,你必须手动配置这些文件,并考虑多 option 文件的使用优先规则,确保它们被正确更新,并确保与 InnoDB Cluster 相关的设置没有错误地被一个未被认可的额外 option 文件中的选项覆盖。
- InnoDB集群限制
由于InnoDB Cluster使用了Group Replication(组复制),所以除了下面列的限制外,还要考虑组复制的限制:
- InnoDB 集群不管理手动配置的异步复制通道。Group Replication(组复制)和 AdminAPI 不确保异步复制仅在主服务器上处于活动状态,并且不跨实例复制状态。这可能会导致复制不再工作的各种情况,并且可能导致集群出现脑裂。只有从 MySQL 8.0.27 版本开始可用的 InnoDB ClusterSet 才支持在一个 InnoDB 集群与另一个集群之间的复制,它管理着从一个活动的主读写 InnoDB 集群到多个只读副本集群的复制工作。
- InnoDB Cluster 被设计为在局域网中部署。在广域网上部署单个 InnoDB 集群对写性能有显著影响。稳定且低延迟的网络对于InnoDB 集群成员服务器使用底层 Group Replication(组复制)技术相互通信以达成事务共识非常重要。然而,InnoDB ClusterSet 是被设计为跨多个数据中心部署的,每个 InnoDB 集群部署在单个数据中心中,通过异步复制通道将它们连接起来。
- 对于 AdminAPI 操作,你只能使用 TCP/IP 连接和经典的 MySQL 协议连接到InnoDB集群中的服务器实例。AdminAPI操作不支持使用 Unix sockets 和 named pipes(命名管道),AdminAPI 操作也不支持使用 X 协议。同样的限制也适用于MySQL 服务器实例本身之间的连接。(注意:客户端应用程序可以使用 X 协议、Unix sockets 和 named pipes(命名管道)连接到InnoDB集群中的实例,这些限制仅适用于使用 AdminAPI 命令的管理操作,以及集群内实例之间的连接)
- AdminAPI 和 InnoDB Cluster支持 MySQL Server 5.7 版本的实例。但是对这些实例会有一些限制,并且有一些功能在这版本上不支持。(后面会在“使用运行MySQL 5.7的实例”中专门列一下具体那些功能)
- 使用多主模式时,不支持针对同一对象但在不同服务器上发出的并发DDL语句和DML语句。在某个对象上发出数据定义语句(DDL)期间,在同一对象上但从不同服务器实例发出并发数据操作语句(DML),可能会导致在不同实例上执行的DDL冲突风险而未被检测到。(参考组复制的限制)
- InnoDB集群的用户帐户
InnoDB集群中的成员服务器使用三种类型的用户帐户。一个 InnoDB 集群服务器配置帐户用于为集群配置服务器实例。可以为管理员创建一个或多个 InnoDB 集群管理员帐户,以便在集群安装后管理服务器实例。可以为 MySQL Router 实例创建一个或多个 MySQL Router 帐户,用于连接到集群。这些用户帐户必须存在 InnoDB 集群中的所有成员服务器上都存在,并且使用相同的用户名和密码。
- InnoDB 集群服务器配置帐户
此帐户用于创建和配置 InnoDB 集群的成员服务器。每个成员服务器只有一个服务器配置帐户。集群中的每个成员服务器必须使用相同的用户帐户名和密码。为此,你可以在服务器上使用 root 帐户,但如果这样做,集群中每个成员服务器上的 root 帐户必须具有相同的密码。出于安全原因,不建议这样做。
首选方法是使用 clusterAdmin 选项中的 dba.configureInstance()命令创建一个 InnoDB集群服务器配置账号。为提高安全性,请在交互提示下指定密码,也可以使用 clusterAdminPassword 选项指定密码。在将成为 InnoDB 集群一部分的每个服务器实例上以相同的方式创建相同的帐户,使用相同的用户名和密码——包括你连接以创建集群的实例,以及之后将加入集群的实例。
dba.configureInstance()命令会自动授予帐户所需的权限(你也可以手动设置该帐户并授权,但不建议这么做)。除了完整的 MySQL 管理员权限外,该帐户还需要对 InnoDB 集群元数据表拥有完整的读写权限。
使用 dba.configureInstance()命令创建一个 InnoDB集群服务器配置账号不会复制到集群内的其他服务器上。MySQL Shell 禁用了 dba.configureInstance()命令的二进制日志记录。这意味着你必须在每个 MySQL 服务器实例上单独创建该帐户。
- InnoDB 集群管理员帐户
这些帐户在你完成集群配置之后,可用于管理 InnoDB 集群。你可以设置多个,每个帐户必须存在于 InnoDB 集群中的每个成员服务器上,并具有相同的用户名和密码。
为了给 InnoDB ClusterSet 创建 InnoDB 集群管理员账号,你需要在将所有实例添加到集群后执行 cluster.setupAdminAccount()命令。该命令将使用你给定的用户名和密码创建账号,并赋予必要的权限。使用cluster.setupAdminAccount()创建一个账号的事务将被写入到二进制日志,并发送到集群内所有其他服务器实例上,用于在这些服务器上创建相同的账号。(备注:如果主InnoDB 集群是在MySQL Shell 8.0.20之前版本部署的,cluster.setupAdminAccount()命令可能与 update 选项一起使用,以更新 InnoDB集群服务器配置帐户的权限,这是未写入二进制日志的命令的特殊用法)
- MySQL Router 帐户
这些账号被 MySQL Router 用于连接到 InnoDB 集群中的MySQL服务器实例。你可以设置多个此类账号,每个帐户必须存在于InnoDB 集群中的每个成员服务器上,并具有相同的用户名和密码。创建 MySQL Router 帐户的过程与创建 InnoDB 群集管理员帐户相同,但使用的是 cluster.setupRouterAccount() 命令。(也会写binary log 进行同步创建)
InnoDB集群创建的内部用户帐户
作为使用组复制的一部分,InnoDB 集群会创建内部恢复用户,以实现群集中服务器之间的连接。这些用户是集群的内部用户,生成的用户名遵循 mysql_innodb_cluster_server_id@% 的命名方案,其中 server_id 对每个实例是唯一的。在 8.0.17 之前的版本中,生成用户的用户名遵循 mysql_innodb_cluster_r[10_numbers] 的命名方案。
这些内部用户使用的主机名设置为“%”。在 v8.0.17 之前,ipAllowlist 会通过在 ipAllowlist 中每个主机一个账号来影响主机名行为。
每个内部用户都有一个随机生成的密码。从 8.0.18 版开始,AdminAPI 允许你更改内部用户生成的密码。请参阅【重置恢复帐户密码】。随机生成的用户将获得以下授权:GRANT REPLICATION SLAVE ON *.* to internal_user;
内部用户帐户在 seed 实例上创建,然后复制到集群中的其他实例。内部用户包括:
- 通过执行 dba.createCluster()命令创建一个新集群时生成的
- 通过执行 Cluster.addInstance()命令向集群添加一个新实例时生成的
- 使用主要成员正在使用的身份验证插件时生成的
在 v8.0.17 之前,ipAllowlist 会导致 Cluster.rejoinInstance()命令移除老的内部用户并生成新的用户,而不是重用它们。
重置恢复帐户密码
从8.0.18版开始,你可以使用 Cluster.resetRecoveryAccountsPassword()命令重置InnoDB群集创建的内部恢复帐户的密码,例如为了遵循自定义密码生存期策略。使用 Cluster.resetRecoveryAccountsPassword()命令重置群集使用的所有内部恢复帐户的密码。该命令为每个联机实例的内部恢复帐户设置一个新的随机密码,如果无法访问实例,则操作失败。你可以使用 force 选项忽略此类脱机的实例,但不建议这样做,在使用此命令之前将实例重新联机更安全。此命令仅适用于InnoDB Cluster创建的密码,不能用于更新手动创建的密码。(备注:执行此操作的用户必须具有所有必需的管理权限,尤其是创建用户权限CREATE USER,以确保无论所需的密码验证策略如何,都可以更改恢复帐户的密码。换句话说,这与系统变量 password_require_current 是否启用无关)
- InnoDB集群生产部署
在生产环境中,组成 InnoDB 集群的MySQL服务器实例运行在以网络链接的多个主机上,而不是一台机器上。在组建 InnoDB 集群之前,我们必须将所需的软件安装到要作为集群服务器实例的每台机器上。
下图说明了将组建的 InnoDB 集群的逻辑架构(IP只是做示例作用):
注意:对于生产部署,必须持久化实例上的任何配置变更,如何做到这一点取决于实例上运行的MySQL版本。
为了将服务器的连接信息传递给 AdminAPI,可以使用类似 URI 的连接字符串或数据字典,本例中我们将使用类似 URI 的连接字符串。
接下来开始 MySQL InnoDB 集群的实际安装部署工作:
一、首先按如下配置准备三台虚拟机(手动固定IP,Linux远程客户端使用的是SecureCRT):
HostName:linux01、linux02、linux03
操作系统:CentOS-7-x86_64
初始内存:2G
初始CPU:1P2C
初始硬盘:40G
网络模型:Bridge
IP地址:192.168.0.251、192.168.0.252、192.168.0.253
网关地址:192.168.0.1
DNS地址:10.198.1.1,114.114.114.114
开启三台虚拟机,使用ping命令检查三台虚拟机之间的网络是否互通(还必须可以访问互联网),然后对它们进行基本的环境准备工作,包括:
- 使用 hostnamectl --static set-hostname <你的主机名> 命令分别修改三台虚拟机的主机名为linux01、linux02和linux03,使用 hostnamectl status 命令查看是否修改成功;
- 在三台虚拟机上分别执行 yum install -y lrzsz 、yum install -y rsync 和yum install -y vim 命令,确保后面可能会用到的工具软件都成功安装;
- 通过 vi /etc/hosts 命令将三台虚拟机的 hosts 文件内容修改为如下的值并保存:
- 通过 reboot -h now 命令分别重启三台虚拟机;
- 通过 ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa 命令分别在三台虚拟机上生成RSA密钥对文件;
- 在每台虚拟机上都执行这三条命令 cat ~/.ssh/id_rsa.pub | ssh root@linux01 "cat - >> ~/.ssh/authorized_keys" 、cat ~/.ssh/id_rsa.pub | ssh root@linux02 "cat - >> ~/.ssh/authorized_keys" 、cat ~/.ssh/id_rsa.pub | ssh root@linux03 "cat - >> ~/.ssh/authorized_keys" ,将自己的RSA公钥文件内容追加到每台虚拟机的 authorized_keys 文件中(使三台虚拟机之间免密登录,如果SSH未安装,请先自行安装);
- 在每台虚拟机上都执行 mkdir /opt/software 和 mkdir /opt/servers 创建我们后面软件包的存放路径和安装路径;
二、准备好MySQL Server、MySQL Shell和MySQL Router的安装包
到MySQL官网去下载本例要用的MySQL相关组件包,这些包在官网有非常多的版本,下载哪些?怎么搭配使用?
本例中我们做的是MySQL 5.7的InnoDB集群,主包MySQL Server肯定是要选5.7系列的最新免费版,当前(2022年4月)最新免费版为 mysql-community-server-5.7.37 ,版本名中的 community 代表社区版的意思,也就是免费的,不要去下收费版,你懂的!我们的三台虚拟机安装的都是Redhat Linux系列下的 CentOS-7-x86_64 操作系统,所以本次下载的MySQL Server安装包为:mysql-community-server-5.7.37-1.el7.x86_64.rpm
另外,在安装MySQL Server 5.7.* 时,按官方建议:在大多数情况下,你需要安装 mysql-community-server、mysql-community-client、mysql-community-libs、mysql-community-common和 mysql-community-libs-compat软件包,以获得功能正常的标准MySQL安装。所以我们要将上述几个包都下载下来:
将这些包通过 rz 命令上传到三台虚拟机的 /opt/software 目录下:
对于MySQL Shell和MySQL Router这里要重点说明一下,因为你可能在官网上找不到带 "5.7.*" 字样的MySQL Shell和MySQL Router !我们在官网的MySQL Shell和MySQL Router的下载页中找到了如下的一些关于MySQL Shell和MySQL Router的说明:
“MySQL Shell是一个交互式Javascript、Python或SQL接口,支持MySQL Server的开发和管理,是MySQL Server的一个组件。你可以使用MySQL Shell执行数据查询和更新以及各种管理操作。强烈建议将MySQL Shell 8.0与MySQL Server 8.0和5.7配合使用。请升级到MySQL Shell 8.0。”
“MySQL Router是一种轻量级中间件,在应用程序和任何后端MySQL服务器之间提供透明路由。它可以用于各种各样的用例,例如通过有效地将数据库流量路由到适当的后端MySQL服务器来提供高可用性和可扩展性。强烈建议将MySQL Router 8.0与MySQL Server 8.0和5.7配合使用。请升级到MySQL Router 8.0。”
相信读完上述两段话,你应该知道了 —— MySQL Server 5.7 使用的MySQL Shell和MySQL Router 都是8.0.*的版本,事实上MySQL官网上目前也只有这个版本系列,连 “Looking for previous GA versions?” 的旧版本导向链接都没有!
MySQL Shell安装包为:mysql-shell-8.0.28-1.el7.x86_64.rpm
MySQL Router安装包为:mysql-router-community-8.0.28-1.el7.x86_64.rpm
三、安装好MySQL Server、MySQL Shell和MySQL Router
1.安装MySQL Server:
我们这里按照官方RPM包安装说明书进行标准MySQL安装,在 linux01上执行命令cd /opt/software 转到MySQL相关安装包所在目录,执行如下命令(我加了一个 -y 选项,让该命令遇到依赖需要下载时进行自动下载):
$> sudo yum install -y mysql-community-{server,client,common,libs}-*
安装过程中可能需要从互联网上下载一些依赖包,所以在让相应虚拟机可以连上互联网,例如:我的需要下载net-tools-2.0-0.25.20131004git.el7.x86_64.rpm 依赖包:
从上述截图可以看到,mariadb-libs.x86_64 和 mysql-community-libs-compat 这两个包将会在后续版本中被淘汰了(这里不用管它)!由于基本都是本地RPM包,所以安装过程非常丝滑快速:
我们知道Linux安装软件无非就是解压缩包并复制文件到指定目录、创建用户、修改配置、修改权限和环境变量等,那么上述安装命令会将MySQL安装到那些目录下呢?下表便是MySQL标准安装后各文件或资源分发到的位置:
文件或资源
|
位置(Location)
|
Client programs and scripts
|
/usr/bin
|
mysqld server
|
/usr/sbin
|
Configuration file
|
/etc/my.cnf
|
Data directory
|
/var/lib/mysql
|
Error log file
|
For RHEL, Oracle Linux, CentOS or Fedora platforms: /var/log/mysqld.log
For SLES: /var/log/mysql/mysqld.log
|
Value of secure_file_priv
|
/var/lib/mysql-files
|
System V init script
|
For RHEL, Oracle Linux, CentOS or Fedora platforms: /etc/init.d/mysqld
For SLES: /etc/init.d/mysql
|
Systemd service
|
For RHEL, Oracle Linux, CentOS or Fedora platforms: mysqld
For SLES: mysql
|
Pid file
|
/var/run/mysql/mysqld.pid
|
Socket
|
/var/lib/mysql/mysql.sock
|
Keyring directory
|
/var/lib/mysql-keyring
|
Unix manual pages
|
/usr/share/man
|
Include (header) files
|
/usr/include/mysql
|
Libraries
|
/usr/lib/mysql
|
Miscellaneous support files (for example, error messages, and character set files)
|
/usr/share/mysql
|
上述安装命令还将在系统上创建一个名为 mysql 的用户和一个名为 mysql 的组。
注意:使用较旧的软件包安装以前版本的MySQL可能会创建一个名为 /usr/my.cnf 的配置文件。强烈建议你检查文件的内容,并将其中所需的设置迁移到文件 /etc/my.cnf 文件,然后删除 /usr/my.cnf。
上述安装命令完成之后,MySQL并不会自动启动。对于RHEL、Oracle Linux、 CentOS 和 Fedora的系统使用下述命令启动:
$> systemctl start mysqld
对于SLES的系统,将mysqld 换成 mysql 即可!
如果操作系统已启用 systemd,则应使用标准systemctl命令(或者参数颠倒的 service命令)来管理 MySQL 服务器服务(如stop、start、status和restart)。默认情况下,mysqld 服务处于启用状态,并在系统重新启动时启动。请注意,在 systemd 平台上,某些事情可能会以不同的方式工作:例如,更改数据目录的位置可能会引发问题。
$> systemctl {start|stop|restart|status} mysqld
$> service mysqld {start|stop|restart|status}
在使用RPM和DEB软件包进行升级安装期间,如果在升级时MySQL服务器正在运行,则MySQL服务器将停止,升级完成后MySQL服务器将重新启动。有一个例外:如果版本性质在升级过程中也发生了变化(例如从社区版转到了商业版,或者反之亦然),那么MySQL服务器不会重新启动。
在服务器初始启动时,如果服务器的数据目录为空,则会发生以下情况:
- 服务器会被初始化;
- 在数据目录(默认/var/lib/mysql)中生成SSL证书和密钥文件;
- 安装并启用 validate_password;
- 将创建超级用户帐户 'root'@'localhost' ,为root账户生成一个临时初始密码并保存到错误日志文件(默认/var/log/mysqld.log)中。对RHEL、Oracle Linux、CentOS和Fedora系统可使用以下命令提取该密码:
$> sudo grep 'temporary password' /var/log/mysqld.log
对于 SLES 的系统,将文件路径换成 /var/log/mysql/mysqld.log 即可!
拿到临时初始密码后,接下来要用该密码登录MySQL,并为root账户指定一个符合validate_password密码策略的新密码:
$> mysql -uroot -p
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '1qaz@WSX';
备注:validate_password的默认密码策略要求密码长度至少为8,并包含至少一个大写字母、一个小写字母、一个数字和一个特殊符号!
如果安装过程中发生了某些错误,你可以到错误日志文件中查看一下详情(默认/var/log/mysqld.log)。
如是上述过程都没有问题的话,接下来我们要创建一个可以远程登录到 MySQL Server 的普通账号,因为出于安全考虑,超级账号root默认是只能在MySQL服务器本机登录的,我们不要图方便去改变root账号的这个默认特性(生产真出了问题你负责不起的)。MySQL服务器本机使用 mysql -uroot -p 登录MySQL,执行以下命令,创建一个名不myapp的普通账号,并授以必要的权限:
mysql> CREATE USER 'myapp'@'%' IDENTIFIED BY 'myapp1@WSX';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,ALTER,DROP ON *.* TO 'myapp'@'%' WITH GRANT OPTION;
mysql> flush privileges;
创建账号时使用的 'myapp'@'%' 其中%号表示该账号可以在任何客户端主机上登录,一般用于给开发人员使用的MySQL账号才会这样设置。为了安全起见,生产环境一般会固定为应用程序所在的特定IP或域名(例如'myapp'@'%.example.com');另外授权时使用了 ON *.* ,表示针对所有数据库的所有对象。为了安全起见,生产环境一般会固定为应用程序所要连接的特定数据库(例如 target_db.* );
接下来我们尝试一下在远程客户端(例如Navicat上)使用账号 myapp 和密码 myapp1@WSX 远程登录linux01上的MySQL服务器,但是在此之前我们还有一件重要事情要做——对MySQL使用的远程连接端口号3306进行防火墙例外设置,并重新载入防火墙:
$> sudo firewall-cmd --zone=public --add-port=3306/tcp --permanent
$> sudo firewall-cmd --reload
注意1:一定要带上--permanent 参数才能永远生效,否则系统重启后丢失,另外 --zone 、--add-port 和 --permanent参数前面是两个-;
注意2:一定要重新载入防火墙,让设置生效;
使用Windows上的Navicat测试连接:
按照上述步骤在linux02和linux03服务器上也安装配置好MySQL,然后我们继续...
其它有必要提一下的内容:
对于某些Linux发行版,可能需要增加 mysqld 可用的文件描述符数量(如何修改就自行查找了)。接下来我们看看mysqld 可用的文件描述符的默认数量:
可以通过以下命令先查看一下操作系统对 mysqld 进程打开文件数的限制:ps -ef | grep mysqld找到 mysqld 的 pid,再用 cat /proc/<mysqld_pid>/limits | grep files 显示进程打开文件数的限制:
可以看到操作系统层面对 mysqld 进程打开文件数限制在5000,我们使用 root 账号连接上MySQL 使用 select @@open_files_limit; 命令再看看MySQL自身所做的限制是多少:
可以看到MySQL内部对打开文件数限制也是 5000。
2.安装MySQL Shell,并使用它的相关命令创建集群:
我们先将MySQL Shell的RPM包上传到上面三台虚拟机的 /opt/software 目录下。
MySQL Shell 是一个可以单独安装的组件,目前只能安装在64位的Windows、Linux和macOS上(仅适用于有限数量的Linux发行版)。
MySQL Shell的安装非常简单,安装前没有多余的配置,只需要执行下 python --version 命令检查一下系统中是否已经需要的Python依赖(版本最好高于Python 2.7.0),没问题的话执行命令cd /opt/software 转到MySQL Shell安装包所在目录,再执行下述命令即可:
$> sudo yum install -y mysql-shell-*
你也可以明确指定具体的RPM包文件名,例如 sudo yum install -y mysql-router-community-8.0.28-1.el7.x86_64.rpm
安装完后,可以执行 mysqlsh 命令验证是否安装成功!
在其余两台虚拟机上也做同样的安装操作就可以了!
在MySQL Shell命令行窗口可以在JavaScript模式和SQL模式进行切换,默认是JavaScript模式:
mysql-js> \sql --切换为SQL模式
mysql-js> \js --切换为JavaScript模式
好,到现在我们搭建 MySQL InnoDB 集群所需要的软件基本都已经安装完成,剩下的工作是使用MySQL Shell AdminAPI把集群配起来:
- 在三个MySQL Server上创建一个 InnoDB 集群服务器配置帐户——configer,必须是相同的账号名称和密码,并授以相应的权限:
$> mysql -uroot -p
mysql> CREATE USER 'configer'@'%' IDENTIFIED BY 'configer1@WSX';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,ALTER,DROP ON *.* TO 'configer'@'%' WITH GRANT OPTION;
mysql> GRANT CREATE USER, FILE, PROCESS, RELOAD, REPLICATION CLIENT, REPLICATION SLAVE, SHUTDOWN, SUPER ON *.* TO 'configer'@'%' WITH GRANT OPTION;
mysql> GRANT ALTER ROUTINE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, EXECUTE, INDEX, LOCK TABLES, REFERENCES, SHOW VIEW, TRIGGER ON mysql_innodb_cluster_metadata.* TO 'configer'@'%' WITH GRANT OPTION;
mysql> GRANT ALTER ROUTINE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, EXECUTE, INDEX, LOCK TABLES, REFERENCES, SHOW VIEW, TRIGGER ON mysql_innodb_cluster_metadata_bkp.* TO 'configer'@'%' WITH GRANT OPTION;
mysql> GRANT ALTER ROUTINE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, EXECUTE, INDEX, LOCK TABLES, REFERENCES, SHOW VIEW, TRIGGER ON mysql_innodb_cluster_metadata_previous.* TO 'configer'@'%' WITH GRANT OPTION;
mysql> flush privileges;
- 在三个MySQL Server所在虚拟机上用mysqlsh命令打开MySQL Shell,并分执行以下命令将其上的MySQL实例配置为用于InnoDB集群的实例(实际上这些命令会查找MySQL的配置文件my.cnf,将集群需要的配置写入其中,当然还有其他些方面的配置变更),同时指定 InnoDB 集群管理员账号——icadmin,必须是相同的账号名称和密码(注意:MySQL 5.7用的是configureLocalInstance(),不是configureInstance()):
linux01上:mysql-js> dba.configureLocalInstance("configer@linux01:3306",{clusterAdmin:"'icadmin'@'%'"});
linux02上:mysql-js> dba.configureLocalInstance("configer@linux02:3306",{clusterAdmin:"'icadmin'@'%'"});
linux03上:mysql-js> dba.configureLocalInstance("configer@linux03:3306",{clusterAdmin:"'icadmin'@'%'"});
完了后根据提示使用 \q命令退出MySQL Shell,然后使用系统命令systemctl restart mysqld 将三台虚拟机上的MySQL服务重启。
- 使用以下命令重新进入MySQL Shell,并分别验证三个MySQL Server实例的配置状态是否已经能够作为InnoDB集群的成员了:
linux01上:#> mysqlsh --uri icadmin@linux01:3306
linux01上:mysql-js> dba.checkInstanceConfiguration();
linux02上:#> mysqlsh --uri icadmin@linux02:3306
linux02上:mysql-js> dba.checkInstanceConfiguration();
linux03上:#> mysqlsh --uri icadmin@linux03:3306
linux03上:mysql-js> dba.checkInstanceConfiguration();
当然,你也可以像下面这样,只在一台虚拟机的MySQL Shell命令行中将三个实例一起验证完:
linux01上:#> mysqlsh
linux01上:mysql-js> dba.checkInstanceConfiguration('icadmin@linux01:3306');
linux01上:mysql-js> dba.checkInstanceConfiguration('icadmin@linux02:3306');
linux01上:mysql-js> dba.checkInstanceConfiguration('icadmin@linux03:3306');
但不建议你这么做!一开始我在linux01上做完所有实例的配置验证,发现所有状态都是"ok",可是后面将各实例添加到集群时,发现linux03可以添加到集群,linux02死活不行。重新在linux01上远程检查linux02的配置状态依旧是 "ok"的,但在linux02本机上检查却报出了如下的错误:
各种办法我都试过了,都没能解决,最后将linux02重装系统,重新对其进行配置和安装MySQL相关组件,然后才加进集群里了~~!!!
- 我们将linux01规划为创建集群的种子节点,在linux01上使用集群管理员账号重新进入MySQL Shell中,执行以下命令创建一个名为 "myCluster"的集群:
linux01上:#> mysqlsh --uri icadmin@linux01:3306
linux01上:mysql-js> var cluster = dba.createCluster('myCluster');
在创建集群添加Seed Instance的过程中可能会报错,这个时候要 cat /var/log/mysqld.log 打开日志看看具体报错,比如我的就是由于防火器阻止了端口33061上的连接所致:
在防火像中像上面的3306端口一样给放开就好了(三台机都要)!
还要将三台机子的selinux关闭:
#> setenforce 0
#> vi /etc/selinux/config
将其中的SELINUX设置为disabled ,然后重新执行 dba.createCluster('myCluster');
- 将其它MySQL实例成员添加到上面创建的集群中
如果你前面使用 dba.createCluster('myCluster');创建集群时,没用 var cluster来承接,或者你创建完集群后重新进入MySQL Shell的,那么你可以通过下面命令来获取的目标集群对象(旧时代叫【句柄】):
mysql-js>var cluster=dba.getCluster('myCluster');
mysql-js>cluster.addInstance('icadmin@linux02:3306');
mysql-js>cluster.addInstance('icadmin@linux03:3306');
记住:如果在添加某个实例到集群中报错,首先要看看cat /var/log/mysqld.log 打开日志看看具体报错!
查看集群简要信息:
mysql-js>cluster.describe();
查看集群详细信息:
mysql-js>cluster.status();
- 测试一下数据库能否同步——在主节点上新建和删除数据库,分别在从库上验证是否同步了(这涉及到主节点二进制日志和从节点中继日志配合工作的原理):
- 测试一下故障转移——在主节点上执行 systemctl stop mysqld 将其停掉(模拟主节点故障,这涉及到GTID相关的主节点选举原理):
- 测试一下故障恢复后数据库能否同步——在新主节点上创建新表(注意:InnoDB集群要求所有表必须有主键,我这边没有给t1表指定主键,后面会影响集群状态的),对其增、删、改等操作,然后重新启动因故障失联的旧主节点,看看从故障中恢复的旧主节点上能否同步数据:
可以看到,从故障中恢复的旧节点没有进行最新数据的同步!原因是:从故障中恢复后的旧主节点已经不在再是集群的主节点了,但它的“super_read_only”选项仍然处于禁用状态,导致其group_replication功能处于停止状态!
- 测试一下将故障中恢复的节点重新加入集群——使用 cluster.rejoinInstance('linux01:3306'); 命令将故障恢复后的失联节点重新加入集群(MySQL 8.0.16之后的版本可以配置成故障恢复后自动重新加入集群!):
- 停止集群——停止掉集群内各个实例的group_replication:
mysql-js> shell.connect("icadmin@linux01:3306"); --链接上某个集群实例(断开连接用shell.disconnect();)
mysql-js> \sql --切换为SQL模式
mysql-sql> stop group_replication;
mysql-sql> \js --切换为JavaScript模式继续停掉其它节点的组复制
重新加入集群的故障节点会进行数据的同步,此时你在linux01上也能看到新主节点上新增的t1表了!至此集群的配置和基本验证完成!!!
可能的集群问题整理:
整个集群全部中断后(例如上面的停止集群或服务器全部重启后),要在最后更新的实例上使用 var cluster = dba.rebootClusterFromCompleteOutage(); 命令重新启动集群。
如果集群发生脑裂(split-brain),那么你可以将整个集群的所有实例全部重启,然后var cluster = dba.rebootClusterFromCompleteOutage(); 命令重新启动集群。
如是dba.rebootClusterFromCompleteOutage();时出现类似——Dba.rebootClusterFromCompleteOutage: The active session instance (linux01:3306) isn't the most updated in comparison with the ONLINE instances of the Cluster's metadata. Please use the most up to date instance: 'linux03:3306'. (RuntimeError)的错误,根据提示——这是因为你当前所在实例不是最后上次集群停止前最后更新的实例(GTID不是最新),这时你要根据提示连接到最后更新的实例上(比如这里提示为'linux03:3306')执行dba.rebootClusterFromCompleteOutage();重启集群:
如果出现某个实例的GTID为空,导致重新加入集群失败(ERROR: The target instance 'linux02:3306' has an empty GTID set so it cannot be safely rejoined to the cluster. Please remove it and add it back to the cluster.),那么先将其移除,再重新加入:
mysql-js>cluster.removeInstance("linux02:3306");
mysql-js>cluster.addInstance('icadmin@linux02:3306');
如果只从库由于组复制问题无法加回集群,那可以可暂停从库的组复制,然后重置它,再重新加入集群:
mysql> stop group_replication; --(暂停组复制)
mysql> reset master;
mysql> reset slave;
mysql-js>cluster.addInstance('icadmin@linux02:3306');
或
mysql-js>cluster.rejoinInstance('icadmin@linux02:3306');
如果各种办法都没法恢复集群了,那就只能重置整个集群了(但是集群上实例的数据库数据不要去动):
主节点:
mysql-js>dba.dropMetadataSchema(); 登录mysql-shell清空集群
mysql> stop group_replication;
mysql> reset master; (清空日志,确保和从库的表没有冲突)
mysql> reset slave;
其他节点(主要清理和主库的主从信息, 确保主库和从库的表没有冲突)
mysql> stop group_replication;
mysql> reset master;
mysql> reset slave
然后重新创建集群,添加实例等操作。
3.安装MySQL Router,并使用它实现读写分离、负载均衡、故障转移:
从前面出现过的这两张集群逻辑架构图中我们可以清楚的看到:在一个MySQL InnoDB集群中,MySQL Router 是独立出来放在集群外面的,这也是由它在整个集群中所充当的角色所决定的。
MySQL Router作为整个InnoDB集群的流量入口(门神),肩负着流量承接、负载均衡、成员管理以及故障转移的重任(就像前置Nginx服务器一样),非常有必要使用一台高性能的独立服务器进行部署,还要配以足够的带宽(事实上,如果仅有一台集中式MySQL Router服务器的话,它将成为整个集群性能和可用性的瓶颈,而解决这个问题方法是接下来要讲的内容)。另外, MySQL Shell 作为InnoDB集群的管理入口,其实也可以放在整个集群之上安装,但在用MySQL Shell的指令dba.configureInstance("configer@linux01:3306");将MySQL Server实例配置为用于InnoDB集群的实例时,会在命令执行的本机查找MySQL Server的配置文件my.cnf,找不到将会报错,所以上面三台MySQL Server实例所在主机上也安装上MySQL Shell。
实际生产中,为了获得最佳性能,MySQL Router通常会与它服务的客户端应用程序安装在同一台主机上,主要原因有:
- 这将允许客户端应用程序使用本地UNIX域socket来与MySQL Router建立链接,而不需要走TCP/IP;(注意:本地UNIX域socket只能在应用程序和MySQL Router之间发挥作用,在MySQL Router和MySQL Server之间不行)
- 这将减少网络延迟;
- 这将允许MySQL Router直接使用分配给客户端应用程序的MySQL账号(例如myapp@192.168.0.254)来连接MySQL,而不需要单独为MySQL Router的宿主机再分配一个额外的MySQL账号(例如放大可登录范围的账号myapp@%);
- 这将允许MySQL Router跟随应用程序服务器一起横向扩展,形成一对一的服务,消除集中式MySQL Router性能和可用性问题;
当然,你也可以在网络上运行多个MySQL Router实例,并且不需要将MySQL Router 独立部署到一台机器上。这是因为MySQL Router不会依赖于何特定的服务器或主机!
下面是官方给出的MySQL Router的系统要求:
- 硬件:至少需要1核CPU和256MB的内存,建议使用4核CPU和4GB以上的内存;
- 磁盘空间:最少需要100MB的空间;
- 外部库依赖:大部分外部依赖都已经打包在了MySQL Router包中(例如protobuf和rapidjson),有一个例外,那就是OpenSSL,它仅在Windows版本的MySQL Router包中有捆绑。类Linux系统的包管理器会自动解决OpenSSL依赖,并根据需要安装正确的OpenSSL版本。
为了更接近真实生产环境,这里我们再添加一台更高配的虚拟机来安装部署MySQL Router和MySQL Shell(后续的应用程序也将在该虚拟机上部署),配置如下(手动固定IP和主机名):
HostName:linux666
操作系统:CentOS-7-x86_64
初始内存:4G
初始CPU:1P4C
初始硬盘:20G
网络模型:Bridge
IP地址:192.168.0.254
网关地址:192.168.0.1
DNS地址:10.198.1.1,114.114.114.114
注意:我的宿主机是五星级神机能带得起这么多虚拟机来,如果你的机器带不起来,那么可以将虚拟机的配置稍微降一降,我后面还有其他集群和服务要用到这些虚拟机,所以配高一点!
将新的虚拟机linux666启动起来,参照之前的三台虚拟机对它进行环境的基本设置(四台机子的 /etc/hosts 文件中要把linux666也加进去),最重要的是确保它能与之前的三台虚拟机网络互通(否则还路由个锤子)!
环境基本设置完成后,我们将MySQL Router包上传到linux666的/opt/software目录下。
现在来我们安装MySQL Router:
MySQL Router 也是一个可以单独安装的组件,使用官方的DEB或RPM包安装 MySQL Router 会器默认在其运行的主机上创建一个名为“mysqlrouter”的本地系统用户和组。使用RPM包安装MySQL Router也很简单,只需要执行命令cd /opt/software 转到MySQL Router安装包所在目录,再执行下述命令即可:
$> sudo yum install -y mysql-router-*
你也可以明确指定具体的RPM包文件名,例如 sudo yum install -y mysql-router-community-8.0.28-1.el7.x86_64.rpm
当然你也可以使用rpm命令进行安装:
$> sudo rpm -i mysql-router-community-8.0.28-1.el7.x86_64.rpm
关于MySQL Router的卸载可以使用以下命令:
$> sudo rpm -e mysql-router
或者
$> sudo yum remove mysql-router
但是这些卸载命令不会删除MySQL Router的配置文件,你可能需要手动删除以下路径下的配置文件:
/etc/mysqlrouter
/etc/init.d/mysqlrouter
/etc/mysqlrouter/mysqlrouter.conf
/etc/apparmor.d/usr.sbin.mysqlrouter
最后可以再执行一下查找命令,看看哪里还有余留的文件,将它们删除掉
$> whereis mysqlrouter
接下来配置MySQL Router,实现InnoDB集群读写分离和故障自动转移:
- 进行MySQL Rouer初始化引导(Bootstrapping) ——连接上InnoDB集群获取配集群配置(执行命令先要先检查一下集群的状态是否正常):
MySQL Router通过使用--bootstrap和其他命令行选项为现有的InnoDB集群自动配置MySQL Router。在引导过程中,MySQL Route连接到目标集群,获取其元数据来实现自我配置(如果对应的InnoDB集群重新创建了,使用它的MySQL Router需要删除旧引导生成的配置文件(/etc/mysqlrouter/mysqlrouter.conf),并重新初始化引导)。
$> mysqlrouter --bootstrap icadmin@linux01:3306 --user root
- icadmin 是InnoDB集群管理员账号,linux01:3306是集群主节点或成员节点,如果给的是成员节点引导模块会自动转发到主节点(PRIMARY);
- --user root指定的是操作系统账号,不MySQL Router用于连接到 InnoDB 集群的账号,该账号你可以重新建一个专用的,我这边直接用了root账号;
[root@linux666 .ssh]# mysqlrouter --bootstrap icadmin@linux01:3306 --user root Please enter MySQL password for icadmin: # 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 - Read/Write Connections: localhost:6446 - Read/Only Connections: localhost:6447 ## MySQL X protocol - Read/Write Connections: localhost:6448 - Read/Only Connections: localhost:6449 [root@linux666 .ssh]#
可以看到配置完成后显示:MySQL经典协议读写操作走的是6446端口,而只读协议走的是6647端口,有兴趣的话你可以进一步查看一下生成的MySQL Router配置文件 /etc/mysqlrouter/mysqlrouter.conf。后面启动MySQL Router时,如不做特殊指定的话将使用这个生成的默认配置文件!
看到端口,我们要第一时间记得对它们进行防火墙例外设置,并重新载入防火墙:
$> sudo firewall-cmd --zone=public --add-port=6446/tcp --permanent
$> sudo firewall-cmd --zone=public --add-port=6447/tcp --permanent
$> sudo firewall-cmd --zone=public --add-port=6448/tcp --permanent
$> sudo firewall-cmd --zone=public --add-port=6449/tcp --permanent
$> sudo firewall-cmd --reload
- 为集群配置 MySQL Router 帐户:
在安装有MySQL Shell的主机上连接上集群,使用MySQL Shell AdminAPI为集群添加MySQL Router专用账号(可以多个,但实际上可以不添加MySQL Router账号,直接使用分配给应用程序的MySQL账号即可,否则你还得为这个新添加的专用账号到MySQL服务器上做授权操作,默认权限很少)。
linux01上:#> mysqlsh --uri icadmin@linux01:3306
mysql-js>var cluster=dba.getCluster('myCluster');
mysql-js>cluster.setupRouterAccount('myrouter');
以上命令为集群创建了一个可以在任意位置向集群发起连接的MySQL Router账号——myrouter@%。如果要限制在某个IP或域名下才能发起连接,可以这样做:
mysql-js>cluster.setupRouterAccount('myrouter@example.com');
- 手动启动MySQL Rouer:
$> mysqlrouter &
或者
$> mysqlrouter --config=/path/to/file/your_router.conf &
命令末尾的 & 符是为了让其以系统后台进程的方式运行,不致于卡着终端窗口,执行完命令按回车即可!
- 验证可以通过MySQL Router连接上MySQL集群
这一步你可以在linux666上安装一个纯的MySQL Client来做验证,也可以直接用其他三台机子上的MySQL客户端:
$>mysql -u myrouter -h linux666 -P 6446 -p
还可以直接在Windows上用Navicat作验证:
讲到这里,顺便整理一下应用程序通过MySQL Router连接上MySQL InnoDB集群里MySQL Server实例的过程:
- MySQL客户端或Connector连接器连接到MySQL Router的指定端口,例如这里的6446或6447;
- MySQL Router检查可用的MySQL Server实例;
- MySQL Router向合适的MySQL Server实例建立连接并打开(根据端口类型:读写(RW)端口发给主节点,只读(RO)端口会进行负载均衡选取从节点);
- MySQL Router在应用程序和MySQL服务器之间来回转发数据包。
- 如果连接的MySQL服务器出现故障,MySQL Router将断开应用程序的连接。然后,应用程序可以重试连接到MySQL Router,MySQL Router会选择另一个可用的MySQL服务器建立连接。
- 验证读写分离:
先查看一下InnoDB集群中三台MySQL Server实例的server_id是多少,并将它们记下来,分别打开三台机子配置文件/etc/my.cnf得到结果如下:
linux01上:server_id = 3029772939
linux02上:server_id = 3920259733
linux03上:server_id = 3585953301
而当前集群的状态如下图所示:
从上面验证结果可以看到,读写(RW)接口的请求发往了主节点,只读(RO)接口的请求分别发往了从节点。读写分离和负载均衡验证通过,这不是偶然情况,你可以多试几次!
- 验证故障转移
在上一步的基础上,保持上一步的各个会话连接不要断开,直接停掉当前的主节点(linux01上的MySQL Server实例),我们看看会发生什么:
我们看到故障转移在上一步实验的客户端会话连接未关闭的情况下就自动发生了,但却有一个只读(RO)端口的连接连在了当前的新主节点上!这是为什么呢?不是说只读(RO)端口建立连接时只会选择从节点吗?其实原因是:我们是在没有关闭上一步的会话连接的情况下做的本次实验,这次关掉旧主节点时,其它节点上原先保持住的会话连接并不会重新建立,只有原来建立在旧的主节点上的连接会重新进行连接!当然后续新发起的连接还是会根据端口类型:读写(RW)端口发给主节点,只读(RO)端口会进行负载均衡选取从节点!
好了,到此我们在CentOS7上MySQL5.7 InnoDB Cluster安装(一主两从复制)就已经完成了,里面关于MySQL InnoDB Cluster利用的Paxos相关的分布式一致性算法,故障时的主节选举等,就需要各位自己去深入学习了,这个才是重点!
还有一个是将MySQL Router配置成随操作系统一起启动,安装过程中自动创建的mysqlrouter.service服务有问题!
后记:
在虚拟机环境下,InnoDB集群运行一段时间(特别是当宿主机休眠一段时间后),重新查看集群状态时,经常会有节点报错离线—— "ERROR: group_replication has stopped with an error.",去目标节点的查看其MySQL服务器实全日志tail -300 /var/log/mysqld.log经常看到这样的报错信息:[ERROR] Plugin group_replication reported: 'Member was expelled from the group due to network failures, changing member status to ERROR.',有时甚致直接发生脑裂(split-brain,这时你只能重启所有MySQL Server实例再重新var cluster = dba.rebootClusterFromCompleteOutage(); 命令重新启动集群)。
直接打开MySQL客户端,登录目标节点,重启group_replication功能后又能恢复正常,如此反复很不是爽!
mysql> stop group_replication;
mysql> start group_replication;
mysql> select * from performance_schema.replication_group_members;
从官网中找到如下几个参数:
看表底下的说明,应该是集群成员尝试超过group_replication_recovery_retry_count参数设置的次数之后,如果还没有跟组内的成员取得联系,那么相应成员实例将放弃并停止自身的组复制。而当我的宿主机进入休眠状态时,主机网卡不工作,虚拟机之间的网络也变得不可用,该参数默认的10次尝试很快就用完了!如果是生产环境的网络不太好的原因,可以在InnoDB集群成员所有节点上修改这参数的值,将其设置的大一点(对宿主机进入休眠状态其实没有多少作用,虚拟机最好将网络适配器的“复制物理网络连接状态(P)”复选框勾上):
mysql> set global group_replication_recovery_retry_count= 64;
改完后记得重新启动一下集群:
mysql-js>mysqlsh --uri icadmin@linux01:3306
mysql-js>var cluster = dba.rebootClusterFromCompleteOutage();