本文档主要介绍MySQL新发布的高可用集群方案InnoDB Cluster。MySQL InnoDB Cluster解决方案由MySQL的多个不同产品和技术组成:MySQL Server、MySQL Shell、MySQL Router、Group Replication。
该方案基于MySQL组复制,比主从复制要可靠。使用innodb引擎,比ndb引擎具备更强的性能、稳定性和容错性。
该方案可以达到以下容错效果:少于一半的服务器意外故障(宕机、断电、断网)。如果是有计划的维护,极限状况下可以在仅有1台存活时提供服务(Router和MySQL运行在同一台服务器,并且不考虑1台服务器能否支撑正常业务)。
该方案使用的软件包包括以下内容:
1、MySQL Community Server。MySQL单机版软件包。
本文档中使用的版本为mysql-8.0.18-linux-glibc2.12-x86_64.tar.xz。
2、MySQL Shell。MySQL Shell是一个交互式的JavaScript、Python或SQL接口,支持MySQL服务器的开发和管理,是MySQL服务器的一个组件。
本方案中用来组建集群和进行管理。本文档中使用的版本为mysql-shell-8.0.18-linux-glibc2.12-x86-64bit.tar.gz。
3、MySQL Router。MySQL Router是轻量级中间件,提供应用程序和任何后端MySQL服务器之间的透明路由。它可以用于各种各样的用例,例如通过高效地将数据库通信路由到适当的后端MySQL服务器,从而提供高可用性和可伸缩性。
本文档中使用的版本为mysql-router-8.0.18-linux-glibc2.12-x86_64.tar.xz。
4、Keepalived。Keepalived是一个类似于layer3,4&7交换机制的软件。本方案中用来实现Router的热备,提供虚地址供客户端访问,并在发生故障时自动切换。
5、Python。MySQL Shell要求Python2.7以上环境。如未安装或版本不符,需要先安装或升级Python。
mysql-8.0.18-linux-glibc2.12-x86_64.tar.xz下载地址:
https://download.csdn.net/download/Zhuge_Dan/12558708
mysql-shell-8.0.18-linux-glibc2.12-x86-64bit.tar.gz下载地址:
https://download.csdn.net/download/Zhuge_Dan/12811579
mysql-router-8.0.18-linux-glibc2.12-x86_64.tar.xz下载地址:
https://download.csdn.net/download/Zhuge_Dan/12811584
keepalived-1.2.13-5.el6_6.x86_64.rpm下载地址:
https://download.csdn.net/download/Zhuge_Dan/12811597
Python-2.7.3下载地址:
https://download.csdn.net/download/Zhuge_Dan/12811603
该方案的原理如下图所示:
1、MySQL Server通过组复制的方式组建1主多从的集群。
2、MySQL Router通过Keepalived虚拟出一个虚地址给终端提供服务,发生故障时自动切换虚地址。
3、客户端访问MySQL Router,MySQL Router把请求分配到MySQL Server上。
4、MySQL Shell提供集群组建、管理的功能,同时为Router提供路由规则。
官方文档:https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-cluster-userguide.html
本示例中使用5台机器为例(3台服务器的最小集群,2个Router节点可以复用其中2个server节点)。其中包括2台MySQL Router和3台MySQL Server(1主2从)。IP规划如下:
分类 | 角色 | IP地址 | 运行软件 | 防火墙规则 |
---|---|---|---|---|
Router | 路由,主节点(MYSQL01) | 192.168.9.77、 192.168.10.55(虚) |
MySQL Router、 MySQL Shell、 Keepalived、 Router监测脚本 |
VRRP组播 对所有业务段IP开放:6446、6447 |
Router | 路由,主节点(MYSQL02) | 192.168.9.78、 192.168.10.55(虚) |
MySQL Router、 MySQL Shell、 Keepalived、 Router监测脚本 |
VRRP组播 对所有业务段IP开放:6446、6447 |
Server | MySQL主节点(MYSQL03) | 192.168.9.79 | MySQL Router、 MySQL Shell |
对其它4个节点开放:3306、33060、33061 |
Server | MySQL从节点(MYSQL04) | 192.168.9.80 | MySQL Router、 MySQL Shell |
对全部5个节点开放:3306、33060、33061 |
Server | MySQL从节点(MYSQL05) | 192.168.9.81 | MySQL Router、 MySQL Shell |
对全部5个节点开放:3306、33060、33061 |
操作设备:所有节点
[root@MYSQL01 ~]# setenforce 0
[root@MYSQL01 ~]# vi /etc/selinux/config
SELINUX=disabled #将SELINUX设置为disabled
[root@MYSQL01 ~]# getenforce
Permissive
[root@MYSQL01 ~]# systemctl stop firewalld #关闭防火墙
[root@MYSQL01 ~]# systemctl disable firewalld #禁止防火墙开机自启
[root@MYSQL01 ~]# echo '* soft nofile 65535' >> /etc/security/limits.conf
[root@MYSQL01 ~]# echo '* hard nofile 65535' >> /etc/security/limits.conf
[root@MYSQL01 ~]# cat /etc/security/limits.conf | tail -2
* soft nofile 65535
* hard nofile 65535
[root@MYSQL01 ~]# echo 'ulimit -SHn 65535' >> /etc/rc.d/rc.local
[root@MYSQL01 ~]# cat /etc/rc.d/rc.local | tail -1
ulimit -SHn 65535
[root@MYSQL01 ~]# chmod +x /etc/rc.d/rc.local #为rc.local增加可执行权限
[root@MYSQL01 ~]# reboot
[root@MYSQL01 ~]# getenforce #查看SELinux是否关闭成功
Disabled #关闭成功
[root@MYSQL01 ~]# systemctl status firewalld #查看防火墙状态
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
Active: inactive (dead) #防火墙已关闭
Docs: man:firewalld(1)
[root@MYSQL01 ~]# systemctl is-enabled firewalld #查看防火墙是否开机自启
disabled #防火墙已禁止开机自启
[root@MYSQL01 ~]# ulimit -a | grep open
open files (-n) 65535 #open files已调整为65535
在其它所有服务器上操作前4步。
方法:可使用Xshell,在菜单栏“工具”中选择“发送键输入到所有会话”,同时控制多台设备。
操作设备:所有节点
将所有主机的IP和主机名对应关系配置到系统中,在所有服务器上执行:
[root@MYSQL01 /]# echo '192.168.9.77 MYSQL01' >> /etc/hosts
[root@MYSQL01 /]# echo '192.168.9.78 MYSQL02' >> /etc/hosts
[root@MYSQL01 /]# echo '192.168.9.79 MYSQL03' >> /etc/hosts
[root@MYSQL01 /]# echo '192.168.9.80 MYSQL04' >> /etc/hosts
[root@MYSQL01 /]# echo '192.168.9.81 MYSQL05' >> /etc/hosts
[root@MYSQL01 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.9.77 MYSQL01
192.168.9.78 MYSQL02
192.168.9.79 MYSQL03
192.168.9.80 MYSQL04
192.168.9.81 MYSQL05
然后通过ping其它主机名检查是否生效。
操作设备:所有节点
MySQL Shell要求python版本2.7以上。检查当前python版本:
[root@MYSQL03 local]# python --version
Python 2.7.5
如果低于2.7,进行版本升级:
将Python-2.7.3.zip复制到/usr/local/目录下:
[root@MYSQL03 ~]# cd /usr/local/
[root@MYSQL03 local]# unzip Python-2.7.3.zip
[root@MYSQL03 local]# cd Python-2.7.3
[root@MYSQL03 local]# ./configure
[root@MYSQL03 local]# make all
[root@MYSQL03 local]# make install
[root@MYSQL03 local]# make clean
[root@MYSQL03 local]# make distclean
[root@MYSQL03 local]# mv /usr/bin/python /usr/bin/pythonbackup
[root@MYSQL03 local]# ln -s /usr/local/bin/python2.7 /usr/bin/python
操作设备:所有节点
在所有服务器上执行:
[root@MYSQL /]# rpm -qa|grep mysql
如果有输出结果,先进行卸载:
[root@MYSQL /]# rpm -e --nodeps xxx(上面的输出结果)
操作设备:MySQL主节点(可以与MySQL从节点同时操作,注意修改my.cnf中的server_id,所有节点不允许重复)
将mysql-8.0.18-linux-glibc2.12-x86_64.tar.xz复制到服务器MYSQL03的/usr/local/目录下,然后解压。解压完成后删除压缩包,并重命名解压出来的目录。
[root@MYSQL ~]# cd /usr/local/
[root@MYSQL local]# tar -xvf mysql-8.0.18-linux-glibc2.12-x86_64.tar.xz
mysql-8.0.18-linux-glibc2.12-x86_64/bin/
mysql-8.0.18-linux-glibc2.12-x86_64/bin/myisam_ftdump
……
mysql-8.0.18-linux-glibc2.12-x86_64/share/install_rewriter.sql
mysql-8.0.18-linux-glibc2.12-x86_64/share/uninstall_rewriter.sql
[root@MYSQL local]# rm -f mysql-8.0.18-linux-glibc2.12-x86_64.tar.xz
[root@MYSQL local]# mv mysql-8.0.18-linux-glibc2.12-x86_64/ mysql
[root@MYSQL local]# groupadd mysql
[root@MYSQL local]# useradd -g mysql mysql
[root@MYSQL local]# mkdir -p /var/lib/mysql
[root@MYSQL local]# mkdir -p /data/mysql/data
[root@MYSQL local]# chown -R mysql:mysql /var/lib/mysql/
[root@MYSQL local]# chown -R mysql:mysql /usr/local/mysql/
[root@MYSQL local]# chown -R mysql:mysql /data/mysql/data/
[root@MYSQL local]# chmod -R 755 /var/lib/mysql/
[root@MYSQL local]# chmod -R 755 /usr/local/mysql/
[root@MYSQL local]# chmod -R 755 /data/mysql/data/
1、该步骤会在消息打印中输出默认的root密码。此密码在首次登录时需要输入,如果丢失,可以先清空datadir,重新进行初始化。
注意保存输出的默认密码。可以使用XShell保存日志文件的方式记录输出。
2、如果报错“error while loading shared libraries: libaio.so.”,先安装libaio-0.3.109-13.el7.x86_64.rpm。
libaio-0.3.109-13.el7.x86_64.rpm下载地址:
https://download.csdn.net/download/Zhuge_Dan/12558796
[root@MYSQL local]# rpm -qa|grep libaio
libaio-0.3.109-13.el7.x86_64
[root@MYSQL local]# /usr/local/mysql/bin/mysqld --initialize --user=mysql --basedir=/usr/local/mysql/ --datadir=/data/mysql/data/
2020-06-02T02:12:10.385762Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the default. Consider not using this option as it' is deprecated and will be removed in a future release.
2020-06-02T02:12:10.385911Z 0 [System] [MY-013169] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.0.18) initializing of server in progress as process 8907
2020-06-02T02:12:14.429872Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: y):plM;vp5e*
注意:保存此处默认的root密码:y):plM;vp5e*
[root@MYSQL local]# cp mysql/support-files/mysql.server /etc/rc.d/init.d/mysql
[root@MYSQL local]# chmod +x /etc/rc.d/init.d/mysql
[root@MYSQL local]# ln -s /usr/local/mysql/bin/mysql /usr/bin/mysql
[root@MYSQL local]# ln -s /usr/local/mysql/bin/mysqldump /usr/bin/mysqldump
[root@MYSQL local]# chmod +x mysql/bin/*
[root@MYSQL local]# vi /etc/my.cnf
[mysqld]
basedir=/usr/local/mysql
datadir=/data/mysql/data
socket=/var/lib/mysql/mysql.sock
user=mysql
#bind_address=127.0.0.1
port=3306
innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:5G
skip-character-set-client-handshake
skip-host-cache
skip-name-resolve
collation_server=utf8mb4_general_ci
character_set_server=utf8mb4
init-connect=SET NAMES utf8mb4
default-time-zone='+08:00'
# Disabling symbolic-links is recommended to prevent assorted security risks
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
max_connections=3000
thread_cache_size=64
wait_timeout=28800
tmp_table_size=16M
max_heap_table_size=8M
interactive_timeout=28800
thread_stack=192K
join_buffer_size = 4M
key_buffer_size = 16M
bulk_insert_buffer_size = 8M
read_buffer_size = 8M
sort_buffer_size = 2M
max_allowed_packet = 64M
read_rnd_buffer_size = 32M
group_concat_max_len=102400
#设置为可用物理内存70%左右
innodb_buffer_pool_size = 6G
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 16M
innodb_log_file_size = 256M
innodb_log_files_in_group = 2
innodb_max_dirty_pages_pct = 35
innodb_lock_wait_timeout = 1500
event_scheduler=ON
#id不允许重复
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
log_bin_trust_function_creators=ON
#binlog保留多长时间
binlog_expire_logs_seconds=2592000
transaction_write_set_extraction=XXHASH64
#权重 选举主节点时权重最高的节点优先成为主节点
#group_replication_member_weight=30
#网络超时时间
#group_replication_unreachable_majority_timeout=10
#脱离集群之后尝试重新加入的次数。0到2016
#group_replication_autorejoin_tries=2016
[mysqladmin]
socket=/var/lib/mysql/mysql.sock
[mysql]
socket=/var/lib/mysql/mysql.sock
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
[mysqldump]
socket=/var/lib/mysql/mysql.sock
[client]
socket=/var/lib/mysql/mysql.sock
default-character-set=utf8mb4
CentOS Linux release 7.8.2003版本提示:
[root@MYSQL local]# service mysql restart
ERROR! MySQL server PID file could not be found!
Starting MySQL..... SUCCESS!
CentOS 6.10版本提示:
[root@MYSQL local]# service mysql restart
MySQL server PID file could not be found! [FAILED]
Starting MySQL..... [ OK ]
[root@MYSQL local]# mysql -uroot -p
Enter password: #密码为第4步输出的内容,如果已经丢失,需要重新进行初始化
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.18
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
#重置root@localhost密码。密码根据需要调整。
mysql> ALTER USER USER() IDENTIFIED BY 'atenorth';
Query OK, 0 rows affected (0.01 sec)
#创建外部访问的root账户,为了兼容旧版程序,密码校验方式使用本地密码方式(根据需要,此步骤可以省略)
mysql> CREATE USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'atenorth' PASSWORD EXPIRE NEVER;
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)
#添加应用程序用户
mysql> CREATE USER 'Raikon'@'%' IDENTIFIED WITH mysql_native_password BY 'atenorth' PASSWORD EXPIRE NEVER;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE DATABASE Contacts character set utf8 collate utf8_general_ci;
Query OK, 0 rows affected (0.00 sec)
#把Contacts库的所有表,分配给Raikon用户all权限(根据需要调整),注意去掉WITH GRANT OPTION,即不给应用程序用户分配“分配权限”的权限。
mysql> GRANT ALL ON Contacts.* TO 'Raikon'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.01 sec)
mysql> exit
Bye
先把mysql-shell-8.0.18-linux-glibc2.12-x86-64bit.tar.gz上传到/usr/local目录下。
[root@MYSQL03 local]# tar -zxvf mysql-shell-8.0.18-linux-glibc2.12-x86-64bit.tar.gz
mysql-shell-8.0.18-linux-glibc2.12-x86-64bit/
mysql-shell-8.0.18-linux-glibc2.12-x86-64bit/bin/
……
mysql-shell-8.0.18-linux-glibc2.12-x86-64bit/share/mysqlsh/mysqlprovision.zip
mysql-shell-8.0.18-linux-glibc2.12-x86-64bit/share/mysqlsh/upgrade_checker.msg
[root@MYSQL03 local]# rm -f mysql-shell-8.0.18-linux-glibc2.12-x86-64bit.tar.gz
[root@MYSQL03 local]# mv mysql-shell-8.0.18-linux-glibc2.12-x86-64bit shell
通过MySQL Shell登录到本地:
[root@MYSQL03 local]# /usr/local/shell/bin/mysqlsh root@localhost
Please provide the password for 'root@localhost': ********
MySQL Shell 8.0.18
Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'root@localhost'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 10 (X protocol)
Server version: 8.0.18 MySQL Community Server - GPL
No default schema selected; type \use to set one.
MySQL localhost:33060+ ssl JS > dba.checkInstanceConfiguration('root@localhost:3306'); #或者为“root@主机名:3306”
Please provide the password for 'root@localhost:3306': ******** #数据库密码
Validating local MySQL instance listening at port 3306 for use in an InnoDB cluster...
This instance reports its own address as localhost:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Checking whether existing tables comply with Group Replication requirements...
No incompatible tables detected
Checking instance configuration...
Instance configuration is compatible with InnoDB cluster
The instance 'localhost:3306' is valid for InnoDB cluster usage.
{
"status": "ok" #输出结果为ok即为正常,如果有错误,根据错误提示修复问题之后重新检查
}
MySQL localhost:33060+ ssl JS > dba.configureLocalInstance() #检查通过之后进行节点配置
Configuring local MySQL instance listening at port 3306 for use in an InnoDB cluster...
This instance reports its own address as localhost:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
The instance 'localhost:3306' is valid for InnoDB cluster usage.
The instance 'localhost:3306' is already ready for InnoDB cluster usage.
MySQL localhost:33060+ ssl JS > \quit #退出
Bye!
[root@MYSQL local]# chkconfig mysql on
操作设备:MySQL从节点(可以与MySQL主节点同时操作,注意修改my.cnf中的server_id,所有节点不允许重复)
在所有从节点上重复执行2.6中的步骤。
区别如下:my.cnf中的server_id修改,所有节点不允许重复。
操作设备:Router主节点
在MYSQL01(Router主节点)上通过MySQL Shell连接主服务节点,并创建集群。
过程同“2.6.9 安装MySQL Shell”。
[root@MYSQL01 local]# /usr/local/shell/bin/mysqlsh root@MYSQL03 #在MYSQL01连接主节点MYSQL03
Please provide the password for 'root@MYSQL03': ********
MySQL Shell 8.0.18
……
Server version: 8.0.18 MySQL Community Server - GPL
No default schema selected; type \use to set one.
MySQL MYSQL03:33060+ ssl JS > cluster.status(); #查看集群状态:集群尚未创建
ReferenceError: cluster is not defined
MySQL MYSQL03:33060+ ssl JS > var cluster = dba.createCluster('testCluster'); #创建集群
A new InnoDB cluster will be created on instance 'MYSQL03:3306'.
……
Adding Seed Instance...
Cluster successfully created. Use Cluster.addInstance() to add MySQL instances.
At least 3 instances are needed for the cluster to be able to withstand up to
one server failure.
MySQL MYSQL03:33060+ ssl JS > cluster.status(); #查看集群状态:主节点加入集群
{
"clusterName": "testCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "MYSQL03:3306",
"ssl": "REQUIRED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures.",
"topology": {
"MYSQL03:3306": {
"address": "MYSQL03:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.18"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "MYSQL03:3306"
}
操作设备:Router主节点
接2.8,将从节点加入集群:
MySQL MYSQL03:33060+ ssl JS > cluster.addInstance('root@MYSQL04'); #添加从节点MYSQL04
Please provide the password for 'root@MYSQL04': ********
……
Please select a recovery method [C]lone/[A]bort (default Abort): C #选择C(强制将主节点数据覆盖到从节点)即可
……
The instance 'MYSQL04' was successfully added to the cluster.
MySQL MYSQL03:33060+ ssl JS > cluster.addInstance('root@MYSQL05'); #添加从节点MYSQL05
Please provide the password for 'root@MYSQL05': ********
……
Please select a recovery method [C]lone/[A]bort (default Abort): C #选择C(强制将主节点数据覆盖到从节点)即可
……
The instance 'MYSQL05' was successfully added to the cluster.
MySQL MYSQL03:33060+ ssl JS > cluster.status(); #查看集群状态:此时能看到集群中有3个节点,status全都是ONLINE或者RECOVERING 状态,MYSQL03的模式(mode)是R/W,MYSQL04和MYSQL05是R/O
{
"clusterName": "testCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "MYSQL03:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"MYSQL03:3306": {
"address": "MYSQL03:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.18"
},
"MYSQL04:3306": {
"address": "MYSQL04:3306",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.18"
},
"MYSQL05:3306": {
"address": "MYSQL05:3306",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.18"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "MYSQL03:3306"
}
MySQL MYSQL03:33060+ ssl JS > \quit #退出
Bye!
操作设备:所有节点
[root@MYSQL03 ~]# /usr/local/shell/bin/mysqlsh root@MYSQL03 #可以在任意一个路由节点、主节点或从节点登录集群
Please provide the password for 'root@MYSQL03': ******** #输入数据库密码
……
MySQL MYSQL03:33060+ ssl JS > cluster.status(); #直接查看会报错说集群没有定义
ReferenceError: cluster is not defined
MySQL MYSQL03:33060+ ssl JS > cluster=dba.getCluster(); #先执行定义语句
MySQL MYSQL03:33060+ ssl JS > cluster.status(); #查看集群状态
{
"clusterName": "testCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "MYSQL03:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"MYSQL03:3306": {
"address": "MYSQL03:3306",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.18"
},
"MYSQL04:3306": {
"address": "MYSQL04:3306",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.18"
},
"MYSQL05:3306": {
"address": "MYSQL05:3306",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.18"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "MYSQL03:3306"
}
MySQL MYSQL03:33060+ ssl JS > \quit #退出
Bye!
操作设备:MySQL主节点、从节点
当需要选举主节点时(如之前的主节点宕机),集群会按照优先级来确定新的主节点。为了使主节点切换可控,为每个节点配置优先级。
在每个节点的/etc/my.cnf文件中加入以下内容(取消每行前的注释#),然后逐个节点重启MySQL:
#权重 选举主节点时权重最高的节点优先成为主节点
group_replication_member_weight=20 #可分别设置为30(主)、20(从)、10(从)
group_replication_unreachable_majority_timeout=10
group_replication_autorejoin_tries=2016
在三个节点先后重启MySQL:
[root@MYSQL03 local]# service mysql restart
Shutting down MySQL....... SUCCESS!
Starting MySQL.. SUCCESS!
操作设备:Router主节点
在MYSQL01上安装MySQL Router,初始化并启动。
1、安装
[root@MYSQL01 local]# tar -xvf mysql-router-8.0.18-linux-glibc2.12-x86_64.tar.xz
mysql-router-8.0.18-linux-glibc2.12-x86_64/bin/mysqlrouter_passwd
mysql-router-8.0.18-linux-glibc2.12-x86_64/bin/libcrypto.so.1.1
……
mysql-router-8.0.18-linux-glibc2.12-x86_64/README.router
mysql-router-8.0.18-linux-glibc2.12-x86_64/LICENSE.router
[root@MYSQL01 local]# rm -f mysql-router-8.0.18-linux-glibc2.12-86_64.tar.xz
[root@MYSQL01 local]# mv mysql-router-8.0.18-linux-glibc2.12-x86_64 router
2、初始化
[root@MYSQL01 local]# /usr/local/router/bin/mysqlrouter --bootstrap root@bigdata-01:3306 --user=root --force #root@MySQL集群主节点主机名
Please enter MySQL password for root:
# Bootstrapping system MySQL Router instance...
……
- Read/Only Connections: localhost:64470
3、修改配置文件/usr/local/router/mysqlrouter.conf
[root@MYSQL01 local]# vi /usr/local/router/mysqlrouter.conf
# File automatically generated during MySQL Router bootstrap
[DEFAULT]
name=system
user=root
keyring_path=/usr/local/router/var/lib/mysqlrouter/keyring
master_key_path=/usr/local/router/mysqlrouter.key
connect_timeout=15
read_timeout=30
dynamic_state=/usr/local/router/bin/../var/lib/mysqlrouter/state.json
[logger]
level = INFO
[metadata_cache:testCluster]
router_id=1
user=mysql_router1_3scto4nl2y4k
metadata_cluster=testCluster
ttl=0.5
use_gr_notifications=0
[routing:testCluster_default_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://testCluster/default?role=PRIMARY
routing_strategy=first-available
protocol=classic
max_connections=65535 #为6446端口和6447端口的配置项追加此行:增加最大连接数,默认只有512
[routing:testCluster_default_ro]
bind_address=0.0.0.0
bind_port=6447
destinations=metadata-cache://testCluster/default?role=SECONDARY
routing_strategy=round-robin-with-fallback
protocol=classic
max_connections=65535 #为6446端口和6447端口的配置项追加此行:增加最大连接数,默认只有512
#[routing:testCluster_default_x_rw] #删除/注释掉64460、64470端口的配置
#bind_address=0.0.0.0
#bind_port=64460
#destinations=metadata-cache://testCluster/default?role=PRIMARY
#routing_strategy=first-available
#protocol=x
#[routing:testCluster_default_x_ro] #删除/注释掉64460、64470端口的配置
#bind_address=0.0.0.0
#bind_port=64470
#destinations=metadata-cache://testCluster/default?role=SECONDARY
#routing_strategy=round-robin-with-fallback
#protocol=x
4、启动Router
[root@MYSQL01 local]# nohup /usr/local/router/bin/mysqlrouter -c /usr/local/router/mysqlrouter.conf >/var/log/mysqlrouter.log 2>&1 &
[1] 5605
加入开机启动项:将上述启动命令追加到/etc/rc.local中。
[root@MYSQL01 local]# vi /etc/rc.local
……
nohup /usr/local/router/bin/mysqlrouter -c /usr/local/router/mysqlrouter.conf >/var/log/mysqlrouter.log 2>&1 & #增加在文件末尾
操作设备:Router从节点
1、在MYSQL02上安装MySQL Shell。
过程同“2.6.9 安装MySQL Shell”。
2、在MYSQL02上安装MySQL Router,初始化并启动。
过程同“2.12 初始化Router”。
在本方案中,客户端的读写请求通过MySQL Router进行调度,最终由某一台MySQL Server来完成,实现负载均衡。
当MySQL Server主节点宕机或者服务停止时,其中一个从节点会成为新的主节点,从而实现MySQL服务的高可用性。
配置了一主一备两个MySQL Router来实现Router服务的高可用性。两个Router通过Keepalived虚拟出一个虚拟IP来对终端提供服务,当发生宕机或服务故障时,通过虚拟IP的自动切换来实现主备切换。从而实现Router服务的高可用性。
Keepalived是一个免费开源的,用C编写的类似于layer3,4 & 7交换机制软件,具备第3层、第4层和第7层交换机的功能。主要提供loadbalancing(负载均衡)和 high-availability(高可用)功能,负载均衡实现需要依赖Linux的虚拟服务内核模块(ipvs),而高可用是通过VRRP协议实现多台机器之间的故障转移服务。
上图是Keepalived的功能体系结构,大致分两层:用户空间(user space)和内核空间(kernel space)。
内核空间:主要包括IPVS(IP虚拟服务器,用于实现网络服务的负载均衡)和NETLINK(提供高级路由及其他相关的网络功能)两个部分。
用户空间:
WatchDog:负载监控checkers和VRRP进程的状况。
VRRP Stack:负载负载均衡器之间的失败切换FailOver,如果只用一个负载均稀器,则VRRP不是必须的。
Checkers:负责真实服务器的健康检查healthchecking,是keepalived最主要的功能。换言之,可以没有VRRP Stack,但健康检查healthchecking是一定要有的。
IPVS wrapper:用户发送设定的规则到内核ipvs代码。
Netlink Reflector:用来设定vrrp的vip地址等。
1、在MYSQL01和MYSQL02上分别安装Keepalived。
操作设备:Router主节点、从节点
[root@MYSQL01 local]# unzip keepalived.zip
Archive: keepalived.zip
creating: keepalived/
……
inflating: keepalived/perl-threads-1.87-4.el7.x86_64.rpm
inflating: keepalived/perl-threads-shared-1.43-6.el7.x86_64.rpm
[root@MYSQL01 local]# cd /usr/local/keepalived
[root@MYSQL01 keepalived]# rpm -ivh *.rpm --force --nodeps
Preparing... ################################# [100%]
Updating / installing...
1:perl-parent-1:0.225-244.el7 ################################# [ 3%]
2:perl-HTTP-Tiny-0.033-3.el7 ################################# [ 6%]
……
32:lm_sensors-3.4.0-4.20160601gitf91################################# [ 97%]
33:gpm-libs-1.20.7-5.el7 ################################# [100%]
2、编辑配置文件
(1)操作设备:Router主节点
[root@MYSQL01 local]# vi /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_DEVEL
}
vrrp_instance VI_1 {
state MASTER #主节点
interface ens192 #设置为正确的网卡
virtual_router_id 18 #主节点、从节点要一致
priority 100 #优先级高于从节点
advert_int 1
authentication {
auth_type PASS
auth_pass database
}
virtual_ipaddress {
192.168.10.55/16 #虚地址,注意不要与其它地址冲突,否则会挤走已有地址
}
notify_master "/sbin/arping -I ens192 -c 5 -s 192.168.10.55 192.168.1.254 &>/dev/null" #修改为本机虚地址的网卡(同interface)、虚地址IP、网关地址
}
(2)操作设备:Router从节点
[root@MYSQL02 local]# vi /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_DEVEL
}
vrrp_instance VI_1 {
state BACKUP #从节点
interface ens192 #设置为正确的网卡
virtual_router_id 18 #主节点、从节点要一致
priority 50 #优先级低于于主节点
advert_int 1
authentication {
auth_type PASS
auth_pass database
}
virtual_ipaddress {
192.168.10.55/16 #虚地址,注意不要与其它地址冲突,否则会挤走已有地址,造成I
}
notify_master "/sbin/arping -I ens192 -c 5 -s 192.168.10.55 192.168.1.254 &>/dev/null" #修改为本机虚地址的网卡(同interface)、虚地址IP、网关地址
}
操作设备:Router主节点、从节点
MYSQL01和MYSQL02上分别启动Keepalived并加入开机自启。
[root@MYSQL01 local]# systemctl restart keepalived
[root@MYSQL01 local]# systemctl enable keepalived
操作设备:Router主节点、从节点
1、为了解决主节点宕机恢复之后,备节点上已建立的的长连接不可用直到超时,影响业务的问题,给MYSQL01和MYSQL02的lo网卡上绑定虚地址。MYSQL01和MYSQL02上分别执行以下命令。
[root@MYSQL01 local]# vi /etc/init.d/realserver.sh
#!/bin/sh
# description:realserver
# jboss_vip start realserver
jboss_vip=192.168.10.55
/etc/rc.d/init.d/functions
case "$1" in
start)
ifconfig lo:0 $jboss_vip netmask 255.255.255.255 broadcast $jboss_vip
/sbin/route add -host $jboss_vip dev lo:0
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
sysctl -p >/dev/null 2>&1
echo "RealServer Start OK"
;;
stop)
ifconfig lo:0 down
route del $jboss_vip >/dev/null 2>&1
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "RealServer Stoped"
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
exit 0
2、然后赋予执行权限、启动脚本。
[root@MYSQL01 local]# chmod +x /etc/rc.d/init.d/functions
[root@MYSQL01 local]# chmod +x /etc/init.d/realserver.sh
[root@MYSQL01 local]# /etc/init.d/realserver.sh start
问题1:
[root@MYSQL01 local]# /etc/init.d/realserver.sh start
-bash: /etc/init.d/realserver.sh: /bin/sh^M: bad interpreter: No such file or directory
解决:dos2unix
[root@MYSQL01 local]# dos2unix /etc/init.d/realserver.sh
-bash: dos2unix: command not found
[root@MYSQL01 local]# yum -y install dos2unix
Loaded plugins: fastestmirror
Determining fastest mirrors
……
Complete!
[root@MYSQL01 local]# dos2unix /etc/init.d/realserver.sh
dos2unix: converting file /etc/init.d/realserver.sh to Unix format ...
[root@MYSQL01 local]# /etc/init.d/realserver.sh start
问题2:
[root@MYSQL01 local]# /etc/init.d/realserver.sh start
/etc/rc.d/init.d/functions: line 711: return: can only `return' from a function or sourced script
/etc/init.d/realserver.sh: line 16: ifconfig: command not found
/etc/init.d/realserver.sh: line 17: /sbin/route: No such file or directory
解决:net-tools
[root@MYSQL01 local]# yum -y install net-tools
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
……
Complete!
[root@MYSQL01 local]# /etc/init.d/realserver.sh start
/etc/rc.d/init.d/functions: line 711: return: can only `return' from a function or sourced script #在确认“chmod +x /etc/rc.d/init.d/functions”已执行后,可忽略此提示
RealServer Start OK
3、加入开机自启:向/etc/rc.local文件追加启动命令。
[root@MYSQL01 local]# vi /etc/rc.local
……
/etc/init.d/realserver.sh start #增加在文件末尾
操作设备:Router主节点、从节点
MYSQL01的网卡上绑定了虚IP:192.168.10.55,并且切换正常。
1、网卡绑定了虚拟IP即为正常。
[root@MYSQL01 local]# ip a
……
2: ens192: mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:df:2f:46 brd ff:ff:ff:ff:ff:ff
inet 192.168.9.77/16 brd 192.168.255.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet 192.168.10.55/16 scope global secondary ens192
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fedf:2f46/64 scope link
valid_lft forever preferred_lft forever
2、停掉MYSQL01的Keepalived,再次查看IP,虚IP消失。
[root@MYSQL01 local]# systemctl stop keepalived
[root@MYSQL01 local]# ip a
……
2: ens192: mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:df:2f:46 brd ff:ff:ff:ff:ff:ff
inet 192.168.9.77/16 brd 192.168.255.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fedf:2f46/64 scope link
valid_lft forever preferred_lft forever
3、 查看MYSQL02网卡,绑定上虚IP为正常。
[root@MYSQL02 local]# ip a
……
2: ens192: mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:6d:29:e8 brd ff:ff:ff:ff:ff:ff
inet 192.168.9.78/16 brd 192.168.255.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet 192.168.10.55/16 scope global secondary ens192
valid_lft forever preferred_lft forever
inet6 fe80::1d7a:93c:d185:9cfa/64 scope link noprefixroute
valid_lft forever preferred_lft forever
4、开启MYSQL01的Keepalived,再次查看IP,网卡绑定了虚拟IP即为正常。
[root@MYSQL01 local]# ip a
……
2: ens192: mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:df:2f:46 brd ff:ff:ff:ff:ff:ff
inet 192.168.9.77/16 brd 192.168.255.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet 192.168.10.55/16 scope global secondary ens192
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fedf:2f46/64 scope link
valid_lft forever preferred_lft forever
操作设备:Router主节点、从节点
1、经过安装配置Keepalived,仅能实现MYSQL01宕机时的虚拟IP切换,当MYSQL01服务器本身正常,但是Router服务停止时,不能实现切换,会造成服务停止,所以加入监测脚本来检查Router服务是否正常。以下操作在MYSQL01和MYSQL02上都执行:
[root@MYSQL01 local]# vi /usr/local/chk_router.sh
while [ 1 -eq 1 ]
do
#router进程是否正常
ROUTERPS=`ps -ef | grep mysqlrouter | grep -v grep | wc -l`
#keepalived进程是否正常
KEEPALIVEDPS=`ps -ef | grep keepalived | grep -v grep | wc -l`
#router停止,并且keepalived正常,停掉keepalived保证虚地址转移
if [[ $ROUTERPS -eq 0 ]];then
if [[ $KEEPALIVEDPS -gt 0 ]];then
service keepalived stop
fi
#router正常,如果keepalived已停止,重启keepalived
else
if [[ $KEEPALIVEDPS -eq 0 ]];then
service keepalived restart
fi
fi
sleep 2
done
[root@MYSQL01 local]# chmod +x /usr/local/chk_router.sh #赋予执行权限
2、启动脚本
[root@MYSQL01 local]# nohup /usr/local/chk_router.sh >/dev/null 2>&1 &
3、加入开机自启:向/etc/rc.local文件追加启动命令。
[root@MYSQL01 local]# vi /etc/rc.local
……
nohup /usr/local/chk_router.sh >/dev/null 2>&1 & #增加在文件末尾
1、可读写端口(6446)只有1个节点提供服务,不存在负载均衡。
2、只读端口(6447)有多个节点,可以通过使用MySQL客户端Navicat Premium多次访问6447端口,查看实际提供服务的服务器的hostname来验证负载均衡。
(1)在Navicat Premium新建查询。
(2)选择“192.168.10.55_6447”,执行命令,查看输出结果:
SHOW VARIABLES LIKE ‘%hostname%’;
(3)再新建若干个查询页面,重复(1)(2)。
(4)如果不同查询页面的输出结果在MYSQL04和MYSQL05之间变换,即可证明负载均衡有效。
1、首先验证可以通过MySQL客户端Navicat Premium访问虚地址的服务端口:
192.168.10.55:6446和192.168.10.55:6447。
2、停掉MYSQL01上的router服务。
查看Router的进程号,然后强制结束。
[root@MYSQL01 ~]# ps -ef | grep mysqlrouter | grep -v grep
root 18069 21730 0 09:55 pts/1 00:00:00 /usr/local/router/bin/mysqlrouter -c /usr/local/router/mysqlrouter.conf #进程号为18069
[root@MYSQL01 ~]# kill -9 18069
[root@MYSQL01 ~]# ip a #此时网卡ens192与虚拟IP解绑
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet 192.168.10.55/32 brd 192.168.10.55 scope global lo:0
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens192: mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:df:2f:46 brd ff:ff:ff:ff:ff:ff
inet 192.168.9.77/16 brd 192.168.255.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fedf:2f46/64 scope link
valid_lft forever preferred_lft forever
3、再次通过MySQL客户端Navicat Premium访问虚地址的服务端口,验证是否可以正常访问。
4、开启MYSQL01的Router,查看MYSQL01的IP,网卡绑定了虚拟IP即为正常。再次通过MySQL客户端Navicat Premium访问虚地址的服务端口,验证可以正常访问。
[root@MYSQL01 ~]# nohup /usr/local/router/bin/mysqlrouter -c /usr/local/router/mysqlrouter.conf >/dev/null 2>&1 &
[root@MYSQL01 ~]# ip a #此时网卡与虚拟IP绑定
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet 192.168.10.55/32 brd 192.168.10.55 scope global lo:0
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens192: mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:df:2f:46 brd ff:ff:ff:ff:ff:ff
inet 192.168.9.77/16 brd 192.168.255.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet 192.168.10.55/16 scope global secondary ens192
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fedf:2f46/64 scope link
valid_lft forever preferred_lft forever
5、直接关机MYSQL01,验证通过MySQL客户端Navicat Premium可以正常访问虚地址的服务端口。重新开机之后MYSQL01可以获取到虚拟IP。
6、断掉MYSQL01的网络(本示例中为虚拟机,可以在虚拟机客户端断开网卡连接实现,物理机可以通过拔掉网线实现),验证通过MySQL客户端Navicat Premium可以正常访问虚地址的服务端口。恢复网络之后MYSQL01可以重新获取虚IP并提供服务。
模拟意外故障:MYSQL03、MYSQL04和MYSQL05任意关机或断网一台,证通过MySQL客户端Navicat Premium可以正常访问虚地址的服务端口。
模拟有计划地停机:登录到MySQL Shell,移除任意两个节点,只剩下1个节点提供服务时,通过虚地址访问服务依然正常:
[root@MYSQL01 ~]# /usr/local/shell/bin/mysqlsh root@MYSQL03 #连接主节点
mysql-js> var cluster=dba.getCluster('testCluster')
mysql-js> cluster.removeInstance('root@MYSQL04')
mysql-js> cluster.removeInstance('root@MYSQL05')