RHEL——MySQL集群技术
# MySQL 集群(MySQL Cluster)是通过多台服务器协同工作,解决单点故障、提升读写性能、保障数据可靠性的高可用架构。常见方案包括主从复制(读写分离)、MHA(自动故障切换)、MGR(组复制)、分库分表等,实现数据库层面的高并发与高可用。
- 高可用(HA):单个节点故障不影响整体服务;
- 高扩展(Scalability):可通过增加节点提升处理能力;
- 数据一致性:集群内数据保持同步(不同架构一致性级别不同)
一、MySQL 实验环境
- 在企业中 90%的服务器操作系统均为 Linux
- Mysql 版本使用最多的是 Mysql5.7 和 Mysql8
- 在企业中对于 Mysql 的安装通常用源码编译的方式来进行
# Mysql下载链接:https://www.mysql.com/
1. 下载、源码编译 mysql
# 克隆母盘创建一台 mysql-N1 主机(192.168.153.10)来做数据库服务器
# 下载 mysql8.3 版本:https://downloads.mysql.com/archives/get/p/23/file/mysql-boost-8.3.0.tar.gz
# mysql 的数据较大,源码编译时时间较长(1小时左右)
- cmake:生成 Makefile(告诉 make 怎么编译)
- make:编译源代码,生成可执行文件
- make install:安装,把编译好的文件复制到系统目录
[root@mysql-N1 ~]# ip.sh eth0 192.168.153.10 mysql-N1
[root@mysql-N1 ~]# wget https://downloads.mysql.com/archives/get/p/23/file/mysql-boost-8.3.0.tar.gz
[root@mysql-N1 ~]# wget https://repo.almalinux.org/almalinux/9/CRB/x86_64/os/Packages/libtirpc-devel-1.3.3-9.el9.x86_64.rpm
[root@mysql-N1 ~]# dnf install ./libtirpc-devel-1.3.3-9.el9.x86_64.rpm -y
[root@mysql-N1 ~]# dnf install cmake3 gcc git bison openssl-devel ncurses-devel systemd-devel rpcgen.x86_64 libtirpc-devel-1.3.3-9.el9.x86_64.rpm gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils gcc-toolset-12-annobin-annocheck gcc-toolset-12-annobin-plugin-gcc -y
[root@mysql-N1 ~]# tar zxf mysql-boost-8.3.0.tar.gz
[root@mysql-N1 ~]# cd mysql-8.3.0/
[root@mysql-N1 mysql-8.3.0]# mkdir build
[root@mysql-N1 mysql-8.3.0]# cd build
[root@mysql-N1 build]# cmake3 .. -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/data/mysql -DMYSQL_UNIX_ADDR=/data/mysql/mysql.sock -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_EXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_unicode_ci -DWITH_BOOST=bundled -DWITH_SYSTEMD=1 -DWITH_SSL=system -DWITH_DEBUG=OFF
[root@mysql-N1 build]# make
2. 部署 mysql
# 将编译好的文件进行安装,设置 mysql 运行环境的环境变量,创建 mysql 用户
# 配置 mysql 主配置文件"/etc/my.cnf",指定数据目录和套接字文件
[root@mysql-N1 ~]# make install
[root@mysql-N1 ~]# ls /usr/local/mysql/
[root@mysql-N1 ~]# vim ~/.bash_profile
9 export PATH=$PATH:/usr/local/mysql/bin
[root@mysql-N1 ~]# source ~/.bash_profile
[root@mysql-N1 ~]# useradd -r -s /sbin/nologin -M mysql
[root@mysql-N1 ~]# mkdir -p /data/mysql
[root@mysql-N1 ~]# chown mysql.mysql /data/mysql/
[root@mysql-N1 ~]# vim /etc/my.cnf
1 [mysqld]
2 datadir=/data/mysql
3 socket=/data/mysql/mysql.sock
4 server-id=10
3. 数据结构初始化
- 创建系统数据库(mysql 库、performance_schema 等)
- 生成 root 临时密码(会在日志里显示)
- 创建数据目录结构(在指定的 /data/mysql 下)
- 设置权限(所有文件归 mysql 用户)
[root@mysql-N1 ~]# mysqld --initialize --user=mysql

4. 启动 mysql
# 启动方式
- 红帽7:启动命令“service mysqld start”,服务脚本位置“/etc/init.d/”
- 红帽9:启动命令“systemctl start mysqld”,服务脚本位置“/usr/lib/systemd/system/”
(1)红帽7启动
- 红帽9也能用这种方式,需要安装 initscripts
- initscripts 系统初始化脚本工具集,能使用 server、chkconfig 等命令
- service start/stop/restart/status(启动/停止/重启/状态)
- chkconfig 设置开机自启动;"--level 35"只在运行级别 3 和 5 开启自启;运行级别 3 = 多用户命令行模式;运行级别 5 = 图形界面模式
[root@mysql-N1 ~]# dnf install initscripts-10.11.8-4.el9.x86_64 -y
[root@mysql-N1 ~]# cd /usr/local/mysql/support-files/
[root@mysql-N1 support-files]# ls
[root@mysql-N1 support-files]# cp -p mysql.server /etc/init.d/mysqld
[root@mysql-N1 support-files]# service mysqld start
[root@mysql-N1 support-files]# chkconfig --level 35 mysqld on
(2)红帽9启动
[root@mysql-N1 ~]# ll /usr/local/mysql/usr/lib/systemd/system/mysqld.service
[root@mysql-N1 ~]# cp /usr/local/mysql/usr/lib/systemd/system/mysqld.service /usr/lib/systemd/system/
[root@mysql-N1 ~]# systemctl daemon-reload
[root@mysql-N1 ~]# systemctl start mysqld
[root@mysql-N1 ~]# systemctl enable --now mysqld
5. 安全初始化
# 在数据结构初始化时,有 root 临时密码,需要重新设置密码,提高数据库的安全性
[root@mysql-N1 ~]# mysql_secure_installation

# 测试 mysql 能否登录,并查看服务器 id 号
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> select @@server_id;
mysql> quit

