# 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


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