InnoDB Cluster搭建

简介

InnoDB Cluster与MySQL Cluster是完全不同的:InnoDB Cluster用于InnoDB,而MySQL Cluster用于NDB。
InnoDB Cluster并没有单独的应用,它是由3个应用共同协作构成的:

  • MySQL服务:提供数据库功能及组复制同步功能。
  • MySQL Shell:提供控制台功能,自带一些扩展的API。
  • MySQL Router:将用户应用透明连接到MySQL实例,以及故障转移功能。

准备工作

进入官方下载页面:

https://dev.mysql.com/downloads/shell/

  1. 下载并安装MySQL Community Server。
  2. 下载MySQL Shell。解压即用,不需要其他设置。
  3. 下载MySQL Router。

MySQL当前最新版为8.0.21。本次以该版本为例。
文中会多次用到cmd。打开cmd后,需cd到 mysql/bin/ 目录下。也可以直接将该目录配置到系统环境变量中,这样打开cmd无需cd即可调用mysql命令。下文将不再强调这一点。

MySQL 配置文件

安装MySQL Community Server,需要创建my.ini配置文件并添加配置。一个示例配置如下:

[client]
# 设置mysql客户端连接服务端时默认使用的端口
port=3306
default-character-set=utf8mb4

[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8mb4

[mysqld]
# 日志时间
log_timestamps=SYSTEM
# 设置3306端口
port=3306
# 设置mysql的安装目录
basedir=D:\Program Files\mysql
# 设置 mysql数据库的数据的存放目录,MySQL 8+ 不需要以下配置,系统自己生成即可,否则有可能报错
# datadir=D:\\sqldata
# 允许最大连接数
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为utf8mb4
character-set-server=utf8mb4
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password
# 不使用严格模式
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

任何对my.ini配置文件的修改,都必须重启MySQL服务才能生效。
不需要也不推荐在其中添加任何集群相关的配置

MySQL重置

若需要MySQL恢复初始状态,又不想卸载重新安装,可执行如下步骤来重置MySQL:

  1. 确认my.ini配置文件正确,然后以管理员身份打开cmd。
  2. 输入net stop mysql来停止服务。
  3. 删除 MySql/data/ 文件夹的所有内容。
  4. 输入mysqld --initialize --console,等待初始化完成,记下密码。
  5. 输入net start mysql来启动服务。

这样,MySQL就重置完成了。

MySQL 用户赋权

需要为MySQL创建'root'@'%'用户并为其赋权。
打开cmd,首先登陆MySQL:

mysql -u root -p

输入密码后,若是第一次运行MySQL,则需要修改密码,下面将密码改为123456。否则跳过这一步:

alter user 'root'@'localhost' identified by '123456';

然后依次输入:

create user 'root'@'%' identified by '123456';
grant all privileges on *.* to 'root'@'%' with grant option;
flush privileges;

服务器准备

3台服务器,1台安装系统windows 10,2台安装windows 7。都已安装MySQL 8.0.21,使用相同的my.ini配置文件,并按上述步骤创建'root'@'%'用户并赋权。
查看我的电脑属性,可以看到这三台机器的计算机名,其ip与计算机名分别为:

  • 192.168.15.98 YJPC-20200402
  • 192.168.15.38 BF-20191127EQLK
  • 192.168.15.115 PC-20180424RHNT

接下来要以192.168.15.98作为主服务器。
为了防止防火墙影响,关闭三台服务器的防火墙。生产环境防火墙不能关闭,则需要开放3306和33061两个端口。

配置hosts

对三台机器都执行如下操作
打开 C:\Windows\System32\drivers\etc\ ,将hosts文件复制到桌面上。打开桌面上的hosts文件,在最后添加:

192.168.15.98 YJPC-20200402
192.168.15.38 BF-20191127EQLK
192.168.15.115 PC-20180424RHNT  

将hosts文件复制回 C:\Windows\System32\drivers\etc\ ,替换。

关于日志

日志文件位于*/mysql/data/计算机名.err*,记录的是MySQL生成的日志。无论何种方式影响到MySQL,都会将日志写入该文件。
当使用MySQL Shell操作时,MySQL Shell本身控制台的输出信息是有限的,更多详细信息会被写入到*.err日志文件中。因此遇到问题时,应多分析.err日志文件。
对于集群添加/删除实例等,不仅会影响到主实例的
.err日志文件,还会影响到被添加/删除实例的.err*日志文件。

集群创建

打开\mysql-shell\bin\mysqlsh.exe,默认以JS方式操作。

检查实例并修正配置

运行指令:

dba.checkInstanceConfiguration('[email protected]:3306')

其中括号内的参数需要使用''号包裹,@号之前为用户名,@号之后为数据库访问地址。
然后会提示输入密码,以及是否记住密码。
接着会列出检查结果:
config_error
打印出的表格已经给出了有问题到的变量、变量的当前值、变量的所需值、修改建议。并提示可使用dba.configureInstance()命令来修正这些问题。按照建议对有问题的变量进行修改:

dba.configureInstance('[email protected]:3306')

在执行过程中会2次询问用户是否修改,输入y,然后即可修改成功。
config_repair

MySQL会自动重启,重新输入dba.checkInstanceConfiguration('[email protected]:3306')来检测:
config_success

此时状态已变为OK。
对其他两台服务器也使用相同方式修正配置

创建集群

创建集群之前,MySQL Shell需要连接到一个主实例,也称为集群的种子实例。该实例会作为模板,用于该集群其他实例的同步。

  1. 输入\connect [email protected]:3306来连接到主服务器,准备将其作为种子实例。第一次连接会提示输入密码并是否记住。连接成功,会显示connection id的值,且JS命令行会带有前缀192.168.15.98:3306 ssl

  2. 输入var cluster = dba.createCluster('clusterTest')来创建一个名为clusterTest的集群,等待创建完成。创建完成后,会提示集群至少需要3个实例才能支撑1个实例的故障。
    InnoDB Cluster搭建_第1张图片

  3. 创建完成,可调用命令查看其状态:

    cluster.status()
    

集群创建后,MySQLperformance_schema.global_variables表会添加许多集群相关的变量,例如group_replication_ip_whitelistgroup_replication_local_address等。只有集群创建后才会添加这些变量;当解散集群时,这些变量仅仅不会再使用,并不会被删除。
可以使用cmd登录MySQL查询这些变量,不过使用Navicat等数据库工具更方便。
MySQL Shell只是一个控制台,InnoDB Cluster本质上还是MySQL提供的功能。因此即使关闭MySQL Shell,InnoDB Cluster依然是运行的。关闭MySQL Shell后再次打开,可输入\connect root@ip:3306来连接到任意实例并操作InnoDB Cluster。

向集群中添加实例

设置白名单

添加实例之前,要将各个实例的地址加入到白名单中。
白名单相关的全局变量为performance_schema.global_variables表中的group_replication_ip_whitelist,默认值为AUTOMATIC
打开cmd,登陆MySQL:

mysql -u root -p123456

然后进行永久化设置

stop group_replication;
set persist group_replication_ip_whitelist="192.168.15.98,192.168.15.38,192.168.15.115";
set global group_replication_bootstrap_group=on;
start group_replication;
set global group_replication_bootstrap_group=off;

persist是MySQL8.0的新特性。
第一次执行很可能会遇到3092错误,详细见下文错误处理。将错误日志中的ip调用如上命令进行永久化设置即可。

自动配置文件mysqld-auto.cnf

网上的文章大多通过修改my.ini来永久化设置白名单。
在不使用persist的情况下,有时无论怎样修改my.ini中的白名单并重启MySQL服务,甚至重启计算机,group_replication_ip_whitelist始终都是AUTOMATIC,或者之前某一次配置的值。不仅仅是白名单,此时my.ini中的非集群设置都生效了,就是集群设置不生效。
实际上,这是mysqld-auto.cnf导致的。
使用persist永久化配置,或者调用dba.rebootClusterFromCompleteOutage()来重启集群,都会创建自动配置文件mysqld-auto.cnf。该文件位于 /mysql/data/ 文件夹下。
该文件内容是一个json,会保存集群相关的一些配置项。若该文件的配置项与my.ini中的配置项冲突,则优先使用该文件的配置项。这也是为什么my.ini可能会失效的原因,故而不推荐在my.ini中添加集群相关配置。
可以调用set persist 变量名=变量值来更改某个要永久化的变量,或者直接修改mysqld-auto.cnf

动态设置

网上对白名单进行动态设置的方法通常为:打开cmd,登陆MySQL,然后依次输入:

stop group_replication;
set global group_replication_ip_whitelist="192.168.15.98,192.168.15.38,192.168.15.115";
set global group_replication_bootstrap_group=on;
start group_replication;
set global group_replication_bootstrap_group=off;

但实际上,动态设置的group_replication_ip_whitelist值,会在MySQL服务重启后失效。
每次重启MySQL服务,根据mysqld-auto.cnf中是否配置了group_replication_ip_whitelist变量的值:

  • mysqld-auto.cnf中没有配置,则会设置为AUTOMATIC
  • mysqld-auto.cnf中有配置,则会设置为配置值。

不推荐该方式,建议直接使用永久化设置

my.ini 中的loose-前缀

如前文所说,不推荐使用my.ini来进行任何集群相关的设置。该处仅仅是一个说明,不需要了解的可跳过。
网上各种资料配置my.ini白名单使用的设置都是loose-group_replication_ip_whitelist
loose-前缀的作用是:当启动MySQL服务时若使用本配置的插件尚未加载,则不报错,令MySQL服务继续加载。若没有该前缀,则若MySQL没有启用组复制插件,那么MySQL将无法启动。
因此,当数据库刚初始化时,由于这时没有使用组复制相关的插件,若不带loose-前缀,必然会报错。但若带上loose-前缀,就可以继续加载了。
同理,若数据库中尚未创建集群,那么若此时my.ini中使用了不带loose-前缀的配置项,一旦重启MySQL服务,会发现MySQL服务无法启动。
总之就是:推荐带上loose-前缀,这样无论是否使用了组复制插件都不会报错。

添加实例

打开MySQL Shell:

  1. 输入\connect [email protected]:3306来连接到主实例。

  2. 定义变量来保存集群对象:

    var cluster = dba.getCluster()
    

    获取到cluster变量后,可调用命令查看其状态:

    cluster.status()
    

    只要连接到InnoDB Cluster上的任意实例,都可获取cluster。

  3. 将另一个实例添加到该集群中来:

    cluster.addInstance('[email protected]:3306')
    
  4. 提示选择一种恢复策略,用于决定实例如何恢复与集群同步所需的事务。提供了2种策略:

    • [C]lone:克隆。会完全复制种子实例,并且会删除本实例与种子实例的差异。
    • [I]ncremental recovery:增量恢复。会将种子实例中存在但本实例中不存在的数据同步到本实例,并且会保留本实例中存在但种子实例中不存在的数据。
    • [A]bort:取消导入。

    并提示 (default Clone),即默认为克隆。可根据需求选择。这里选择第一项克隆,输入C
    若从实例只参与集群,不做其他用途,则可以选克隆;若从实例除了集群还有其他作用,则需要选择增量。
    等待执行完成,提示添加成功:
    InnoDB Cluster搭建_第2张图片

其他实例按相同方式添加。
这样,集群就构成了。对主实例进行任何操作,都会同步到从实例中。

集群状态

输入cluster.status()来查看集群状态。
InnoDB Cluster搭建_第3张图片

其中:

  • clusterName: 集群名称。
  • defaultReplicaSet:集群设置。其下包含多个项及所有连接到此集群的实例设置。
    • primary:当前主实例的地址,仅在群集以单主模式运行时显示。若未显示,则群集将以多主模式运行。
    • ssl:群集是否使用安全连接。值为REQUIRED/DISABLED,默认为REQUIRED。可在createCluster()addInstance()时设置memberSslMode从而更改该值。此参数返回的值对应于实例上的group_replication_ssl_mode服务器变量的值。
    • status:集群状态。取值为:
      • OK:所有节点处于online状态,有冗余节点。
      • OK_PARTIAL:有节点不可用,但仍有冗余节点。
      • OK_NO_TOLERANCE:有足够的online节点,但没有冗余,例如:两个节点的Cluster,其中一个挂了,集群就不可用了。
      • NO_QUORUM:有节点处于online状态,但达不到法定节点数,此状态下Cluster无法写入,只能读取。
      • UNKNOWN:不是online或recovering状态,尝试连接其他实例查看状态。
      • UNAVAILABLE:组内节点全是offline状态,但实例在运行,可能实例刚重启还没加入Cluster。
    • topology:集群。其下每个实例的设置为:
      • mode:模式。R/W为读写,R/O为只读。
      • role:角色。目前只有H/A
      • status:实例状态。取值为:
        • ONLINE:实例在线并参与集群。
        • OFFLINE:实例离线。
        • RECOVERING:实例正在与集群同步,同步完成后将变为ONLINE
        • UNREACHABLE:实例不可达,与集群无法通信。
        • ERROR:实例在回复阶段或应用事务时遇到错误。ERROR状态的实例其super_read_only将被设置为ON
        • MISSING:已在元数据中注册但在实时集群中找不到的实例。
      • version:实例的MySQL版本。
    • groupInformationSourceMember:集群的某些信息。默认显示集群创建实例的连接。可通过cluster.status({'extended':value})传入额外的参数值来显示不同的信息,value的取值为:
      • 0:默认值,禁用附加信息。
      • 1:包括有关组复制报告的组复制协议版本,组名称,集群成员UUID,集群成员角色和状态以及受防护系统变量列表的信息。
      • 2: 包括有关连接和应用程序处理的事务的信息。

配置MySQL Router

MySQL Router单独配置时,通常是手动在根目录下添加一个mysqlrouter.ini文件。
但结合InnoDB Cluster使用时,则是使用--bootstrap命令来自动生成配置文件。
首先打开cmd,cd到 /mysql-router/bin/ 目录下。然后调用--bootstrap命令来自动生成配置文件:

mysqlrouter --bootstrap [email protected]:3306

生成完成,会看到控制台信息:
InnoDB Cluster搭建_第4张图片

此时查看MySQL Router根目录,会发现自动生成了2个文件:mysqlrouter.confmysqlrouter.key。其中mysqlrouter.conf就是生成的配置文件。
查看mysqlrouter.conf,会发现创建了如下几个端口设置:

  • 6446: 经典MySQL协议,读写会话,MySQL Router将传入连接重定向到主服务器实例。
  • 6447: 经典MySQL协议,只读会话,MySQL Router将传入连接重定向到其中一个辅助服务器实例。
  • 64460: X协议,读写会话,MySQL Router将传入连接重定向到主服务器实例。
  • 64470: X协议,只读会话,MySQL Router将传入连接重定向到其中一个辅助服务器实例。

当客户端连接到一个端口并进行操作时,MySQL Router就会进行重定向。

  • 6446端口进行读写,则会顺序定向到主节点。
  • 6447端口进行只读,则会顺序定向到所有节点。

策略可修改。
然后执行命令来启动MySQL Router:

mysqlrouter &

若出现提示

logging facility initialized, switching logging to loggers specified in configuration

且新的一行出现小横线且无法再输入,即运行成功。

结构

最底层为多个MySQL实例,由InnoDB Cluster负责同步。这些实例需要分别访问。
向上一层为MySQL Router,对多个MySQL实例进行了统一入口,只需要访问单一端口,即可对整个集群进行操作。使得整个集群结构对用户透明。
最上层为用户,只需要访问MySQL Router提供的读写/只读端口。
也就是说,对于最上层用户而言,只需要区分哪个操作用读写端口,哪个操作用只读端口。地址是唯一的。

测试

创建一个名为script.bat的文件,在其中添加脚本:

echo 访问6446端口:主实例,读写.
mysql -uroot -p123456 -P6446 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -p123456 -P6446 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -p123456 -P6446 --protocol=TCP -N -r -B -e"select @@hostname"
echo 访问6447端口:从实例,只读.
mysql -uroot -p123456 -P6447 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -p123456 -P6447 --protocol=TCP -N -r -B -e"select @@hostname"
mysql -uroot -p123456 -P6447 --protocol=TCP -N -r -B -e"select @@hostname"

其中:

  • 用户名为root,密码为123456,按实际情况替换。
  • 前3条命令的作用是访问6446端口,返回主机名。
  • 后3条命令的作用是访问6447端口,返回主机名。

然后在同一目录下再创建一个名为test.bat的文件,在其中添加脚本:

script.bat >result.txt

双击运行test.bat,等待执行完成,会在同目录下生成result.txt文件,打开即可看到执行结果。

cluster操作

操作 方法示例
连接实例 \connect [email protected]:3306
检测实例状态 dba.checkInstanceConfiguration('[email protected]:3306')
自动修正实例设置 dba.configureInstance('[email protected]:3306')
删除集群元数据 dba.dropMetadataSchema()
创建集群 var cluster = dba.createCluster('clusterTest')
向集群中添加实例 cluster.addInstance('[email protected]:3306')
从集群中删除实例 cluster.removeInstance('[email protected]:3306')
从集群中删除实例(强制) cluster.removeInstance('[email protected]:3306',{force:true})
查看集群状态 cluster.status()
查看集群描述 cluster.describe()
重启集群 var cluster = dba.rebootClusterFromCompleteOutage()
解散集群 cluster.dissolve({force:true})
指定一个新的主节点 cluster.setPrimaryInstance('[email protected]:3306')
切换到多主模式 cluster.switchToMultiPrimaryMode()
切换到单主模式 cluster.switchToSinglePrimaryMode('[email protected]:3306')
更改集群设置 cluster.setOption('clusterName','newCluster')
更改集群实例设置 cluster.setInstanceOption('[email protected]:3306', 'exitStateAction', 'READ_ONLY')

说明:

  • 若遇到MISSING状态的实例,直接重启该实例的MySQL服务。调用cluster.status(),会看到该实例的状态变更为RECOVERING。等待其执行完成,状态会变为ONLINE。若依然不能解决,将该实例从集群中删除,重新添加。
  • 若主实例的MySQL服务重启,则必须调用dba.rebootClusterFromCompleteOutage()来重启集群。
  • 调用\connect连接到哪个实例,后续的操作就对哪个实例生效。例如dba.dropMetadataSchema()会删除当前连接实例的集群元数据。

常见错误

当遇到错误时,要看错误是操作哪个实例出现的。若仅仅是操作一个实例导致,那么就查看该实例的日志;若是两个实例通信,则需要同时分析这两个实例的日志。

3096错误

ERROR: Unable to start Group Replication for instance 'PC-20180424RHNT:3306'. Please check the MySQL server error log for more information.
Cluster.addInstance: Group Replication failed to start: MySQL Error 3096 (HY000): PC-20180424RHNT:3306: The START GROUP_REPLICATION command failed as there was an error when initializing the group communication layer. (RuntimeError)

以及日志提示:

2020-07-31T06:56:59.959400Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Timeout while waiting for the group communication engine to be ready!'
2020-07-31T06:56:59.959400Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The group communication engine is not ready for the member to join. Local port: 33061'
2020-07-31T06:57:01.009400Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member was unable to join the group. Local port: 33061'
2020-07-31T06:57:07.229400Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Error connecting to all peers. Member join failed. Local port: 33061'

通常为hosts配置错误。检查通信的两个实例各自的hosts是否已正确设置。
若确认hosts配置无误,则可能是防火墙/路由器阻止,3306/33061端口不通。可以使用telnet命令来测试目标实例的这两个端口确保畅通。

3092错误

ERROR 3092 (HY000): The server is not configured properly to be an active member of the group. Please see more details on error log.

日志位于\mysql\data\YJPC-20200402.err。用记事本打开,可以看到提示:

2020-08-01T03:00:37.094436Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Error connecting to the local group communication engine instance.'
2020-08-01T03:00:37.159548Z 0 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] The member was unable to join the group. Local port: 33061'
2020-08-01T03:00:42.256356Z 0 [Warning] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Connection attempt from IP address ::ffff:192.168.147.1 refused. Address is not in the IP whitelist.'