6. 准备多台 mysql 主机
# 做好第一台数据库服务器后,还需要多台数据库服务器来做实验
- mysql-N2 (192.168.153.20)
- mysql-N3 (192.168.153.30)
# 在 mysql-N1 主机上将安装好的 mysql 的目录和主配置文件拷贝到 mysql-N2 和 mysql-N3 上
[root@mysqlN1 ~]# ls /usr/local/mysql/
[root@mysqlN1 ~]# for i in 20 30; do scp -r /usr/local/mysql 192.168.153.$i:/usr/local/; scp /etc/my.cnf 192.168.153.$i:/etc/; done
# 在 mysql-N2 主机上配置环境和初始化数据库
[root@mysql-N2 ~]# ls /usr/local/mysql/
[root@mysql-N2 ~]# vim ~/.bash_profile
9 export PATH=$PATH:/usr/local/mysql/bin
[root@mysql-N2 ~]# source ~/.bash_profile
[root@mysql-N2 ~]# useradd -r -s /sbin/nologin -M mysql
[root@mysql-N2 ~]# mkdir -p /data/mysql
[root@mysql-N2 ~]# chown mysql.mysql /data/mysql/
[root@mysql-N2 ~]# vim /etc/my.cnf
4 server-id=20
[root@mysql-N2 ~]# ll /usr/local/mysql/usr/lib/systemd/system/mysqld.service
[root@mysql-N2 ~]# cp /usr/local/mysql/usr/lib/systemd/system/mysqld.service /usr/lib/systemd/system/
[root@mysql-N2 ~]# systemctl daemon-reload
[root@mysql-N2 ~]# systemctl start mysqld
[root@mysql-N2 ~]# systemctl enable --now mysqld
[root@mysql-N2 ~]# mysqld --initialize --user=mysql
[root@mysql-N2 ~]# mysql_secure_installation
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> select @@server_id;
mysql> quit

# mysql-N3 和 mysql-N2 操作类似,只在服务器 id 有所不同
[root@mysql-N2 ~]# vim /etc/my.cnf
4 server-id=30

二、主从复制
# 设置一台数据库为主服务器,其余为从服务器
# 主库 master:mysql-N1
# 从库 slave:
- slave1——mysql-N2
- slave2——mysql-N3
1. 设置一主一从数据库
(1)实验环境
# 在3台主机中配置 mysql 主配置文件
[root@mysql-N1 ~]# vim /etc/my.cnf
1 [mysqld]
2 datadir=/data/mysql
3 socket=/data/mysql/mysql.sock
4 symbolic-links=0
5
6 server-id=10
7 log-bin=mysql-bin
[root@mysql-N1 ~]# systemctl restart mysqld.service
[root@mysql-N2 ~]# vim /etc/my.cnf
1 [mysqld]
2 datadir=/data/mysql
3 socket=/data/mysql/mysql.sock
4 symbolic-links=0
5
6 server-id=20
7 log-bin=mysql-bin
[root@mysql-N2 ~]# systemctl restart mysqld.service
[root@mysql-N3 ~]# vim /etc/my.cnf
1 [mysqld]
2 datadir=/data/mysql
3 socket=/data/mysql/mysql.sock
4 symbolic-links=0
5
6 server-id=30
7 log-bin=mysql-bin
[root@mysql-N3 ~]# systemctl restart mysqld.service

(2)创建数据库账号
# 创建 lee 用户,并授予复制权限;测试其他主机能否登陆 master 端数据库
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> SHOW VARIABLES LIKE 'default_authentication_plugin';
mysql> CREATE USER lee@'%' identified with mysql_native_password by 'lee';
mysql> SELECT USER from mysql.user;
mysql> GRANT replication slave ON *.* to lee@'%';
mysql> SHOW GRANTS FOR lee@'%';
mysql> quit
[root@mysql-N2 ~]# mysql -ulee -plee -h 192.168.153.10
mysql> quit


(3)配置一主一从数据库
# 查看日志状态,配置主从复制
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> SHOW MASTER STATUS;
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> CHANGE MASTER TO MASTER_HOST='192.168.153.10',MASTER_USER='lee',MASTER_PASSWORD='lee',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=659;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS \G;


# 测试主库创建的数据库,从库能否看到
# 创建 timinglee 数据库命令:create database timinglee;
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> show databases;
mysql> create database timinglee;
mysql> show databases;
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> show databases;


2. 向一主一从中加入新数据库
(1)模拟主库有数据情况
# 当主库有数据时,要先将数据库里的信息备份到新库中,将所有数据拉平
# 添加数据命令:INSERT INTO timinglee.userlist values ('user1','123');
# 删除数据命令:DELETE FROM timinglee.userlist where name='user1';
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> CREATE TABLE timinglee.userlist (
-> name VARCHAR(10) not null,
-> pass VARCHAR(50) not null
-> );
mysql> INSERT INTO timinglee.userlist values ('user1','123');
mysql> SELECT * FROM timinglee.userlist;
mysql> quit
[root@mysql-N1 ~]# mysqldump -uroot -p timinglee > timinglee.sql
Enter password:
[root@mysql-N1 ~]# scp timinglee.sql root@192.168.153.30:/root/
timinglee.sql
[root@mysql-N3 ~]# mysql -uroot -p -e "create database timinglee;"
Enter password:
[root@mysql-N3 ~]# mysql -uroot -p timinglee < timinglee.sql
Enter password:
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;


(2)将新库加入到主从结构中
# 查看日志状态,配置主从复制
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> SHOW MASTER STATUS;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> CHANGE MASTER TO MASTER_HOST='192.168.153.10',MASTER_USER='lee',MASTER_PASSWORD='lee',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=1406;
mysql> start slave;
mysql> show slave status \G;


(3)测试一主两从
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> INSERT INTO timinglee.userlist values ('user2','123');
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;


3. 延迟复制
# 当我们对数据库写入数据时,可能会造成某些错误数据,可以通过延迟复制来恢复数据
- 延迟复制时用来控制 sql 线程的,和 i/o 线程无关
- 这个延迟复制不是 i/o 线程过段时间来复制,i/o 是正常工作的
- 是日志已经保存在 slave 端了,那个 sql 要等多久进行回放
(1)设置延迟复制
# 在 slave1 端主机上设置60s延迟复制
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> STOP REPLICA;
mysql> CHANGE REPLICATION SOURCE TO SOURCE_DELAY=60;
mysql> START REPLICA;
mysql> quit
[root@mysql-N2 ~]# mysql -uroot -plee -e "show slave status \G;" | grep SQL_Delay

(2)测试延迟复制
# 在 master 端上修改数据,查看 slave 端的数据变化
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> delete from timinglee.userlist where name='user1';
mysql> select * from timinglee.userlist;
[root@mysql-N2 ~]# mysql -uroot -plee -e "select * from timinglee.userlist;"
[root@mysql-N2 ~]# sleep 60 && mysql -uroot -plee -e "select * from timinglee.userlist;"
[root@mysql-N3 ~]# mysql -uroot -plee -e "select * from timinglee.userlist;"

4. 慢查询日志
# 慢查询,顾名思义,执行很慢的查询
- 当执行 SQL 超过 long_query_time 参数设定的时间阈值(默认 10s)时,就被认为是慢查询,这个 SQL 语句就是需要优化的
- 慢查询被记录在慢查询日志里
- 慢查询日志默认是不开启的
- 如果需要优化 SQL 语句,就可以开启这个功能,它可以让你很容易地知道哪些语句是需要优化的
# 在 master 配置主配置文件(永久生效),开启慢日志并设定慢日志时长
# 临时生效
- set global slow_query_log=ON #开启慢日志
- set long_query_time=4 #设定慢日志时长为4
[root@mysql-N1 ~]# vim /etc/my.cnf
9 slow_query_log=ON
10 long_query_time=4
[root@mysql-N1 ~]# systemctl restart mysqld.service
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> show variables like "slow%";
mysql> show variables like "long%";
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -e "select sleep (3);"
[root@mysql-N1 ~]# mysql -uroot -plee -e "select sleep (4);"
[root@mysql-N1 ~]# cat /data/mysql/mysql-N1-slow.log


