本篇文章是 MySQL 学习课程中的一部分笔记。
本博文详细说明了如何搭建 MySQL 高可用并对解决普通主从复制延迟不理想提供了解决方案,最后借助 MHA 实现了主从的自动切换。
MySQL 官方下载地址:https://downloads.mysql.com/archives/community/
操作系统:CentOS 7
MySQL:mysql-5.7.29
在这个页面中选择操作系统和对应版本,如下图所示:

在 CentOS 中只用使用 wget 命令下载即可,下载完成后进行解压:
> wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.29-1.el7.x86_64.rpm-bundle.tar
> tar -xvf mysql-5.7.29-1.el7.x86_64.rpm-bundle.tar
mysql-community-embedded-devel-5.7.29-1.el7.x86_64.rpm
mysql-community-test-5.7.29-1.el7.x86_64.rpm
mysql-community-embedded-5.7.29-1.el7.x86_64.rpm
mysql-community-embedded-compat-5.7.29-1.el7.x86_64.rpm
mysql-community-libs-5.7.29-1.el7.x86_64.rpm
mysql-community-client-5.7.29-1.el7.x86_64.rpm
mysql-community-server-5.7.29-1.el7.x86_64.rpm
mysql-community-devel-5.7.29-1.el7.x86_64.rpm
mysql-community-libs-compat-5.7.29-1.el7.x86_64.rpm
mysql-community-common-5.7.29-1.el7.x86_64.rpm
在开始安装之前,需要确认系统中没有 MySQL 以及相关产品,使用 CentOS 镜像安装的操作系统的话,默认会自带一个 MariaDB,如果使用云主机,一般不带,不过也需要检查一下:
> rpm -qa | grep mariadb
> rpm -e mariadb-libs-5.5.41-2.el7_0.x86_64 --nodeps # 如果有的话,使用这种方式移除
上面 MySQL 的安装包解压后有很多组件,一般不需要全部安装,挑常用的几个安装即可,并且要注意安装顺序:
> rpm -ivh mysql-community-common-5.7.29-1.el7.x86_64.rpm
> rpm -ivh mysql-community-libs-5.7.29-1.el7.x86_64.rpm
> rpm -ivh mysql-community-libs-compat-5.7.29-1.el7.x86_64.rpm
> rpm -ivh mysql-community-client-5.7.29-1.el7.x86_64.rpm
> rpm -ivh mysql-community-server-5.7.29-1.el7.x86_64.rpm
> rpm -ivh mysql-community-devel-5.7.29-1.el7.x86_64.rpm
上面组件安装完成后就完全够日常使用了。
使用下面命令进行初始化:
> mysqld --initialize --user=mysql # --user 是可选的参数,意思是创建一个名称是 mysql 的用户
初始化完成后,mysql 会给 root 用户创建一个临时的登录密码,登录进去后再对 root 用户的密码进行自定义修改,临时密码在 mysql 的日志中,默认路径是 /var/log/mysqld.log,一般在最后一行类似下面:
A temporary password is generated for root@localhost: hSQ&zl/3m?oF
一会使用上面的临时密码登录。
使用系统方式启动 mysql,同时还把 mysql 的加入到系统自启动列表中:
> systemctl start mysqld.service
> systemctl status mysqld.service # 检查是否启动成功
然后使用上面日志中的临时密码登录,登录后修改密码,否则不能做任何操作:
> mysql -uroot -p
....
> set password=password('your_new_password');
为了让集群中的各个数据库节点能正常通信,需要关闭防火墙或者在防火墙中放行 3306 的端口,这里就直接停掉了:
> systemctl stop iptables # iptables 防火墙
> systemctl stop firewalld # firewalld 防火墙,CentOS 自带
> systemctl disable firewalld.service # 彻底一点,从开机启动中禁用掉
修改主库的配置文件 /etc/my.cnf,增加以下配置:
# log_bin
server-id=1 # 设置 server-id,每个数据库不能重复,必须
log_bin=mysql-bin # 指定 binlog 的名称,相当于开启 bin log, 必须
sync-binlog=1 # 开始刷新 bin log 到磁盘,每次有更新事务,完成后都要马上刷新到磁盘
binlog-ignore-db=performance_schema # binlog 中忽略的库
binlog-ignore-db=information_schema
binlog-ignore-db=sys
#binlog-do-db=lagou # 可以使用这个参数指定只同步那个库
保存退出后,重启数据库:
> systemctl restart mysqld
开始 bin log 后,就需要对 master 库进行一下权限设置,设置哪些 ip 的 slave 可以从本 master 同步 binlog。
登录进 mysql 执行一些授权操作:
grant replication slave on *.* to 'root'@'%' identified by 'root'; # 复制授权
grant all privileges on *.* to 'root'@'%' identified by 'root';
flush privileges; # 刷新权限
在查看一下数据库作为 master 的一些状态
mysql> show master status;
+------------------+----------+--------------+-------------------------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+-------------------------------------------+-------------------+
| mysql-bin.000001 | 869 | | performance_schema,information_schema,sys | |
+------------------+----------+--------------+-------------------------------------------+-------------------
重点已关注 当前使用的 bin log 文件名称以及 bin log 的写入位置。
修改每个从库的 /etc/my.cnf,添加一下内容:
server-id=3 # 注意一个集群环境中的 id 不能冲突
relay_log=mysql-relay-bin
# read_only=1 # 开启只读
重启数据库,然后登陆到 mysql 中做一些相关配置:
> show slave status; # 查看 salve 状态,如果已经是开启状态,需要先 stop slave;修改配置后,在 start slave;
# 设置主库的 bin log 信息(用到了主库中当前 bin log 的文件名称以及当前写位置)
> change master to master_host='172.17.42.6',master_port=3306,master_user='root',master_password='root',master_log_file='mysql-bin.000001',master_log_pos=869;
> show slave status; # 查看从库状态, 第一次配置,此时连接 master 的状态应该是 No
> start slave; # 开启 salve 模式
经过以上步骤,主库中新建库以及DDL 操作都能同步到 salve 数据库中了。
主从结构的 MySQL 的一个缺点就是主从同步延迟,5.7 后 MySQL 提供了半同步复制和并行复制来减小这个同步延迟,其实就是从并发执行 bin log 回放线程。
主库配置
查看当前版本是否支持插件:
> select @@have_dynamic_loading;
+------------------------+
| @@have_dynamic_loading |
+------------------------+
| YES |
+------------------------+
查看当前已经安装的插件:
> show plugins;
默认应该是没有 rpl_semi_sync插件的,那就需要安装:
> install plugin rpl_semi_sync_master soname 'semisync_master.so'; # soname 后面是个别名
安装完成后,对参数做一下调整(开启 rpl 以及调整超时时长):
mysql> show variables like '%semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | OFF |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
+-------------------------------------------+------------+
mysql> set global rpl_semi_sync_master_enabled=1;
Query OK, 0 rows affected (0.00 sec)
mysql> set global rpl_semi_sync_master_timeout=1000;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 1000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)
从库配置
同样需要安装 rpl_semi 插件, 注意和主库中安装的略有不同,主库插件后缀为 master,从库后缀名称为 slave:
> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
同样需要开启 rpl:
mysql> show variables like '%semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set (0.00 sec)
mysql> set global rpl_semi_sync_slave_enabled=1;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
然后在重启一下 slave 模式加载新的配置就可以了:
mysql> stop slave;
mysql> start slave;
这里针对并行复制的组提交模式进行配置:
主库参数
mysql> show variables like '%binlog_group%';
+-----------------------------------------+-------+
| Variable_name | Value |
+-----------------------------------------+-------+
| binlog_group_commit_sync_delay | 0 |
| binlog_group_commit_sync_no_delay_count | 0 |
+-----------------------------------------+-------+
2 rows in set (0.00 sec)
mysql> set global binlog_group_commit_sync_delay=1000; # 组提交延迟时间
Query OK, 0 rows affected (0.00 sec)
mysql> set global binlog_group_commit_sync_no_delay_count=100; # 组中最大事务数量
Query OK, 0 rows affected (0.00 sec)
从库配置
因为部分参数在 msyql 控制台是只读的,所以需要在 my.cnf 中修改
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=8
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=1
保存后,重启数据库,然后登陆后查看参数:
mysql> show variables like '%slave_parallel%';
+------------------------+---------------+
| Variable_name | Value |
+------------------------+---------------+
| slave_parallel_type | LOGICAL_CLOCK |
| slave_parallel_workers | 8 |
+------------------------+---------------+
mysql> show variables like '%relay_log%';
+---------------------------+--------------------------------------+
| Variable_name | Value |
+---------------------------+--------------------------------------+
| max_relay_log_size | 0 |
| relay_log | mysql-relay-bin |
| relay_log_basename | /var/lib/mysql/mysql-relay-bin |
| relay_log_index | /var/lib/mysql/mysql-relay-bin.index |
| relay_log_info_file | relay-log.info |
| relay_log_info_repository | TABLE |
| relay_log_purge | ON |
| relay_log_recovery | ON |
| relay_log_space_limit | 0 |
| sync_relay_log | 10000 |
| sync_relay_log_info | 10000 |
+---------------------------+--------------------------------------+
配置完成。
验证
mysql> use performance_schema;
mysql> select * from replication_applier_status_by_worker;
+--------------+-----------+-----------+---------------+-----------------------+-------------------+--------------------+----------------------+
| CHANNEL_NAME | WORKER_ID | THREAD_ID | SERVICE_STATE | LAST_SEEN_TRANSACTION | LAST_ERROR_NUMBER | LAST_ERROR_MESSAGE | LAST_ERROR_TIMESTAMP |
+--------------+-----------+-----------+---------------+-----------------------+-------------------+--------------------+----------------------+
| | 1 | 27 | ON | ANONYMOUS | 0 | | 0000-00-00 00:00:00 |
| | 2 | 28 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 3 | 29 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 4 | 30 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 5 | 31 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 6 | 32 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 7 | 33 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 8 | 34 | ON | | 0 | | 0000-00-00 00:00:00 |
+--------------+-----------+-----------+---------------+-----------------------+-------------------+--------------------+----------------------+
MHA 的功能就是监控主从集群中主节点的运行状态,以及 master 挂掉后,自动在可用的 slave 中选出一个新的 master。
MHA 要求集群环境中至少有一主两从三台数据库。
本次操作集群列表如下:
| 主机名 | IP | 初始主从角色 | MHA 角色 |
|---|---|---|---|
| mysql-master | master | MHA Node | |
| ysql-slave-1 | slave | MHA Node | |
| mysql-slave-2 | slave | MHA Node | |
| mha-manager | MHA Manager |
根据主机名称,在各自机器的 /etc/hosts 中配置其他机器的 host。
安装基本依赖
所有数据库服务器和要作为 MHA Manager 的服务器都要安装:
# 安装一个epel源
wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo
# 用yum安装依赖包 (CentOS 8 安装 perl-DBD-MySQL 即可)
yum install perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes -y
配置 SSH 免密登录
配置所有机器之间可以通过 SSH 免密登录,比如在 mha-manager 机器中,先生成对称秘钥,在把公钥复制到其他三台机器上:
> ssh-keygen -t rsa # 一路回车,全部使用默认值
> ssh-copy-id -i /root/.ssh/id_rsa.pub mysql-slave-1
> ssh-copy-id -i /root/.ssh/id_rsa.pub mysql-slave-2
> ssh-copy-id -i /root/.ssh/id_rsa.pub mysql-master
其他三台机器依照上述过程,把本机生成的公钥复制到其他三台机器中。
修改从库的配置
从库的 relay_log 不能被自动删除:
relay_log_purge = 0 # 不自动删除relay log
log_bin=mysql-bin # 同样开启 bin log
# 因为主库中配置了 bin log 库过滤,从库也必须配置一样的库过滤
binlog-ignore-db=performance_schema
binlog-ignore-db=information_schema
binlog-ignore-db=sys
安装 MHA Node
所有的数据库节点都需要安装 MHA Node,从 https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads 中下载 MHA Node 0.56 rpm RHEL6,可能需要手动下载在上传到服务器节点。
> rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm # 安装 MHA Node
安装完成在 /usr/bin目录下出现以下脚本文件:
[root@mysql-slave-2 ~]# ll /usr/bin | grep 'logs'
-rwxr-xr-x 1 root root 16367 Apr 1 2014 apply_diff_relay_logs
-rwxr-xr-x 1 root root 1465 Jun 10 2014 dbilogstrip
-rwxr-xr-x 1 root root 8261 Apr 1 2014 purge_relay_logs
-rwxr-xr-x 1 root root 7525 Apr 1 2014 save_binary_logs
这些脚本工具通常由MHA Manager的脚本触发,无需人为操作。
mha 授权
在所有数据库节点上给 mha 数据库角色授权:
> grant all on *.* to 'mha'@'%' identified by 'mha'; # 这块和下面 manager 中配置文件配置的用户要相同
所有的从库上配置只读:
> set global read_only=1;
安装 MHA Manager
在机器mha-manager 中安装。
Manager 也会依赖 mha-node ,因此要先安装MHA Node
从 https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads 下载[MHA Manager 0.56 rpm RHEL6],安装:
[root@MHA-Manager ~]# rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm
[root@MHA-Manager ~]# rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm
配置 manager
首先创建 mha 工作目录和配置文件
> mkdir -p /etc/mha
> touch /etc/mha/app1.cnf
> vim /etc/mha/app1.cnf
配置文件内容(基本内容):
[server default]
#MHA日志名字
manager_log=/etc/mha/manager.log
#MHA的工作目录
manager_workdir=/etc/mha
#数据库binlog存放路径, 如果在 maser 库中自定义了 bin log 的目录,则需要在这里配置
#master_binlog_dir=/var/logs/
#mha管理用户的用户名, 全面所有数据库中已经给这个用户做了授权
user=root
#mha管理用户的密码
password=root
#监测心跳,每隔2秒监测一次(默认是3秒)
ping_interval=2
#ssh远程连接用户(做完免密的)
ssh_user=root
[server1]
hostname=172.17.42.6
port=3306
[server2]
hostname=172.17.42.8
port=3306
[server3]
hostname=172.17.42.10
port=3306
测试
> masterha_check_ssh --conf=/etc/mha/app1.cnf # 测试 ssh 免密登录
> masterha_check_repl --conf=/etc/mha/app1.cnf # 测试主从复制状态
这两部测试出现问题,根据输出提示进行修复。
启动
后台启动 manager
> nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /etc/mha/manager.log 2>&1 &
上面部分参数说明:
查看运行状态:
> masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:13778) is running(0:PING_OK), master:172.17.42.6
测试主库自动切换
恢复上一步停掉的主库
现在恢复只能先已从库的角色恢复,如需要在切换成主库,可以手动切换。
> grep -i 'change master to' /etc/mha/manager.log
Wed May 27 11:00:30 2020 - [info] All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='172.17.42.8', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=154, MASTER_USER='root', MASTER_PASSWORD='xxx';
mysql> CHANGE MASTER TO MASTER_HOST='172.17.42.8', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=154, MASTER_USER='root', MASTER_PASSWORD='root';
Query OK, 0 rows affected, 2 warnings (0.02 sec)
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status \G;
手动热切换主库
上面方式是手动停掉主库后触发 MHA 的自动切换,如果要在主从都运行正常情况下切换主从,可使用一下命令:
> masterha_master_switch --conf=/etc/mha/app1.cnf --master_state=alive --new_master_host=172.17.42.6 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
运行中间会涉及交互,仔细阅读提示后进行下一步的操作。
如果安装过程中提示提示缺少依赖:
error: Failed dependencies:
libcrypto.so.10()(64bit) is needed by mysql-community-libs-compat-5.7.29-1.el7.x86_64
libcrypto.so.10(libcrypto.so.10)(64bit) is needed by mysql-community-libs-compat-5.7.29-1.el7.x86_64
libssl.so.10()(64bit) is needed by mysql-community-libs-compat-5.7.29-1.el7.x86_64
libssl.so.10(libssl.so.10)(64bit) is needed by mysql-community-libs-compat-5.7.29-1.el7.x86_64
下载安装一个 openssl-libs-1.0.2k-19.el7.x86_64.rpm(link:http://rpmfind.net/linux/centos/7.8.2003/os/x86_64/Packages/openssl-libs-1.0.2k-19.el7.x86_64.rpm) (来源网址:rpmfind)
下载后强制安装:
> rpm -ivh openssl-libs-1.0.2k-19.el7.x86_64.rpm --force
下载:wget http://mirror.centos.org/centos/6/os/x86_64/Packages/libaio-0.3.107-10.el6.x86_64.rpm
安装:
> rpm -ivh libaio-0.3.107-10.el6.x86_64.rpm
启动失败的原因很多,在 /var/log/mysqld.log中可以查看具体原因,最常见的原因就是数据文件写入没有权限或者binlog 写入没有权限,比如默认 mysql 的数据目录为 /var/lib/mysql,如果提示The innodb_system data file 'ibdata1' must be writable一般就是这个数据目录没有权限了,增加写权限就行:
> chmod -R 777 /var/lib/mysql
或者把 mysql 用户放到 root 组中。
没有活跃的 server
首先要排查是不是防火墙的端口没有放行,然后排查 mysql 是否可以接受远程连接。