有一个ip 192.168.147.1 被拒绝了,这个ip并非是三台服务器的ip。需要将此ip也加入到白名单中。
可以使用永久化方式添加,按前文方式操作即可。

3097错误

ERROR 3097 (HY000): The START GROUP_REPLICATION command failed as there was an error when joining the communication group.

通常是在调用start group_replication;时报此错误。start group_replication;必须先设置group_replication_bootstrap_groupon

set global group_replication_bootstrap_group=on;
start group_replication;
set global group_replication_bootstrap_group=off;

metadata exists, instance belongs to that metadata, but GR is not active

调用\connect连接到主节点,然后var cluster = dba.getCluster()时报:

Dba.getCluster: This function is not available through a session to a standalone instance (metadata exists, instance belongs to that metadata, but GR is not active) (RuntimeError)

此时可尝试重启集群:

dba.rebootClusterFromCompleteOutage()

若依然不能解决,则使用cmd方式登录MySQL,然后启动该节点的group replication

set global group_replication_bootstrap_group=on;
start group_replication;
set global group_replication_bootstrap_group=off;

metadata exists, instance does not belong to that metadata, and GR is not active

调用\connect连接到某个从节点,然后var cluster = dba.getCluster()时报:

Dba.getCluster: This function is not available through a session to a standalone instance (metadata exists, instance does not belong to that metadata, and GR is not active) (RuntimeError)

此时可删除从节点元数据,重新加入集群中:

dba.dropMetadataSchema();

执行过程中会报一个ERROR,提示是否禁用super_reade_only,输入y即可。
对于非当前节点报的此错误,首先要\connect连接到错误节点。

你可能感兴趣的:(MySql)