6. gtid 模式
# 当为启用 gtid 时我们要考虑的问题
- 在 master 端的写入时多用户读写,在 slave 端的复制时单线程日志回放,所以 slave 端一定会延迟与 master 端
- 这种延迟在 slave 端的延迟可能会不一致,当 master 挂掉后 slave 接管,一般会挑选一个和 master 延 迟日志最接近的充当新的 master
- 那么为接管 master 的主机继续充当 slave 角色并会指向到新的 master 上,作为其 slave
- 这时候按照之前的配置我们需要知道新的 master 上的 pos 的 id,但是我们无法确定新的 master 和 slave 之间差多少
# 当激活 GITD 之后
- 当 master 出现问题后,slave2 和 master 的数据最接近,会被作为新的 master
- slave1 指向新的 master,但是他不会去检测新的 master 的 pos id,只需要继续读取自己 gtid_next 即可
# GTID 工作流程
- 主库执行一个事务,提交后自动生成一个唯一的 GTID,记录到 binlog 里
- 从库读取主库的 binlog,先记录这个 GTID(标记为 “已收到”)
- 从库执行这个事务,执行完后把 GTID 标记为 “已执行”
- 主从同步时,从库只会向主库请求自己 “未执行” 的 GTID 对应的事务
(1)查看 gtid 状态
[root@mysql-N1 ~]# mysql -uroot -plee -e "show variables like '%gtid%';"

(2)开启 gtid 模式
# 在所有 mysql 主机中都开启 gtid 模式,并在此查看 gtid 模式是否开启
[root@mysql-N1 ~]# vim /etc/my.cnf
12 gtid_mode=ON
13 enforce_gtid_consistency=ON
[root@mysql-N1 ~]# systemctl restart mysqld.service
[root@mysql-N2 ~]# vim /etc/my.cnf
11 gtid_mode=ON
12 enforce_gtid_consistency=ON
[root@mysql-N2 ~]# systemctl restart mysqld.service
[root@mysql-N3 ~]# vim /etc/my.cnf
7 gtid_mode=ON
8 enforce_gtid_consistency=ON
[root@mysql-N3 ~]# systemctl restart mysqld.service
[root@mysql-N1 ~]# mysql -uroot -plee -e "show variables like '%gtid%';"

(3)测试 gtid 模式
# 在 slave 主机上开启 gtid 模式
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> stop slave;
mysql> CHANGE MASTER TO MASTER_HOST='192.168.153.10', MASTER_USER='lee', MASTER_PASSWORD='lee', MASTER_AUTO_POSITION=1;
mysql> start slave;
mysql> show slave status \G;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> stop slave;
mysql> CHANGE MASTER TO MASTER_HOST='192.168.153.10', MASTER_USER='lee', MASTER_PASSWORD='lee', MASTER_AUTO_POSITION=1;
mysql> start slave;
mysql> show slave status \G;

7. 并行复制(多线程回放)
# 默认情况下 slave 中使用的是 sql 单线程回放,在 master 中时多用户读写,如果使用 sql 单线程回放那么会造成组从延迟严重,开启 MySQL 的多线程回放可以解决上述问题
[root@mysql-N2 ~]# mysql -uroot -plee -e "show processlist;"
[root@mysql-N2 ~]# vim /etc/my.cnf
6 slave-parallel-type=LOGICAL_CLOCK
7 slave-parallel-workers=16
8 relay_log_recovery=ON
[root@mysql-N2 ~]# systemctl restart mysqld.service
[root@mysql-N2 ~]# mysql -uroot -plee -e "show processlist;"
[root@mysql-N2 ~]# mysql -uroot -plee -e "SHOW VARIABLES LIKE 'slave_parallel_workers';"
[root@mysql-N2 ~]# mysql -uroot -plee -e "SELECT COUNT(*) FROM performance_schema.threads WHERE NAME LIKE '%worker%';"


8. 原理解刨

(1)三个线程
# 实际上主从同步的原理就是基于 binlog 进行数据同步的。在主从复制过程中,会基于 3 个线程来操作, 一个主库线程,两个从库线程。
- 二进制日志转储线程(Binlog dump thread)是一个主库线程。当从库线程连接的时候, 主库可 以将二进制日志发送给从库,当主库读取事件(Event)的时候,会在 Binlog 上加锁,读取完成之 后,再将锁释放掉。
- 从库 I/O 线程会连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库 的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地的中继日志 (Relay log)。
- 从库 SQL 线程会读取从库中的中继日志,并且执行日志中的事件,将从库中的数据与主库保持同 步。
(2)复制三步骤
- 步骤 1:Master 将写操作记录到二进制日志(binlog)。
- 步骤 2:Slave 将 Master 的 binary log events 拷贝到它的中继日志(relay log);
- 步骤 3:Slave 重做中继日志中的事件,将改变应用到自己的数据库中。 MySQL 复制是异步的且串行化的,而且重启后从接入点开始复制。
(3)具体操作
- slaves 端中设置了 master 端的 ip、用户、日志和日志的 Position,通过这些信息取得 master 的认证及信息
- master 端在设定好 binlog 启动后会开启 binlog dump 的线程
- master 端的 binlog dump 把二进制的更新发送到 slave 端的
- slave 端开启两个线程,一个是 I/O 线程,一个是 sql 线程, i/o 线程用于接收 master 端的二进制日志,此线程会在本地打开 relay log 中继日志,并且保存到本地磁盘 sql 线程读取本地 relay log 中继日志进行回放
- 什么时候我们需要多个 slave? 一当读取的而操作远远高与写操作时,我们采用一主多从架构; 二数据库外层接入负载均衡层并搭配高可用机制
(4)架构缺陷
# 主从架构采用的是异步机制
- master 更新完成后直接发送二进制日志到 slave,但是 slaves 是否真正保存了数据 master 端不会检测 master 端直接保存二进制日志到磁盘
- 当 master 端到 slave 端的网络出现问题时或者 master 端直接挂掉,二进制日志可能根本没有到达 slave
- master 出现问题 slave 端接管 master,这个过程中数据就丢失了
- 这样的问题出现就无法达到数据的强一致性,零数据丢失
三、半同步模式
1. 半同步模式原理
- 用户线程写入完成后 master 中的 dump 会把日志推送到 slave 端
- slave 中的 io 线程接收后保存到 relaylog 中继日志
- 保存完成后 slave 向 master 端返回 ack
- 在未接受到 slave 的 ack 时 master 端时不做提交的,一直处于等待当收到 ack 后提交到存储引擎
- 在 5.6 版本中用到的时 after_commit 模式,after_commit 模式时先提交在等待 ack 返回后输出 ok
2. 开启半同步模式
(1)设定 master 半同步
# 在 master 主机中设定半同步模式,配置主配置文件后一定不能重启(因为已经设定了半同步模式,重启会起不来的;如果需要重启则先把半同步模式注释掉)
# 开启半同步模式后查看半同步模式状态
[root@mysql-N1 ~]# vim /etc/my.cnf
15 rpl_semi_sync_master_enabled=1
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
mysql> SET GLOBAL rpl_semi_sync_master_enabled=1;
mysql>
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%';
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';


