目录
5.6 配置 Master1 作为 Master2 的从服务器
5.7 配置 Master2 作为 Master1 的从服务器
8.3 模拟 Master2 节点故障(VIP 在 Keepalived2 上时)
8.3.2 验证 HAProxy 自动切换至 Master1(假设 Master1 已恢复)
8.4.1 关闭 Keepalived2 和 Master2
一、引言
在数字化时代,数据成为企业的核心资产,数据库的可靠性直接决定着业务的连续性。传统单节点 MySQL 架构存在单点故障风险,一旦宕机,会导致业务中断、数据丢失等严重后果。尤其在电商秒杀、金融交易等场景中,数据库的持续可用性(High Availability,HA)已成为系统设计的刚性需求。
为解决 MySQL 单点问题及应对日益增长的数据量带来的数据库压力,“MySQL 主主复制 + Keepalived+HAProxy” 这一经典高可用架构应运而生。该架构通过开源工具实现数据库的故障自动转移、负载均衡和读写分离,为企业提供高可用、高性能的数据库服务。本笔记将围绕该架构展开,详细介绍其理论知识与实践操作。
二、案例概述
2.1 MySQL 高可用概念
MySQL 高可用(High Availability)是指通过冗余设计,确保数据库服务在单节点故障、网络中断或硬件损坏等异常情况下,仍能持续对外提供服务,同时保证数据一致性。其核心目标是实现 “零停机、零数据丢失” 的业务连续性。
2.2 方案组成
“MySQL 主主复制 + Keepalived+HAProxy” 的高可用方案由三部分组成:
- MySQL 主主复制:两台 MySQL 实例互为主从,双向同步数据,均支持读写操作,提供冗余和扩展能力。
- Keepalived:通过 VRRP 协议管理虚拟 IP(VIP),监控 MySQL 状态,故障时自动将 VIP 漂移至存活节点,确保服务地址不变。
- HAProxy:作为反向代理和负载均衡器,将流量分发至 MySQL 节点,支持健康检查、读写分离(可选)和故障节点自动剔除。
2.3 方案优势
- 高可用性:Keepalived 实现秒级故障切换,HAProxy 健康检查确保流量仅路由到正常节点,避免单点故障。
- 读写扩展:主主架构支持双节点并发写入,提升写入性能;HAProxy 可配置读写分离,利用备节点分担读压力。
- 灵活扩展:可横向扩展 HAProxy 或 MySQL 节点,支持动态调整负载均衡策略(如轮询、权重)。
- 运维友好:基于开源工具,无厂商锁定,社区支持丰富,适合自建数据库集群。
三、案例环境
3.1 环境规划
主机 | 应用 | 操作系统 | IP 地址 |
---|---|---|---|
192.168.10.101 | Master1 | openEuler 24.03 | MySQL 8 |
192.168.10.102 | Master2 | openEuler 24.03 | MySQL 8 |
192.168.10.103 | Keepalived1、HAProxy | openEuler 24.03 | - |
192.168.10.104 | Keepalived2、HAProxy | openEuler 24.03 | - |
3.2 环境要求
- 所有节点关闭 SELinux 和防火墙,确保网络通信正常。
- 安装必要的依赖包,如 gcc、make 等,以便编译安装软件。
四、MySQL 安装与配置
4.1 下载与编译安装 MySQL
- 从 MySQL 官方网站下载适合 openEuler 系统的 MySQL 8 源码包。
- 解压源码包:
tar -xvf mysql-8.0.36.tar.gz
- 创建 MySQL 用户和组:
groupadd mysql
useradd -r -g mysql -s /bin/false mysql
- 编译配置(在源码目录下执行):
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_UNIX_ADDR=/usr/local/mysql/data/mysql.sock -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_MYISAM_STORAGE_ENGINE=1 -DENABLED_LOCAL_INFILE=1 -DMYSQL_DATADIR=/usr/local/mysql/data -DSYSCONFDIR=/etc -DMYSQL_TCP_PORT=3306 -DENABLE_DOWNLOADS=1 -DDEFAULT_AUTHENTICATION_PLUGIN=mysql_native_password
- 编译并安装:
make -j$(nproc)
make install
4.2 初始化 MySQL
- 创建数据目录并设置权限:
mkdir -p /usr/local/mysql/data
chown -R mysql:mysql /usr/local/mysql
- 初始化数据库(在 /usr/local/mysql/bin 目录下执行):
./mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
此次初始化没有采用无密码模式,因此会生成初始随机密码,需要保存,用以后续登录 MySQL 数据库使用。例如,输出中可能显示类似 “A temporary password is generated for root@localhost: Xr6:Gg*u8?/8” 的信息,记录该密码。
4.3 设定配置文件
编辑 MySQL 的配置文件 /etc/my.cnf,内容如下:
[client]
socket=/usr/local/mysql/data/mysql.sock
[mysqld]
socket=/usr/local/mysql/data/mysql.sock
bind-address = 0.0.0.0
skip-name-resolve
port = 3306
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
max_connections=2048
character-set-server=utf8
default-storage-engine=INNODB
max_allowed_packet=16M
4.4 配置 systemctl 方式启动
- 将 MySQL 添加成为系统服务,通过使用 systemctl 来管理。在 /usr/local/mysql/support-files 目录下找到 mysql.server 文件,将其复制到 /etc/rc.d/init.d 目录下,改名为 mysqld 并赋予可执行权限:
cp /usr/local/mysql/support-files/mysql.server /etc/rc.d/init.d/mysqld
chmod +x /etc/rc.d/init.d/mysqld
- 编辑生成 mysqld.service 服务,通过 systemctl 方式来管理。编辑 /lib/systemd/system/mysqld.service 文件,内容如下:
[Unit]
Description=mysqld
After=network.target
[Service]
Type=forking
ExecStart=/etc/rc.d/init.d/mysqld start
ExecReload=/etc/rc.d/init.d/mysqld restart
ExecStop=/etc/rc.d/init.d/mysqld stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
- 重新加载 systemd 配置:
systemctl daemon-reload
- 启用并启动 MySQL 服务:
systemctl enable mysqld
systemctl start mysqld
- 检查 MySQL 服务是否启动成功,查看 3306 端口是否监听:
netstat -tunlp | grep 3306
4.5 设置 root 用户密码
使用初始随机密码登录 MySQL,然后设置新密码:
mysqladmin -u root -p password 'pwd123'
输入初始随机密码完成密码修改。
4.6 创建测试用户
在 Master1 和 Master2 上都执行以下操作,创建测试用户 test,用于后续验证实验:
mysql -u root -p
CREATE USER 'test'@'192.168.10.%' IDENTIFIED BY '123456';
GRANT ALL ON *.* TO 'test'@'192.168.10.%';
ALTER USER 'test'@'192.168.10.%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;
由于 MySQL 8.0 默认使用 caching_sha2_password 认证插件,将其替换为 mysql_native_password 旧版认证插件,确保从库能兼容。
五、配置 MySQL 双主复制
5.1 修改 Master1 配置文件
在 Master1 的 /etc/my.cnf 中修改或增加以下内容:
log-bin=/usr/local/mysql/data/mysql-bin #启用二进制日志并指定其存储路径
binlog_format=MIXED #定义二进制日志的记录格式为混合模式
server-id=1 #为mysql实例分配一个唯一的服务器标识符
5.2 修改 Master2 配置文件
在 Master2 的 /etc/my.cnf 中修改或增加以下内容:
log-bin=/usr/local/mysql/data/mysql-bin #启用二进制日志并指定其存储路径
binlog_format=MIXED #定义二进制日志的记录格式为混合模式
server-id=2 #为mysql实例分配一个唯一的服务器标识符
5.3 重启 MySQL 服务
在 Master1 和 Master2 上都执行以下命令重启 MySQL 服务:
systemctl restart mysqld
5.4 给从服务器授权
在 Master1 和 Master2 上都执行以下操作,创建用于复制的用户 myslave 并授权:
mysql -u root -p
CREATE USER 'myslave'@'192.168.10.%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'192.168.10.%';
ALTER USER 'myslave'@'192.168.10.%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;
同样,将认证插件替换为 mysql_native_password 以兼容从库。
5.5 查看主服务器状态
在 Master1 上登录 MySQL,执行以下命令查看主服务器状态:
show master status;
记录下 File 列的日志名(如 mysql-bin.000001)和 Position 列的偏移量(如 157),这两个值在配置从服务器时需要。
5.6 配置 Master1 作为 Master2 的从服务器
在 Master2 上登录 MySQL,执行以下命令配置同步,将 master_log_file 和 master_log_pos 参数替换为 Master1 的实际值:
change master to master_host='192.168.10.101', master_user='myslave', master_password='123456', master_log_file='mysql-bin.000001', master_log_pos=157;
start slave;
然后查看 Slave 状态,确保 Slave_IO_Running 和 Slave_SQL_Running 都为 YES:
mysql -uroot -p -e "show slave status \G" | grep Yes
5.7 配置 Master2 作为 Master1 的从服务器
在 Master1 上登录 MySQL,执行以下命令配置同步,将 master_log_file 和 master_log_pos 参数替换为 Master2 的实际值(假设 Master2 的主服务器状态中 File 和 Position 与 Master1 相同,实际操作中需根据 Master2 的 show master status 结果调整):
change master to master_host='192.168.10.102', master_user='myslave', master_password='123456', master_log_file='mysql-bin.000001', master_log_pos=157;
start slave;
同样查看 Slave 状态,确保两个值为 YES。
六、安装与配置 HAProxy
6.1 关闭 SELinux 和防火墙
在 Keepalived1 和 Keepalived2 上都执行以下命令:
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0
systemctl disable firewalld
systemctl stop firewalld
6.2 安装 HAProxy
使用 dnf 命令安装 HAProxy:
dnf install haproxy
6.3 编辑 HAProxy 配置文件
在 Keepalived1 和 Keepalived2 上都编辑 /etc/haproxy/haproxy.cfg 文件,内容如下(红色标注为关键配置):
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
user haproxy
group haproxy
daemon
maxconn 4000
defaults
mode tcp
log global
option tcplog
option dontlognull
retries 3
timeout http-request 5s
timeout queue 1m
timeout connect 5s
timeout client 1m
timeout server 1m
timeout http-keep-alive 5s
timeout check 5s
maxconn 3000
listen mysql
bind 0.0.0.0:3306 #显式指定监听地址和端口
balance leastconn #负载均衡算法,最少连接数算法
server mysql1 192.168.10.101:3306 check port 3306 maxconn 300
server mysql2 192.168.10.102:3306 check port 3306 maxconn 300
配置说明:
- mode tcp:表示 TCP 代理。
- listen mysql 0.0.0.0:3306:创建一个名为 mysql 的监听服务,绑定到所有网卡的 3306 端口(MySQL 默认端口),作为流量入口。
- balance leastconn:指定使用最少连接数算法分配请求,将新连接导向当前活跃连接最少的后端服务器,避免单点过载。
- server 声明两个 MySQL 服务器节点 mysql1 和 mysql2,分别指向 192.168.10.101:3306 和 192.168.10.102:3306。check port 3306 表示通过检查节点的 3306 端口是否响应,判断其存活状态;maxconn 300 限制每个后端节点的最大并发连接数为 300,防止节点被压垮。
6.4 检测配置文件并启动服务
- 检测 HAProxy 配置文件是否正确:
haproxy -c -f /etc/haproxy/haproxy.cfg
- 重启 HAProxy 服务:
systemctl restart haproxy
6.5 测试 HAProxy 代理
使用测试用户 test,通过 HAProxy 的代理端口登录 MySQL,验证 HAProxy 是否正常工作:
mysql -utest -p123456 -h192.168.10.103 -P3306
如果能成功登录,说明 HAProxy 代理配置正确。
七、安装与配置 Keepalived
7.1 关闭 SELinux 和防火墙
在 Keepalived1 和 Keepalived2 上都执行与 HAProxy 安装时相同的关闭 SELinux 和防火墙的命令。
7.2 安装 Keepalived
使用 dnf 命令安装 Keepalived:
dnf install keepalived
7.3 编辑 Keepalived1 配置文件
在 Keepalived1 上编辑 /etc/keepalived/keepalived.conf 文件,内容如下(红色标注为关键配置,与 Keepalived2 不同之处需注意):
! Configuration File for keepalived
global_defs {
router_id r1
}
vrrp_script chk_haproxy {
script "/etc/keepalived/chk.sh"
interval 2
}
vrrp_instance VI_1 {
state BACKUP
nopreempt #启用非抢占模式
interface ens33 #指定网络接口
virtual_router_id 51 #虚拟路由ID,同一VRRP实例需一致
priority 100 #优先级,数值越大优先级越高
advert_int 1 #通告间隔时间(秒)
authentication {
auth_type PASS
auth_pass 1111 #认证密码,同一VRRP实例需一致
}
virtual_ipaddress {
192.168.10.100 #虚拟IP地址
}
track_script {
chk_haproxy #关联监控脚本
}
notify_backup "/etc/init.d/haproxy restart" #当状态变为BACKUP时执行的命令
notify_fault "/etc/init.d/haproxy stop" #当检测到故障时执行的命令
}
启用 nopreempt 的作用是:节点 A(优先级 100,配置 nopreempt)故障→节点 B 成为 Master;节点 A 恢复→不触发抢占,节点 B 继续作为 Master,VIP 不切换;仅当节点 B 故障时,节点 A 才会重新成为 Master。
7.4 编辑 Keepalived2 配置文件
在 Keepalived2 上编辑 /etc/keepalived/keepalived.conf 文件,与 Keepalived1 的区别在于 router_id 和 priority:
! Configuration File for keepalived
global_defs {
router_id r2
}
vrrp_script chk_haproxy {
script "/etc/keepalived/chk.sh"
interval 2
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 99 #优先级低于Keepalived
7.5 添加监控脚本
在 Keepalived1 和 Keepalived2 上均需创建并编辑监控脚本chk.sh
,用于检测 HAProxy 进程是否存活:
vim /etc/keepalived/chk.sh
脚本内容如下:
#!/bin/bash
# 检查HAProxy进程数量
if [ $(ps -C haproxy --no-header | wc -1) -eq 0 ]; then
# 若进程不存在,停止Keepalived服务(触发VIP漂移)
/etc/init.d/keepalived stop
fi
赋予脚本执行权限:
chmod +x /etc/keepalived/chk.sh
7.6 启动 Keepalived 服务
在 Keepalived1 和 Keepalived2 上分别启动服务:
systemctl start keepalived
首次启动时,由于两者均配置为BACKUP
且 Keepalived1 优先级更高(100 vs 99),VIP(192.168.10.100)会先绑定在 Keepalived1 的ens33
接口上。可通过以下命令验证:
# 在Keepalived1上执行
ip a | grep ens33
# 输出应包含虚拟IP:192.168.10.100/32
7.7 测试 VIP 连接 MySQL
使用测试用户通过 VIP 连接 MySQL,验证负载均衡是否正常:
mysql -utest -p123456 -h192.168.10.100 -P3306
若能成功登录,说明 Keepalived 与 HAProxy 已协同工作,流量通过 VIP 转发至后端 MySQL 节点。
八、故障转移测试
8.1 模拟 Master1 节点故障
8.1.1 关闭 Master1 主机
# 在Master1所在主机执行(假设为物理机或虚拟机操作)
shutdown -h now
8.1.2 验证 HAProxy 健康检查机制
- 检测 Master1 状态:
# 在Keepalived1或Keepalived2上执行 mysql -utest -p123456 -h192.168.10.103 -P3306 # 通过HAProxy IP连接 mysql> SHOW PROCESSLIST; # 查看连接节点,应仅显示Master2
- 原理:HAProxy 通过
check port 3306
定期探测节点状态,发现 Master1 离线后,会自动将其从负载列表中剔除,流量仅转发至 Master2。
8.1.3 验证 VIP 访问连续性
ping 192.168.10.100 # VIP应保持可达
mysql -utest -p123456 -h192.168.10.100 -P3306 # 仍可通过VIP登录
8.2 模拟 Keepalived1 节点故障
8.2.1 关闭 Keepalived1 服务
systemctl stop keepalived # 在Keepalived1上执行
8.2.2 验证 VIP 漂移至 Keepalived2
- 查看 Keepalived2 的 IP 地址:
# 在Keepalived2上执行 ip a | grep ens33 # 应显示VIP已绑定:192.168.10.100/32
- 原理:Keepalived 通过 VRRP 协议检测到主节点(Keepalived1)离线后,优先级次高的 Keepalived2 会接管 VIP,确保 HAProxy 服务地址不变。
8.2.3 验证业务连续性
mysql -utest -p123456 -h192.168.10.100 -P3306 # 仍可通过VIP登录,流量经Keepalived2的HAProxy转发至Master2
8.3 模拟 Master2 节点故障(VIP 在 Keepalived2 上时)
8.3.1 关闭 Master2 主机
shutdown -h now # 在Master2所在主机执行
8.3.2 验证 HAProxy 自动切换至 Master1(假设 Master1 已恢复)
- 前提:若 Master1 之前故障后已恢复,HAProxy 会重新检测到其存活并加入负载列表。
- 验证连接:
mysql -utest -p123456 -h192.168.10.100 -P3306 mysql> SELECT @@server_id; # 应返回1(Master1的server-id)
8.4 模拟双节点故障(极端场景)
8.4.1 关闭 Keepalived2 和 Master2
# 在Keepalived2上执行
systemctl stop keepalived
# 在Master2上执行
systemctl stop mysqld
8.4.2 验证 VIP 漂移与服务降级
- 若 Keepalived1 已恢复:VIP 会重新漂移至 Keepalived1,HAProxy 继续转发流量至存活的 Master1。
- 若 Master1 也故障:整个集群不可用,需人工介入恢复(如启动备用节点或故障修复)。
九、架构优化与扩展建议
9.1 读写分离配置(可选)
HAProxy 支持根据 SQL 语句类型(读 / 写)分发流量,减轻主节点压力:
- 修改 HAProxy 配置文件,新增读负载均衡监听:
listen mysql_read bind 0.0.0.0:3307 mode tcp balance leastconn server mysql1 192.168.10.101:3306 check port 3306 maxconn 300 read-only server mysql2 192.168.10.102:3306 check port 3306 maxconn 300 read-only
- 应用层配置:读请求连接 3307 端口,写请求连接 3306 端口。
9.2 横向扩展 MySQL 节点
- 新增从节点:
- 在新节点上配置为任一主节点的从库(参考双主复制步骤)。
- 更新 HAProxy 配置,添加新节点:
server mysql3 192.168.10.105:3306 check port 3306 maxconn 300
- 扩展 Keepalived+HAProxy 节点:
- 增加 Keepalived 备份组,提升高可用层冗余。
- 使用 DNS 轮询或 LVS 负载均衡 HAProxy 节点,避免 HAProxy 单点。
9.3 监控与告警
- 基础监控:
- 使用 Prometheus+Grafana 监控 MySQL 指标(如 QPS、连接数、复制延迟)。
- 监控 Keepalived 的 VIP 状态和 HAProxy 的节点健康度。
- 告警规则:
- 复制延迟超过阈值时触发告警。
- HAProxy 节点故障或 VIP 漂移时发送通知。