一、核心架构与工作原理
1. 整体架构
采用两台服务器(双节点) 部署,每台节点同时安装MySQL和KeepAlived,实现MySQL 双主(互为主从)+ KeepAlived 虚拟 IP(VIP)漂移的高可用架构:
- 双节点 IP 示例:Node1(192.168.1.10)、Node2(192.168.1.11)
- 虚拟 VIP:192.168.1.100(应用端仅需连接此 VIP,无需感知后端真实节点)
- 核心特性:两台 MySQL 互为主从(均可读写),KeepAlived 监控节点状态,故障时自动将 VIP 漂移到健康节点,实现秒级故障切换,应用无感知。
2. 关键原理
(1)MySQL 双主模式
两台 MySQL 互相作为对方的主库(Master) 和从库(Slave) ,开启二进制日志和中继日志,实现双向数据同步;通过错开自增 ID 规则,避免双写时的主键冲突,保证数据一致性。
(2)KeepAlived 高可用
基于VRRP(虚拟路由冗余协议) 实现,两台节点分为MASTER(主节点,高优先级)和BACKUP(备节点,低优先级):
- 正常情况下,MASTER 节点持有 VIP,所有应用请求通过 VIP 转发到该节点的 MySQL;
- KeepAlived 默认仅监控自身进程,需通过自定义检测脚本监控 MySQL 状态(进程 / 连通性);
- 当 MASTER 节点 MySQL 宕机 / 服务器故障时,KeepAlived 检测到异常,会释放 VIP,BACKUP 节点检测到主节点失效后,抢占 VIP 并提供服务;
- 故障恢复后,可配置「抢占 / 非抢占」模式,决定 VIP 是否飘回原主节点。
3. 核心依赖与环境说明
- 操作系统:CentOS 7/8(RHEL 系列通用,Ubuntu 需微调命令)
- MySQL 版本:5.7(稳定版,8.0 配置仅需微调同步账号权限,教程以 5.7 为例)
- KeepAlived 版本:系统 YUM 源自带版本(无需手动编译,简单稳定)
- 前置要求:两台节点网络互通、关闭防火墙 / SELinux(学习环境,生产需配置规则)、主机名解析正常。
二、环境准备(两台节点均需执行)
1. 基础环境配置
# 1. 关闭防火墙(生产环境需开放3306/VRRP协议端口)
systemctl stop firewalld && systemctl disable firewalld
# 2. 关闭SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# 3. 配置主机名解析(避免同步/VRRP协议解析问题)
cat >> /etc/hosts << EOF
192.168.1.10 mysql-node1
192.168.1.11 mysql-node2
EOF
# 4. 安装基础依赖
yum install -y wget net-tools vim lrzsz
2. 时间同步(保证双主同步 binlog 时间一致)
# 安装chrony时间同步服务
yum install -y chrony
systemctl start chrony && systemctl enable chrony
# 验证时间同步
timedatectl status
三、安装配置 MySQL 双主模式(核心步骤)
步骤 1:安装 MySQL 5.7(两台节点均执行)
采用 MySQL 官方 YUM 源安装,步骤统一:
# 1. 下载MySQL 5.7 YUM源
wget -O /etc/yum.repos.d/mysql57-community.repo https://repo.mysql.com/yum/mysql-5.7-community/el7/x86_64/mysql57-community.repo
# 2. 安装MySQL服务
yum install -y mysql-community-server --nogpgcheck
# 3. 启动MySQL并设置开机自启
systemctl start mysqld && systemctl enable mysqld
# 4. 查看初始密码(MySQL5.7默认随机生成)
grep 'temporary password' /var/log/mysqld.log
# 输出示例:A temporary password is generated for root@localhost: xxxxxxxx
步骤 2:初始化 MySQL(两台节点均执行)
# 1. 登录MySQL(输入上述初始密码)
mysql -uroot -p
# 2. 修改root密码(需符合复杂度:数字+字母+特殊符号,学习环境可降低复杂度)
ALTER USER 'root'@'localhost' IDENTIFIED BY 'MySQL@123456';
# 刷新权限
FLUSH PRIVILEGES;
# 3. 降低密码复杂度(学习环境可选,生产环境不建议)
set global validate_password_policy=0;
set global validate_password_length=6;
FLUSH PRIVILEGES;
# 4. 远程登录授权(应用/另一节点需远程连接)
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'MySQL@123456';
FLUSH PRIVILEGES;
# 5. 退出MySQL
exit;
步骤 3:配置 MySQL 双主核心参数(两台节点分别配置)
修改 MySQL 主配置文件/etc/my.cnf,核心参数不可重复 / 缺失 ,双节点配置差异仅server-id和自增 ID 规则。
(1)Node1(192.168.1.10)配置
# 备份原配置文件
cp /etc/my.cnf /etc/my.cnf.bak
# 覆盖写入新配置
cat > /etc/my.cnf << EOF
[mysqld]
# 基础配置
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
# 双主核心配置
server-id=1 # 唯一标识,不可重复(Node2设为2)
port=3306
bind-address=0.0.0.0 # 允许所有IP访问
# 开启二进制日志(主库必须开启,用于同步)
log-bin=mysql-bin
binlog_format=ROW # 行级复制,同步更精准
# 开启中继日志(从库必须开启,双主互为主从,故均需开启)
relay-log=mysql-relay-bin
# 从库更新写入自身binlog(双主核心,否则另一台主库无法同步此节点数据)
log-slave-updates=1
# 关闭只读(双主均可写,从库需开启read-only=1,此处互为主从故关闭)
read-only=0
# 跳过主从同步错误(学习环境可选,生产需定位错误)
slave_skip_errors=1062
# 自增ID规则(避免双写主键冲突):起始值1,步长2
auto_increment_offset=1
auto_increment_increment=2
# 指定同步的数据库(*代表所有,学习环境可选)
# replicate-do-db=test_db
EOF
# 重启MySQL使配置生效
systemctl restart mysqld
(2)Node2(192.168.1.11)配置
仅需修改server-id、auto_increment_offset,其余与 Node1 一致:
cp /etc/my.cnf /etc/my.cnf.bak
cat > /etc/my.cnf << EOF
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
# 核心差异:server-id=2
server-id=2
port=3306
bind-address=0.0.0.0
log-bin=mysql-bin
binlog_format=ROW
relay-log=mysql-relay-bin
log-slave-updates=1
read-only=0
slave_skip_errors=1062
# 核心差异:自增起始值2,步长2
auto_increment_offset=2
auto_increment_increment=2
# replicate-do-db=test_db
EOF
# 重启MySQL
systemctl restart mysqld
步骤 4:创建 MySQL 同步账号(两台节点均执行)
双主互相同步,需为对方创建专用同步账号(授予复制权限),避免使用 root:
# 登录MySQL
mysql -uroot -pMySQL@123456
# 创建同步账号(repl为账号,Repl@123456为密码,%允许所有IP访问)
CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl@123456';
# 授予复制权限(主从同步核心权限)
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
# 退出MySQL
exit;
步骤 5:配置双主互为主从(关键步骤,需精准执行)
核心:Node1 作为从库 指向 Node2 的主库,Node2 作为从库指向 Node1 的主库,需先获取对方主库的 binlog 文件和位置。
(1)获取 Node1 主库状态(在 Node1 执行)
mysql -uroot -pMySQL@123456 -e "SHOW MASTER STATUS;"
输出示例 (记录File和Position,后续 Node2 配置用):
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
|---|---|---|---|
| mysql-bin.000001 | 154 |
注意:执行后不要在 Node1 执行任何写操作,否则 Position 会变化,导致同步失败。
(2)Node2 配置为 Node1 的从库(在 Node2 执行)
将<Node1_IP>、<binlog_File>、<binlog_Position>替换为实际值:
mysql -uroot -pMySQL@123456 << EOF
# 停止从库服务(若已配置)
STOP SLAVE;
# 重置从库状态(清空原有同步信息)
RESET SLAVE ALL;
# 配置主库信息(指向Node1)
CHANGE MASTER TO
MASTER_HOST='192.168.1.10',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001', # 对应Node1的Show Master Status的File
MASTER_LOG_POS=154, # 对应Node1的Show Master Status的Position
MASTER_CONNECT_RETRY=30; # 连接失败重试间隔(秒)
# 启动从库服务
START SLAVE;
# 查看从库状态
SHOW SLAVE STATUS\G
EOF
(3)获取 Node2 主库状态(在 Node2 执行)
mysql -uroot -pMySQL@123456 -e "SHOW MASTER STATUS;"
记录File和Position,后续 Node1 配置用。
(4)Node1 配置为 Node2 的从库(在 Node1 执行)
替换为 Node2 的实际 binlog 信息:
mysql -uroot -pMySQL@123456 << EOF
STOP SLAVE;
RESET SLAVE ALL;
CHANGE MASTER TO
MASTER_HOST='192.168.1.11',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001', # 对应Node2的File
MASTER_LOG_POS=154, # 对应Node2的Position
MASTER_CONNECT_RETRY=30;
START SLAVE;
SHOW SLAVE STATUS\G
EOF
步骤 6:验证 MySQL 双主同步(两台节点均需验证)
执行SHOW SLAVE STATUS\G后,两个核心字段必须为 Yes,否则同步失败:
# 登录MySQL执行
mysql -uroot -pMySQL@123456 -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running"
正确输出:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
若为 No,查看
Last_Error字段定位错误(常见原因:server-id 重复、binlog 位置错误、同步账号密码错误、防火墙未关)。
步骤 7:验证双向数据同步(可选,确认双主有效性)
# 1. 在Node1创建数据库,Node2查看是否同步
# Node1执行
mysql -uroot -pMySQL@123456 -e "CREATE DATABASE test_ha;"
# Node2执行
mysql -uroot -pMySQL@123456 -e "SHOW DATABASES;" # 应能看到test_ha
# 2. 在Node2创建表,Node1查看是否同步
# Node2执行
mysql -uroot -pMySQL@123456 -e "CREATE TABLE test_ha.t1(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));"
# Node1执行
mysql -uroot -pMySQL@123456 -e "DESC test_ha.t1;" # 应能看到表结构
# 3. 双节点插入数据,验证自增ID无冲突
# Node1插入(id应为1、3、5...)
mysql -uroot -pMySQL@123456 -e "INSERT INTO test_ha.t1(name) VALUES('node1');"
# Node2插入(id应为2、4、6...)
mysql -uroot -pMySQL@123456 -e "INSERT INTO test_ha.t1(name) VALUES('node2');"
# 两台节点均执行查询,应能看到所有数据
mysql -uroot -pMySQL@123456 -e "SELECT * FROM test_ha.t1;"
四、安装配置 KeepAlived(实现 VIP 漂移与 MySQL 监控)
核心说明
- 两台节点均需安装 KeepAlived,配置分为「主节点(MASTER)」和「备节点(BACKUP)」,仅优先级 / 状态不同;
- KeepAlived默认不监控 MySQL ,需编写自定义检测脚本,当 MySQL 宕机时,自动停止当前节点的 KeepAlived,释放 VIP;
- 脚本核心逻辑:通过
mysqladmin ping检测 MySQL 连通性,失败则执行systemctl stop keepalived。
步骤 1:安装 KeepAlived(两台节点均执行)
直接通过 YUM 源安装,简单稳定:
yum install -y keepalived
# 验证安装
keepalived -v
步骤 2:编写 MySQL 检测脚本(两台节点均执行)
创建脚本目录,编写检测脚本/etc/keepalived/check_mysql.sh,必须赋予执行权限:
# 1. 创建脚本目录
mkdir -p /etc/keepalived/scripts
# 2. 编写检测脚本
cat > /etc/keepalived/check_mysql.sh << EOF
#!/bin/bash
# 定义MySQL连接参数
MYSQL_USER="root"
MYSQL_PWD="MySQL@123456"
MYSQL_HOST="127.0.0.1"
# 检测MySQL连通性(mysqladmin ping是最可靠的方式)
mysqladmin -u\${MYSQL_USER} -p\${MYSQL_PWD} -h\${MYSQL_HOST} ping &>/dev/null
if [ \$? -ne 0 ]; then
# MySQL宕机,停止当前节点KeepAlived,释放VIP
systemctl stop keepalived
echo "MySQL is down, stop keepalived at \$(date)" >> /var/log/keepalived_check_mysql.log
fi
EOF
# 3. 赋予执行权限(关键,否则KeepAlived无法执行脚本)
chmod +x /etc/keepalived/check_mysql.sh
# 4. 创建日志文件
touch /var/log/keepalived_check_mysql.log
步骤 3:配置 KeepAlived 主配置文件(分主 / 备节点)
KeepAlived 主配置文件为/etc/keepalived/keepalived.conf,主节点(Node1) 和备节点(Node2) 配置仅 3 处差异,其余完全一致。
(1)主节点(Node1:192.168.1.10)配置
# 备份原配置
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
# 覆盖写入新配置
cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_10 # 唯一标识,建议用节点IP后两位,与备节点区分
script_user root # 执行检测脚本的用户
enable_script_security # 开启脚本安全验证
}
# 定义检测脚本(调用上述MySQL检测脚本)
vrrp_script check_mysql {
script "/etc/keepalived/check_mysql.sh" # 脚本路径
interval 2 # 检测间隔(秒)
weight -20 # 检测失败,节点优先级降低20
fall 3 # 连续3次检测失败,判定为故障
rise 2 # 连续2次检测成功,判定为恢复
}
# VRRP实例配置(核心,定义VIP和主备规则)
vrrp_instance VI_1 {
state MASTER # 主节点设为MASTER,备节点设为BACKUP
interface eth0 # 网卡名称(需根据自身服务器修改,用ip addr查看)
virtual_router_id 51 # VRRP组ID(1-255,双节点必须一致)
priority 100 # 优先级(主节点高于备节点,备节点设为90)
advert_int 1 # 心跳发送间隔(秒),双节点必须一致
nopreempt # 非抢占模式(可选,主节点恢复后不主动抢回VIP,避免抖动)
authentication { # 认证(双节点必须一致,防止非法节点加入)
auth_type PASS
auth_pass 1111 # 认证密码(任意4-8位)
}
# 调用检测脚本
track_script {
check_mysql
}
# 配置虚拟VIP(双节点必须一致,可配置多个,用空格分隔)
virtual_ipaddress {
192.168.1.100/24 dev eth0 # VIP/子网掩码 绑定网卡
}
}
EOF
(2)备节点(Node2:192.168.1.11)配置
仅修改3 处差异 ,其余与主节点一致,已标注# 差异点:
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_11 # 差异点1:唯一标识,与主节点区分
script_user root
enable_script_security
}
vrrp_script check_mysql {
script "/etc/keepalived/check_mysql.sh"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP # 差异点2:备节点设为BACKUP
interface eth0
virtual_router_id 51
priority 90 # 差异点3:优先级低于主节点(90<100)
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1111
}
track_script {
check_mysql
}
virtual_ipaddress {
192.168.1.100/24 dev eth0
}
}
EOF
网卡名称确认命令:
ip addr(常见为 eth0、ens33、ens192 等)。
步骤 4:启动 KeepAlived 并设置开机自启(两台节点均执行)
# 1. 启动KeepAlived
systemctl start keepalived
# 2. 设置开机自启
systemctl enable keepalived
# 3. 查看状态(确认running)
systemctl status keepalived
# 4. 查看VIP是否在主节点(Node1应能看到VIP,Node2无)
ip addr
主节点 Node1 正确输出(包含 VIP 192.168.1.100):
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:xx:xx:xx brd ff:ff:ff:ff:ff:ff
inet 192.168.1.10/24 brd 192.168.1.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet 192.168.1.100/24 scope global secondary eth0 # VIP标识
valid_lft forever preferred_lft forever
五、高可用集群测试(核心验证,必做)
测试目标:验证MySQL 宕机 和服务器宕机两种场景下,VIP 能否自动漂移,应用能否通过 VIP 正常访问,数据是否一致。
测试工具:可在第三方测试机 / 本地执行
ping 192.168.1.100(查看 VIP 连通性)、mysql -h192.168.1.100 -uroot -pMySQL@123456(查看 MySQL 访问性)。
测试 1:主节点(Node1)MySQL 宕机,VIP 漂移到备节点(Node2)
步骤 1:主节点停止 MySQL
# Node1执行
systemctl stop mysqld
# 查看KeepAlived状态(检测脚本会自动停止KeepAlived)
systemctl status keepalived
步骤 2:验证 VIP 漂移
# 1. Node1查看IP(VIP已消失)
ip addr
# 2. Node2查看IP(VIP已漂移过来)
ip addr
# 3. 第三方测试机ping VIP(连通性正常)
ping 192.168.1.100
# 4. 第三方测试机通过VIP访问MySQL(正常连接,数据与原主节点一致)
mysql -h192.168.1.100 -uroot -pMySQL@123456 -e "SELECT * FROM test_ha.t1;"
步骤 3:恢复主节点 MySQL,验证 VIP 状态
# Node1启动MySQL和KeepAlived
systemctl start mysqld && systemctl start keepalived
# 查看VIP(因配置了nopreempt,VIP仍在Node2,避免抖动;若需抢占,删除nopreempt参数即可)
ip addr
测试 2:主节点(Node1)服务器宕机,VIP 漂移到备节点(Node2)
步骤 1:关闭主节点服务器(或断开网络)
直接在 Node1 执行init 0关闭服务器,或在虚拟机中关闭节点。
步骤 2:验证 VIP 漂移与服务可用性
# 1. Node2查看IP(VIP已漂移)
ip addr
# 2. 第三方测试机通过VIP访问MySQL(正常连接,可读写)
mysql -h192.168.1.100 -uroot -pMySQL@123456 -e "INSERT INTO test_ha.t1(name) VALUES('vip_test');"
步骤 3:恢复主节点服务器,验证数据同步
启动 Node1 服务器,等待 MySQL 和 KeepAlived 自动启动(已设置开机自启):
# Node1执行,查看数据是否同步(备节点写入的数据已同步)
mysql -uroot -pMySQL@123456 -e "SELECT * FROM test_ha.t1;"
# 查看VIP(仍在Node2,非抢占模式)
ip addr
测试 3:备节点故障,主节点正常提供服务(反向验证)
停止 Node2 的 MySQL/KeepAlived,或关闭 Node2 服务器,验证 Node1 的 VIP 始终存在,应用通过 VIP 访问正常,无任何影响。
六、常见问题与避坑指南(重点)
-
主从同步 Slave_IO_Running=No:
- 原因:server-id 重复、防火墙未关、同步账号密码错误、binlog 位置错误;
- 解决:检查
server-id唯一、开放 3306 端口、重新确认同步账号密码、重新获取主库 binlog 位置并配置。
-
VIP 无法漂移:
- 原因:KeepAlived 检测脚本无执行权限、VRRP 组 ID / 认证密码不一致、网卡名称配置错误、防火墙屏蔽 VRRP 协议;
- 解决:给脚本加
chmod +x、统一双节点virtual_router_id/auth_pass、确认网卡名称、关闭防火墙(生产开放 VRRP 协议:iptables -A INPUT -p vrrp -j ACCEPT)。
-
双写 MySQL 出现主键冲突:
- 原因:未配置
auto_increment_offset和auto_increment_increment,或配置错误; - 解决:双节点分别配置
offset=1/step=2和offset=2/step=2,重启 MySQL。
- 原因:未配置
-
检测脚本不执行:
- 原因:未开启
enable_script_security、脚本用户非 root、脚本路径错误; - 解决:配置
script_user root和enable_script_security、确认脚本路径正确。
- 原因:未开启
-
主节点恢复后 VIP 飘回,导致应用抖动:
- 解决:在主备节点均配置
nopreempt(非抢占模式),主节点恢复后不主动抢回 VIP,由人工决定是否切回。
- 解决:在主备节点均配置
七、生产环境优化建议(拓展)
- 防火墙 / SELinux:开启防火墙,仅开放 3306(MySQL)、VRRP 协议端口,SELinux 配置白名单;
- 密码管理:使用加密方式存储 MySQL/KeepAlived 密码,避免明文写在配置 / 脚本中;
- 监控告警:结合 Zabbix/Prometheus 监控 MySQL 主从状态、KeepAlived 状态、VIP 漂移情况,异常及时告警;
- 数据备份:双主同步不替代备份,定期执行全量 + 增量备份,防止数据丢失;
- 抢占模式:生产建议使用非抢占模式(nopreempt),避免主节点恢复后频繁漂移导致应用抖动;
- 多 VIP 配置:若需多实例,可配置多个 VRRP 实例,每个实例对应一个 VIP;
- 脚本增强:检测脚本中增加 MySQL 端口检测、磁盘空间检测,避免单一检测维度误判。
八、整体流程总结
- 环境准备:关闭防火墙 / SELinux、配置主机名解析、时间同步(两台节点均执行);
- MySQL 安装:双节点通过 YUM 安装 MySQL 5.7,初始化密码并授权远程访问;
- 双主配置:修改 my.cnf 核心参数(server-id、自增 ID、日志),创建同步账号,互为主从并验证同步状态;
- KeepAlived 安装:双节点安装 KeepAlived,编写 MySQL 检测脚本并赋予执行权限;
- KeepAlived 配置:分主 / 备节点修改配置文件(仅 3 处差异),启动并验证 VIP 在主节点;
- 高可用测试:验证 MySQL 宕机、服务器宕机两种场景下的 VIP 漂移和服务可用性,确认数据同步;
- 故障排查:根据常见问题处理同步 / 漂移故障,生产环境做针对性优化。