# 查看 mysql 安装所有插件
mysql> SELECT * FROM INFORMATION_SCHEMA.PLUGINS;

# 删除插件(不需要执行,仅知道命令即可),当安装错插件或不需要插件时,可用这条命令
mysql> UNINSTALL PLUGIN rpl_semi_sync_master;
(2)设定 slave 半同步
# 在 slave 主机中设定半同步模式,注意配置主配置文件后也不能重启
[root@mysql-N2 ~]# vim /etc/my.cnf
14 rpl_semi_sync_slave_enabled=1
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
mysql> SET GLOBAL rpl_semi_sync_slave_enabled=1;
mysql> STOP SLAVE IO_THREAD;
mysql> START SLAVE IO_THREAD;
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
[root@mysql-N3 ~]# vim /etc/my.cnf
10 rpl_semi_sync_slave_enabled=1
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
mysql> SET GLOBAL rpl_semi_sync_slave_enabled=1;
mysql> STOP SLAVE IO_THREAD;
mysql> START SLAVE IO_THREAD;
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';

3. 测试半同步模式
(1)所有 mysql 主机正常
# 在所有 mysql 主机都正常运行情况下,查看 master 数据库的表数据,并进行增加数据操作
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;
mysql> insert into timinglee.userlist values ('user1','123');
mysql> select * from timinglee.userlist;
mysql> show status like 'rpl_semi_sync%';
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;


(2)所有 slave 主机都挂了
# 当所有 slave 主机都挂了,在 master 添加数据时,查看半同步模式状态
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> stop slave io_thread;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> stop slave io_thread;
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> insert into timinglee.userlist values ('user3','123');
mysql> show status like 'rpl_semi_sync%';
mysql> select * from timinglee.userlist;
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;


(3)当 slave 主机激活
# 当从库再次激活,主库则会自动开启半同步模式
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> start slave io_thread;
mysql> select * from timinglee.userlist;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> start slave io_thread;
mysql> select * from timinglee.userlist;
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> show status like 'rpl_semi_sync%';


四、MySQL 高可用之 MHA
# 当 master 出现问题时,MHA 可以解决 master 单点问题
1. MHA 概述
(1)什么是 MHA
- MHA(Master High Availability)是一套优秀的 MySQL 高可用环境下故障切换和主从复制的软件
- MHA 的出现就是解决 MySQL 单点的问题
- MySQL 故障切换过程中,MHA 能做到 0-30 秒内自动完成故障切换操作
- MHA 能在故障切换的过程中最大程度上保证数据的一致性,以达到真正意义上的高可用
(2)MHA 的组成
- MHA 由两部分组成: MHAManager (管理节点) MHA Node (数据库节点)
- MHA Manager 可以单独部署在一台独立的机器上管理多个 master-slave 集群,也可以部署在一台 slave 节点上
- MHA Manager 会定时探测集群中的 master 节点
- 当 master 出现故障时,它可以自动将最新数据的 slave 提升为新的 master, 然后将所有其他的 slave 重新指向新的 master
(3)MHA 的特点
- 自动故障切换过程中,MHA 从宕机的主服务器上保存二进制日志,最大程度的保证数据不丢失
- 使用半同步复制,可以大大降低数据丢失的风险,如果只有一个 slave 已经收到了最新的二进制日志,MHA 可以将最新的二进制日志应用于其他所有的 slave 服务器上,因此可以保证所有节点的数据一致性
- 目前 MHA 支持一主多从架构,最少三台服务,即一主两从
(4)故障切换备选主库的算法
# 一般判断从库的是从(position/GTID)判断优劣,数据有差异,最接近于 master 的 slave,成为备选主
# 数据一致的情况下,按照配置文件顺序,选择备选主库
# 设定有权重(candidate_master = 1),按照权重强制指定备选主
- 默认情况下如果一个 slave 落后 master 100M 的 relay logs 的话,即使有权重,也会失效
- 如果 check_repl_delay = 0 的话,即使落后很多日志,也强制选择其为备选主
(5)MHA 工作原理
- 目前 MHA 主要支持一主多从的架构,要搭建 MHA, 要求一个复制集群必须最少有 3 台数据库服务器,一主二从,即一台充当 Master,台充当备用 Master,另一台充当从库
- MHA Node 运行在每台 MySQL 服务器上
- MHAManager 会定时探测集群中的 master 节点
- 当 master 出现故障时,它可以自动将最新数据的 slave 提升为新的 master
- 然后将所有其他的 slave 重新指向新的 master,VIP 自动漂移到新的 master
- 整个故障转移过程对应用程序完全透明
2. 初始化 mysql 环境
# 为了保证 MHA 能顺利部署,将所有 mysql 主机进行数据初始化,保证数据一致性
(1)备份数据库
# 如果数据库里的数据需要保留,则要在数据库初始化之前,将数据库进行备份
# 在主库中备份所有数据库(包括用户权限)
[root@mysql-node1 ~]# mysqldump -uroot -p --all-databases --flush-privileges > /root/alldb_backup.sql
(2)简化 mysql 配置
# 在部署 MHA 时,为了方便实验,去掉一些不必要的 mysql 配置参数
# 所有 mysql 主机都进行相同操作,只有"server-id"不一样,其他参数都是一致的;注意 id!
[root@mysql-N1 ~]# systemctl stop mysqld.service
[root@mysql-N1 ~]# vim /etc/my.cnf
1 [mysqld]
2 datadir=/data/mysql
3 socket=/data/mysql/mysql.sock
4 symbolic-links=0
5
6 server-id=10
7 log-bin=mysql-bin
8
9 gtid_mode=ON
10 enforce_gtid_consistency=ON
(3)初始化数据
# 对所有 mysql 主机进行相同操作,将数据删除,保证数据一致
[root@mysql-N1 ~]# rm -rf /data/mysql/*
[root@mysql-N1 ~]# mysqld --initialize --user mysql
[root@mysql-N1 ~]# systemctl restart mysqld.service
[root@mysql-N1 ~]# mysql_secure_installation
(4)搭建主从复制
# 在 master 端上创建复制专用用户lee,修改认证方式并授予复制从库权限
[root@mysql-N1 ~]# mysql -uroot -plee -e "create user lee@'%' identified with mysql_native_password by 'lee';"
[root@mysql-N1 ~]# mysql -uroot -plee -e "GRANT replication slave ON *.* to lee@'%';"
[root@mysql-N1 ~]# mysql -uroot -plee -e "show master status;"

# 在 slave 端上宣告主库,开启 gtid 自动定位,并启动复制线程
[root@mysql-N2 ~]# mysql -uroot -plee -e "CHANGE MASTER TO MASTER_HOST='192.168.153.10', MASTER_USER='lee', MASTER_PASSWORD='lee', MASTER_AUTO_POSITION=1;"
[root@mysql-N2 ~]# mysql -uroot -plee -e "start slave;"
[root@mysql-N2 ~]# mysql -uroot -plee -e "show slave status\G;"
[root@mysql-N3 ~]# mysql -uroot -plee -e "CHANGE MASTER TO MASTER_HOST='192.168.153.10', MASTER_USER='lee', MASTER_PASSWORD='lee', MASTER_AUTO_POSITION=1;"
[root@mysql-N3 ~]# mysql -uroot -plee -e "start slave;"
[root@mysql-N3 ~]# mysql -uroot -plee -e "show slave status\G;"

3. MHA 部署
# MHA 实验环境至少3台数据库服务器(一主两从)和1台 mha 主机,所以需要4台主机
(1)配置 MHA-Manager
# 将 MHA-7.zip 软件包上传至 mha 主机,进行解压
[root@mha ~]# ls
[root@mha ~]# unzip MHA-7.zip

# 在 RHEL9 版本中,需要安装 perl 环境才能安装 mha
# 安装 perl(MHA用的Perl语言编译器)、perl-DBD-Mysql(Perl连接Mysql数据库驱动)和 perl-CPAN(Perl 模块的包管理器,方便后续安装其他 Perl 模块)
[root@mha ~]# cd MHA-7/
[root@mha MHA-7]# dnf install perl perl-DBD-MySQL perl-CPAN -y
# 进入 CPAN 窗口,安装 Config::Tiny、Log::Dispatch、Mail::Sender、Parallel::ForkManager 模块(安装时可能会很慢很卡,可以多退出安装几次;或者搞个加速器加快下载速度)
# 解压 cpan_backup.tar.gz 文件,可以快速下载 mha 模块
[root@mha MHA-7]# cpan
Would you like to configure as much as possible automatically? [yes] yes
cpan[1]> install Config::Tiny
cpan[2]> install Log::Dispatch
cpan[3]> install Mail::Sender
Specify defaults for Mail::Sender? (y/N) y
Default encoding of message bodies (N)one, (Q)uoted-printable, (B)ase64: n
······
cpan[4]> install Parallel::ForkManager
cpan[5]> exit





# 强制安装 manager 和 node 包,忽略依赖关系
# Manager 工具包
- masterha_check_ssh #检查 MHA 的 SSH 配置状况
- masterha_check_repl #检查 MySQL 复制状况
- masterha_manger #启动 MHA
- masterha_check_status #检测当前 MHA 运行状态
- masterha_master_monitor #检测 master 是否宕机
- masterha_master_switch #控制故障转移(自动或者手动)
- masterha_conf_host #添加或删除配置的 server 信息
# Node 工具包(通常由 masterHA 主机直接调用,无需人为执行)
- save_binary_logs #保存和复制 master 的二进制日志
- apply_diff_relay_logs #识别差异的中继日志事件并将其差异的事件应用于其他的 slave
- filter_mysqlbinlog #去除不必要的 ROLLBACK 事件(MHA 已不再使用这个工具)
- purge_relay_logs #清除中继日志(不会阻塞 SQL 线程)
[root@mha MHA-7]# rpm -ivh mha4mysql-manager-0.58-0.el7.centos.noarch.rpm mha4mysql-node-0.58-0.el7.centos.noarch.rpm --nodeps

(2)在 mysql 主机中安装模块和依赖
# MHA 主机和 mysql 主机都能远程无密登录,在母盘做过:https://blog.csdn.net/Forget_8/article/details/157298700?spm=1001.2014.3001.5502#t11
# 将 MHA 的节点端包安装到 mysql 主机上
[root@mha MHA-7]# for i in 10 20 30; do scp mha4mysql-node-0.58-0.el7.centos.noarch.rpm root@192.168.153.$i:/mnt; ssh -l root 192.168.153.$i "rpm -ivh /mnt/mha4mysql-node-0.58-0.el7.centos.noarch.rpm --nodeps"; done

# 在所有 mysql 主机上安装模块(注意是所有,即 master 和 slave 端做相同操作),与 mha 主机一样上传解压 cpan_backup.tar.gz 文件
# 安装好模块后并进行检测是否安装成功
[root@mysql-node1 ~]# dnf install perl perl-DBD-MySQL perl-CPAN -y
[root@mysql-node1 ~]# tar zxf cpan_plugin.tar.gz
[root@mysql-node1 ~]# cpan
cpan[1]> install Config::Tiny
cpan[2]> install Log::Dispatch
cpan[3]> install Mail::Sender
Specify defaults for Mail::Sender? (y/N) y
Default encoding of message bodies (N)one, (Q)uoted-printable, (B)ase64: n
cpan[4]> install Parallel::ForkManager
cpan[5]> exit
[root@mysql-node1 ~]# perl -MConfig::Tiny -e 'print "OK\n"'
[root@mysql-node1 ~]# perl -MLog::Dispatch -e 'print "OK\n"'
[root@mysql-node1 ~]# perl -MMail::Sender -e 'print "OK\n"'
[root@mysql-node1 ~]# perl -MParallel::ForkManager -e 'print "OK\n"'



(3)修改 MHA-Manager 中的检测代码
# MHA 需要通过数字来比较 mysql 版本号,所以需要将字符串转变为数字
[root@mha MHA-7]# vim /usr/share/perl5/vendor_perl/MHA/NodeUtil.pm
205 sub parse_mysql_major_version($) {
206 my $str = shift;
207 my @nums = $str =~ m/(\d+)/g;
208 my $result = sprintf( '%03d%03d', $nums[0]//0, $nums[1]//0 );
209 return $result;
210 }

(4)为 MHA 建立远程登录用户
# 给所有主机都能以root用户连接数据库,并赋予所有数据库所有权限
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> create user root@'%' identified with mysql_native_password by 'lee';
mysql> grant all on *.* to root@'%';

(5)设定 MHA 管理 Mysql 集群
# 生成 MHA-manager 的配置文件模板
[root@mha MHA-7]# tar zxf mha4mysql-manager-0.58.tar.gz
[root@mha MHA-7]# cd mha4mysql-manager-0.58/
[root@mha mha4mysql-manager-0.58]# mkdir /etc/masterha -p
[root@mha mha4mysql-manager-0.58]# cat samples/conf/masterha_default.cnf samples/conf/app1.cnf > /etc/masterha/app1.cnf
# 编辑配置文件
[root@mha mha4mysql-manager-0.58]# vim /etc/masterha/app1.cnf
1 [server default]
2 user=root
3 password=lee
4 ssh_user=root
5 repl_user=lee
6 repl_password=lee
7 master_binlog_dir=/data/mysql
8 remote_workdir=/tmp
9 secondary_check_script= masterha_secondary_check -s 192.168.153.10 -s 192.168.153.20
10 ping_interval=3
11 # master_ip_failover_script= /script/masterha/master_ip_failover
12 # shutdown_script= /script/masterha/power_manager
13 # report_script= /script/masterha/send_report
14 # master_ip_online_change_script= /script/masterha/master_ip_online_change
15 [server default]
16 manager_workdir=/etc/masterha
17 manager_log=/etc/masterha/mha.log
18
19 [server1]
20 hostname=192.168.153.10
21 candidate_master=1
22 check_repl_delay=0
23
24 [server2]
25 hostname=192.168.153.20
26 candidate_master=1
27 check_repl_delay=0
28
29 [server3]
30 hostname=192.168.153.30
31 no_master=1

(6)测试 MHA 环境
# 用 MHA 检查 SSH 互通性
[root@mha ~]# masterha_check_ssh --conf=/etc/masterha/app1.cnf

# 用 MHA 检测 Mysql 主从复制的健康状态
[root@mha ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf

4. MHA 故障切换
(1)切换过程
- 配置文件检查阶段,这个阶段会检查整个集群配置文件配置
- 宕机的 master 处理,这个阶段包括虚拟 ip 摘除操作,主机关机操作
- 复制 dead master 和最新 slave 相差的 relay log,并保存到 MHA Manger 具体的目录下
- 识别含有最新更新的 slave
- 应用从 master 保存的二进制日志事件(binlog events)
- 提升一个 slave 为新的 master 进行复制
- 使其他的 slave 连接新的 master 进行复制
(2)手动切换
# 手动切换也分为 master 无故障和 master 故障切换
### 在 master 未出现故障时进行手动切换主库
# 查看当前 master 是谁
# 当前环境,mysql-N1 为主库,mysql-N2 和 mysql-N3 为从库
[root@mysql-N2 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15
[root@mysql-N3 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15

# 手动切换 master
# 切换时需要输入3次 yes
- 第一次:FLUSH TABLES 确认——在切换前刷新表
- 第二次:开始切换确认——是否真进行切换
- 第三次:无 VIP 脚本确认——未配 vip 则 MHA 无法自动漂移 IP
[root@mha ~]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=192.168.153.20 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
······
It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.153.10(192.168.153.10:3306)? (YES/no): yes
······
Starting master switch from 192.168.153.10(192.168.153.10:3306) to 192.168.153.20(192.168.153.20:3306)? (yes/NO): yes
······
master_ip_online_change_script is not defined. If you do not disable writes on the current master manually, applications keep writing on the current master. Is it ok to proceed? (yes/NO): yes


# 再次检查 master 是谁
[root@mysql-N1 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15
[root@mysql-N3 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15

### 当 master 出现故障时进行手动切换主库
# 当前环境,mysql-N2 为主库,mysql-N1 和 mysql-N3 为从库
# 当主库挂了,查看状态
[root@mysql-N2 ~]# systemctl stop mysqld.service
[root@mysql-N3 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15

# 进行手动切换,2次 yes 回复;并进行测试
[root@mha ~]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=dead --dead_master_host=192.168.153.20 --dead_master_port=3306 --new_master_host=192.168.153.10 --new_master_port=3306 --ignore_last_failover
[root@mysql-N3 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15



# 修复故障主机
- 当故障主机切换后,mha 主机会出现切换锁文件 app1.failover.complete,若该锁文件存在则不能再次执行切换
- 在将要成为新从库的主机下清除所有从库复制配置信息
- 故障主机(即要成为新从库) mysql-N2 需要重新以从库身份加入集群
# 为什么要清楚从库配置信息
- 当所有主机都是正常状态下,如果一台主机有两份从库配置会导致主库切换失败
- 当一台主机在是主库的状态下又有从库配置,然后宕机后成功切换主库操作时,需要重配置从库信息才能加入集群,但因原先就有从库配置会导致加入集群失败
[root@mha ~]# ls /etc/masterha/
app1.cnf app1.failover.complete
[root@mha ~]# rm -rf /etc/masterha/app1.failover.complete
[root@mysql-N2 ~]# systemctl start mysqld.service
[root@mysql-N2 ~]# mysql -uroot -plee -e "reset slave;"
[root@mysql-N2 ~]# mysql -uroot -plee -e "CHANGE MASTER TO MASTER_HOST='192.168.153.10', MASTER_USER='lee', MASTER_PASSWORD='lee', MASTER_AUTO_POSITION=1;"
[root@mysql-N2 ~]# mysql -uroot -plee -e "start slave;"
[root@mysql-N2 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15

(3)自动切换
# 当主库挂了,则会自动切换优先级高的从库为新主库,且自动切换只能进行一次(有锁文件存在)
# 当前环境,mysql-N1 为主库,mysql-N2 和 mysql-N3 为从库
# 测试环境,然后开启自动切换,并进入后台观察
[root@mha ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf
[root@mha ~]# nohup masterha_manager --conf=/etc/masterha/app1.cnf > /dev/null 2>&1 &
[root@mha ~]# jobs
[root@mha ~]# > /etc/masterha/*.log
[root@mha ~]# watch -n 1 cat /etc/masterha/mha.log
[root@mysql-N1 ~]# systemctl stop mysqld.service
[root@mysql-N3 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15


# 和手动切换故障主机一样,需要恢复故障
- 删除锁文件
- 清空原从库的配置信息
- 将故障主机以从库身份加入集群
[root@mha ~]# rm -rf /etc/masterha/app1.failover.complete
[root@mysql-N1 ~]# systemctl start mysqld.service
[root@mysql-N1 ~]# mysql -uroot -plee -e "reset slave;"
[root@mysql-N1 ~]# mysql -uroot -plee -e "CHANGE MASTER TO MASTER_HOST='192.168.153.20',MASTER_USER='lee',MASTER_PASSWORD='lee',MASTER_AUTO_POSITION=1;"
[root@mysql-N1 ~]# mysql -uroot -plee -e "start slave;"
[root@mysql-N1 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15

5. VIP 功能
# 开启 VIP 功能,实现应用无感知的故障转移
# 如果没有 VIP,当主库宕机了,mha 切换了新主库,但应用连接的还是老主库,则应用会报错,导致需要对老主库手动修改配置或重启
(1)开启 VIP 功能
# 还原环境,mysql-N1 为主库,mysql-N2 和 mysql-N3 为从库,并测试环境
# 防止因为一台主机下会生成两份从库配置而导致集群还原失败,直接对能优先成为主库的主机都进行从库配置清除
[root@mysql-N1 ~]# mysql -uroot -plee -e "reset slave;"
[root@mysql-N2 ~]# mysql -uroot -plee -e "reset slave;"
[root@mha ~]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=192.168.153.10 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
[root@mha ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf
[root@mysql-N2 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15
# 创建 vip 脚本
[root@mha ~]# ll MHA-7/master_ip_*
[root@mha ~]# mkdir /etc/masterha/scripts
[root@mha ~]# cp MHA-7/master_ip_* /etc/masterha/scripts/
[root@mha ~]# vim /etc/masterha/app1.cnf
11 master_ip_failover_script=/etc/masterha/scripts/master_ip_failover
14 master_ip_online_change_script=/etc/masterha/scripts/master_ip_online_change

# 在 mha 主机上编写脚本并授予执行权限,设定 vip(vip为192.168.153.200);在主库上添加 vip
[root@mha ~]# vim /etc/masterha/scripts/master_ip_failover
11 my $vip = '192.168.153.200/24';
[root@mha ~]# chmod +x /etc/masterha/scripts/master_ip_failover
[root@mha ~]# vim /etc/masterha/scripts/master_ip_online_change
7 my $vip = '192.168.153.200/24';
[root@mha ~]# chmod +x /etc/masterha/scripts/master_ip_online_change
[root@mysql-N1 ~]# nmcli connection modify eth0 +ipv4.addresses 192.168.153.200/24
[root@mysql-N1 ~]# nmcli connection reload
[root@mysql-N1 ~]# nmcli connection up eth0
[root@mysql-N1 ~]# ip a

(2)测试 vip 自动切换
# 当主库 mysql-N1 挂了,查看 vip 是否跟主库一起转移
[root@mha ~]# nohup masterha_manager --conf=/etc/masterha/app1.cnf > /dev/null 2>&1 &
[root@mysql-N1 ~]# systemctl stop mysqld.service
[root@mysql-N3 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15
[root@mysql-N2 ~]# ip a

(3)测试 vip 手动切换
# 先删除锁文件,清除现主库的从库配置信息,将老主库加入集群
[root@mha ~]# rm -rf /etc/masterha/app1.failover.complete
[root@mysql-N2 ~]# mysql -uroot -plee -e "reset slave;"
[root@mysql-N1 ~]# systemctl start mysqld.service
[root@mysql-N1 ~]# mysql -uroot -plee -e "CHANGE MASTER TO MASTER_HOST='192.168.153.20', MASTER_USER='lee', MASTER_PASSWORD='lee', MASTER_AUTO_POSITION=1;"
[root@mysql-N1 ~]# mysql -uroot -plee -e "start slave;"
[root@mysql-N1 ~]# mysql -uroot -plee -e "show slave status\G;" | head -n 15
# 进行 vip 手动切换
[root@mha ~]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=192.168.153.10 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
[root@mysql-N1 ~]# ip a

五、MySQL 高可用之 MGR(组复制)
# 组复制解决了传统主从复制在故障转移时可能丢数据、出现脑裂的问题,实现了数据强一致和自动化切换
1. 组复制介绍
(1)组复制介绍
- 组复制是 MySQL 5.7.17 版本出现的新特性,它提供了高可用、高扩展、高可靠的 MySQL 集群服务
- MySQL 组复制分单主模式和多主模式,传统的 mysql 复制技术仅解决了数据同步的问题
- MGR 对属于同一组的服务器自动进行协调,对于要提交的事务,组成员必须就全局事务序列中给定事务的顺序达成一致
- 提交或回滚事务由每个服务器单独完成,但所有服务器都必须做出相同的决定
(2)组复制流程
# 首先我们将多个节点共同组成一个复制组,在执行读写(RW)事务的时候,需要通过一致性协议层 (Consensus 层)的同意,也就是读写事务想要进行提交,必须要经过组里“大多数人”(对应 Node 节点)的同意,大多数指的是同意的节点数量需要大于 (N/2+1),这样才可以进行提交,而不是原发起方一个说了算
# 而针对只读(RO)事务则不需要经过组内同意,直接提交即可
# 组成组复制的多个节点不能超过9个

(3)单主模式
# single-primary mode(单写或单主模式): 单写模式 group 内只有一台节点可写可读,其他节点只可以读。当主服务器失败时,会自动选择新的主服务器。

(4)多主模式
# multi-primary mode(多写或多主模式):组内的所有机器都是 primary 节点,同时可以进行读写操作,并且数据是最终一致的。

2. MGR 组复制部署
# MGR 实验环境至少需要3台 mysql 主机,最多9台组成组复制
(1)还原 mysql 环境
# 注意:还原环境前,如有需要保存数据库数据的需求话,现将数据进行备份
[root@mysql-node1 ~]# mysqldump -uroot -p --all-databases --flush-privileges > /root/alldb_backup.sql
# 对所有 mysql 主机(节点)进行关闭服务并删除数据文件,然后添加 MGR 基础参数
[root@mysql-N1 ~]# for i in 10 20 30; do ssh root@192.168.153.$i "systemctl stop mysqld;rm -rf /data/mysql/*"; done
[root@mysql-N1 ~]# for i in 10 20 30; do
ssh root@192.168.153.$i "cat >> /etc/my.cnf << EOF
default_authentication_plugin=mysql_native_password
log_slave_updates=ON
binlog_format=ROW
binlog_checksum=NONE
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
EOF"; done
[root@mysql-N1 ~]# cat /etc/my.cnf
[root@mysql-N2 ~]# cat /etc/my.cnf
[root@mysql-N3 ~]# cat /etc/my.cnf


# 初始化数据结构
[root@mysql-N1 ~]# mysqld --initialize --user mysql
[root@mysql-N2 ~]# mysqld --initialize --user mysql
[root@mysql-N3 ~]# mysqld --initialize --user mysql
(2)配置功能参数
# 设置所有 mysql 节点,并配置组复制功能参数
# 注意
- 每个节点对组内部通信的 ip 是不一样的
- 因为我们设置了参数禁止虚拟机开启自启动组复制,所以当虚拟机重启时,需要在 mysql 窗口下手动执行"START GROUP_REPLICATION;"命令
[root@mysql-N1 ~]# vim /etc/hosts
3 192.168.153.10 mysql-N1
4 192.168.153.20 mysql-N2
5 192.168.153.30 mysql-N3
[root@mysql-N1 ~]# for i in 20 30;do scp /etc/hosts 192.168.153.$i:/etc/hosts;done
[root@mysql-N1 ~]# for i in 10 20 30; do
ssh root@192.168.153.$i "cat >> /etc/my.cnf << EOF
plugin_load_add='group_replication.so'
group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_start_on_boot=off
group_replication_local_address="192.168.153.10:33061"
group_replication_group_seeds="192.168.153.10:33061,192.168.153.20:33061,192.168.153.30:33061"
group_replication_bootstrap_group=off
group_replication_single_primary_mode=OFF
EOF"; done
[root@mysql-N1 ~]# cat /etc/my.cnf
[root@mysql-N2 ~]# vim /etc/my.cnf
21 group_replication_local_address=192.168.153.20:33061
[root@mysql-N2 ~]# cat /etc/my.cnf
[root@mysql-N3 ~]# vim /etc/my.cnf
21 group_replication_local_address=192.168.153.30:33061
[root@mysql-N3 ~]# cat /etc/my.cnf


(3)开启 MGR 集群
# 在主库创建 MGR 集群的第一个节点(引导节点)
[root@mysql-N1 ~]# systemctl restart mysqld.service
[root@mysql-N1 ~]# mysql -uroot -p'EWI;kHDX%2/X'
mysql> alter user root@localhost identified by 'lee';
mysql> SET SQL_LOG_BIN=0;
mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'lee';
mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
mysql> GRANT CONNECTION_ADMIN ON *.* TO rpl_user@'%';
mysql> GRANT BACKUP_ADMIN ON *.* TO rpl_user@'%';
mysql> GRANT GROUP_REPLICATION_STREAM ON *.* TO rpl_user@'%';
mysql> FLUSH PRIVILEGES;
mysql> SET SQL_LOG_BIN=1;
mysql> CHANGE REPLICATION SOURCE TO SOURCE_USER='rpl_user',SOURCE_PASSWORD='lee' FOR CHANNEL 'group_replication_recovery';
mysql> SHOW PLUGINS;
mysql> reset master;
mysql> SET GLOBAL group_replication_bootstrap_group=ON;
mysql> START GROUP_REPLICATION USER='rpl_user',PASSWORD='lee';
mysql> SET GLOBAL group_replication_bootstrap_group=OFF;
mysql> SELECT * FROM performance_schema.replication_group_members;


(4)其余节点加入集群
# 在其余从库上配置,加入已存在的 MGR 集群
[root@mysql-N2 ~]# systemctl restart mysqld.service
[root@mysql-N2 ~]# mysql -uroot -p'Xdqk5g=8NTf<'
mysql> alter user root@localhost identified by 'lee';
mysql> SET SQL_LOG_BIN=0;
mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'lee';
mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
mysql> GRANT CONNECTION_ADMIN ON *.* TO rpl_user@'%';
mysql> GRANT BACKUP_ADMIN ON *.* TO rpl_user@'%';
mysql> GRANT GROUP_REPLICATION_STREAM ON *.* TO rpl_user@'%';
mysql> SET SQL_LOG_BIN=1;
mysql> CHANGE REPLICATION SOURCE TO SOURCE_USER='rpl_user',SOURCE_PASSWORD='lee' FOR CHANNEL 'group_replication_recovery';
mysql> reset master;
mysql> START GROUP_REPLICATION USER='rpl_user', PASSWORD='lee';
mysql> SELECT * FROM performance_schema.replication_group_members;


(5)测试 MGR 环境
# 测试所有节点是否可执行读写并进行数据同步
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> create database timinglee;
mysql> create table timinglee.userlist (
-> username varchar(10) primary key not null,
-> password varchar(50) not null
-> );
mysql> insert into timinglee.userlist values ('user1','111');
[root@mysql-N2 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;
mysql> insert into timinglee.userlist values ('user2','222');
mysql> select * from timinglee.userlist;
[root@mysql-N3 ~]# mysql -uroot -plee
mysql> select * from timinglee.userlist;
mysql> insert into timinglee.userlist values ('user3','333');
mysql> select * from timinglee.userlist;



六、MySQL Router(路由)
# 在 mysql 集群的高可用中,mysql5.7 版本建议搭配 MHA,mysql8.0 版本建议搭配 MGR
# 在 mysql 集群的高性能中,mysql 可以搭配 LVS、HAProxy,或者可以直接用 mysql router 实现高性能
1. MySQL Router 简介
# MySQL Router 是一个对应用程序透明的 InnoDB Cluster 连接路由服务,提供负载均衡、应用连接故障转移和客户端路由。
# 利用路由器的连接路由特性,用户可以编写应用程序来连接到路由器,并令路由器使用相应的路由策略来处理连接,使其连接到正确的 MySQL 数据库服务器

2. MySQL Router 部署
# mysql router 实验环境至少需要4台 mysql 主机,3台组成 mysql 集群,1台做 mysql 路由器
# 注意:为了保证数据的一致性,需在部署 MHA/MGR 后做 mysql router
(1)安装 mysql router
# 下载网页:MySQL :: 下载MySQL路由器(存档版本)
# 下载链接:https://downloads.mysql.com/archives/get/p/41/file/mysql-router-community-8.4.7-1.el9.x86_64.rpm

# 重命名和做解析 router 并重启,然后下载 mysql router
[root@mha ~]# ip.sh eth0 192.168.153.100 mysql-router
[root@mha ~]# reboot
[root@mysql-router ~]# wget https://downloads.mysql.com/archives/get/p/41/file/mysql-router-community-8.4.7-1.el9.x86_64.rpm
[root@mysql-router ~]# dnf install mysql-router-community-8.4.7-1.el9.x86_64.rpm -y
(2)部署 mysql router
# 编辑主配置文件,添加只读和读写规则
# 一般网页是读多,所以为了保证流量均摊,只读规则的负载设置为轮询
# "first-available"是指哪台服务器先响应,则跳转到哪台服务器上
[root@mysql-router ~]# rpm -qc mysql-router-community
[root@mysql-router ~]# vim /etc/mysqlrouter/mysqlrouter.conf
44 [routing:ro]
45 bind_address = 0.0.0.0
46 bind_port = 7001
47 destinations = 192.168.153.10:3306,192.168.153.20:3306,192.168.153.30:3306
48 routing_strategy = round-robin
49
50 [routing:rw]
51 bind_address = 0.0.0.0
52 bind_port = 7002
53 destinations = 192.168.153.10:3306,192.168.153.20:3306,192.168.153.30:3306
54 routing_strategy = first-available

# 启动 mysql router
[root@mysql-router ~]# systemctl enable --now mysqlrouter.service
[root@mysql-router ~]# netstat -antlupe | grep mysql

(3)测试高性能
# 在 mysql 节点的任意主机中添加 root 远程登录,保证所有 mysql 主机都能登录
[root@mysql-N1 ~]# mysql -uroot -plee
mysql> CREATE USER root@'%' identified by 'lee';
mysql> GRANT ALL ON *.* TO root@'%';
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.10
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.20
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.30
mysql> quit
# 在所有 mysql 节点上实时观测调度情况,mysql router 主机远程登录任意一台 mysql 主机进行测试
# 测试只读效果,在 mysql router 上测试3次及以上
[root@mysql-N1 ~]# watch -n1 lsof -i :3306
[root@mysql-N2 ~]# watch -n1 lsof -i :3306
[root@mysql-N3 ~]# watch -n1 lsof -i :3306
[root@mysql-router ~]# ssh root@192.168.153.10
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.100 -P7001
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.100 -P7001
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.100 -P7001
mysql> quit



# 测试读写效果,在 mysql router 上测试3次及以上
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.100 -P7002
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.100 -P7002
mysql> quit
[root@mysql-N1 ~]# mysql -uroot -plee -h 192.168.153.100 -P7002
mysql> quit



更多推荐


所有评论(0)