MySQL-同步常用指令
-- 安全重启复制
STOP SLAVE;
START SLAVE;
-- 跳过指定错误(慎用)
SET GLOBAL sql_slave_skip_counter = 1;
START SLAVE SQL_THREAD;
-- 重置复制(初始化场景)
RESET SLAVE ALL;
-- 检查主库当前状态
SHOW MASTER STATUS;
-- 查看同步基本状态
SHOW SLAVE STATUS\G
-- 重点关注:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
Last_SQL_Error:
-- GTID模式专用监控
SELECT * FROM performance_schema.replication_group_member_stats;
-- 建立同步复制
CHANGE MASTER TO
MASTER_HOST='192.168.1.102',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@secure123',
MASTER_AUTO_POSITION = 1; # 使用GTID模式
-- 检查复制冲突
SHOW STATUS LIKE 'slave%conflict%';
-- 监控GTID执行进度
SELECT @@global.gtid_executed;MySQL-MHA 高可用:主从同步
graph TD
%% 颜色定义
classDef manager fill:#ffcccc,stroke:#ff0000
classDef master fill:#ffcccc,stroke:#ff0000
classDef slave fill:#000000,stroke:#333,color:white
%% 架构图主体
MHA_manager[MHA manager]:::manager
Master1[Master]:::master
Master2[Master]:::master
Slave01_1[Slave01]:::slave
Slave02_1[Slave02]:::slave
Slave01_2[Slave01]:::slave
Slave02_2[Slave02]:::slave
%% 连接关系
MHA_manager --> Master1
MHA_manager --> Master2
Master1 --> Slave01_1
Master1 --> Slave02_1
Master2 --> Slave01_2
Master2 --> Slave02_2
%% 排版调整
subgraph "MySQL-MHA高可用方案架构图"
MHA_manager
Master1
Master2
Slave01_1
Slave02_1
Slave01_2
Slave02_2
endMySQL Master High Availability (MHA)是一套开源的 MySQL 高可用性解决方案,能够在主库发生故障时自动进行故障转移,将从库提升为新主库,从而保证 MySQL 服务的高可用性和可靠性。以下将从 MHA 的核心原理、架构组成、工作流程、部署实施以及与其他高可用方案的对比等方面进行全面解析。
MHA 概述与核心原理
MHA(Master High Availability)是专门为解决 MySQL 单点故障问题而设计的高可用解决方案。在传统主从复制架构中,当主库发生故障时,MHA 能够在10-30 秒内自动完成故障切换操作,最大程度保证数据一致性。
MHA 的核心原理基于以下几个关键机制:
故障检测机制:MHA Manager 通过心跳检测持续监控主库状态,一旦发现主库不可用(如 SSH 连接失败、MySQL 服务停止等),立即触发故障转移流程。
数据同步保障:MHA 会尝试从宕机的主服务器上保存二进制日志,并利用半同步复制技术确保数据尽可能少丢失。如果只有一个从库已接收最新二进制日志,MHA 会将这些日志应用到其他从库上。
自动拓扑重构:故障转移后,MHA 会自动重新配置所有从库的复制关系,使其指向新的主库,整个过程对应用程序完全透明。
MHA 架构与组件
MHA 架构由两个核心组件组成:MHA Manager和MHA Node。
MHA Manager(管理节点)
MHA Manager 是控制中心,通常部署在独立的服务器上,也可以部署在某个从节点上。其主要功能包括:
- 监控所有 MySQL 节点的健康状态
- 协调故障转移过程
- 管理虚拟 IP(VIP)的漂移
- 提供管理工具如
masterha_check_ssh、masterha_check_repl等
MHA Node(数据节点)
MHA Node 运行在每台 MySQL 服务器上(包括主库和从库),负责:
- 保存和恢复主库的二进制日志
- 应用差异的中继日志到其他从库
- 清除中继日志而不阻塞 SQL 线程
- 提供
save_binary_logs、apply_diff_relay_logs等工具
典型部署架构
一个标准的 MHA 环境至少需要三台服务器:一主两从。这种配置确保了即使一个从库也发生故障,系统仍然可以继续运行。
MHA 工作流程详解
MHA 的故障转移过程可以分为以下几个关键阶段:
故障检测阶段:
- MHA Manager 定期检查主节点的心跳信号
- 如果发现异常,会尝试通过 SSH 连接等方式二次确认主节点状态
新主库选举阶段:
- 根据从库的复制位置(position/GTID)选择数据最接近原主库的从库
- 如果数据一致,则按配置文件顺序选择
- 可通过设置
candidate_master=1强制指定优先候选主库
数据同步阶段:
- 从原主库保存未传输的二进制日志(如果可能)
- 确保新主库应用所有可用的二进制日志
- 将差异数据应用到其他从库
VIP 漂移与拓扑重构阶段:
- 将虚拟 IP(VIP)切换到新主库
- 重新配置所有从库指向新主库
- 更新 MHA 配置文件,移除故障主库信息
整个故障转移过程对应用程序透明,应用程序只需连接 VIP 即可,无需修改连接配置。
MHA 部署实施指南
环境准备
部署 MHA 需要满足以下基本条件:
- 至少三台服务器:一主两从+Manager 节点(可共用)
- 所有节点间配置 SSH 免密登录
- MySQL 版本一致(推荐 5.7 或更高)
- 时间同步(如 NTP 服务)
- 关闭防火墙和 SELinux
| 主机名 | IP 地址 | 配置 | 系统 |
|---|---|---|---|
| mysql-master | 192.168.148.170 | 4C8G | CentOS-7.9 |
| mysql-slave01 | 192.168.148.171 | 4C8G | CentOS-7.9 |
| mysql-slave02 | 192.168.148.172 | 4C8G | CentOS-7.9 |
主库配置
修改配置文件my.cnf:
[mysqld]
server-id=1 # 唯一ID,主库建议设为1
log-bin=master-bin # 指定生成二进制文件,可加目录
log-slave-updates # 指定开启slave角色的更新
innodb_flush_log_at_trx_commit=2
master_info_repository=table
relay_log_info_repository=TABLE
# 开启gtid
gtid_mode = ON
log_slave_updates
enforce_gtid_consistency重启 MySQL 服务
systemctl restart mysqld创建同步专用账户
-- 创建同步用户
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 设置权限
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
# 修改连接验证规则
ALTER USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 刷新权限
FLUSH PRIVILEGES;从库配置
修改配置文件my.cnf
[mysqld]
server-id = 2 # 唯一ID,必须与主库不同
log_bin = slave01-bin # 建议开启,便于级联复制
relay_log = mysql-relay01-bin # 中继日志文件前缀
read_only = ON # 从库设为只读(超级用户仍可写)
# 复制线程配置
slave_parallel_workers = 4 # 并行复制线程数(5.6+)
slave_parallel_type = LOGICAL_CLOCK # 基于事务的并行复制(5.7+)
slave_preserve_commit_order = 1 # 保持事务提交顺序(并行复制时)
# 复制可靠性配置
relay_log_info_repository = TABLE # 中继日志信息存表(mysql.slave_relay_log_info)
master_info_repository = TABLE # 主库信息存表(mysql.slave_master_info)
sync_relay_log = 1 # 每次事务提交都同步中继日志
sync_relay_log_info = 10000 # 每10000次事务同步一次复制元数据
# 开启gtid
gtid_mode = ON
log_slave_updates
enforce_gtid_consistency重启 MySQL 服务
systemctl restart mysqld配置复制链路
CHANGE MASTER TO
MASTER_HOST='192.168.148.171',
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='master-bin.000001',
MASTER_LOG_POS=157;启动复制并检查状态
START SLAVE;
SHOW SLAVE STATUS\G;确认Slave_IO_Running和Slave_SQL_Running均为Yes
SSH 免密配置
所有节点都执行,因为需要所有节点都需要免密互相通信,不止单向通信
# 生成密钥
ssh-keygen -t dsa -P "" -f ~/.ssh/id_dsa
# 将公钥分发到所有MySQL节点
ssh-copy-id -i ~/.ssh/id_dsa.pub root@192.168.148.170
ssh-copy-id -i ~/.ssh/id_dsa.pub root@192.168.148.171
ssh-copy-id -i ~/.ssh/id_dsa.pub root@192.168.148.172MHA 组件部署
创建 mha 用户
grant all privileges on *.* to mha@'%' identified by '123456';部署 MHA Node
所有 MySQL 节点安装 MHA Node 组件
yum -y install perl-DBD-MySQL
rpm -ivh mha4mysql-node-0.57-0.el7.noarch.rpm部署 MHA Manager
MHA 管理组件尽量单独部署或者非 master 节点
安装依赖
yum -y install epel* --nogpgcheck
yum -y install perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes --nogpgcheck
rpm -ivh mha4mysql-manager-0.57-0.el7.noarch.rpm创建配置文件
# 创建日志目录
mkdir -p /var/log/mha/mha1
cat > /etc/mha/mha1.cnf << EOF
[server default]
manager_log=/var/log/mha/mha1/manager.log #manager管理日志存放路径
manager_workdir=/var/log/mha/mha1 #manager管理日志的目录路径
master_binlog_dir=/data/mysql/data #binlog日志的存放路径
user=mha #管理账户
password=123456 #管理账户密码
ping_interval=2 #存活检查的间隔时间
repl_user=repl #主从复制的授权账户
repl_password=123456 #主从复制的授权账户密码
ssh_user=root #用于ssh连接的账户
[server1]
hostname=192.168.148.173
port=3306
candidate_master=1 # 标记为优先候选
[server2]
hostname=192.168.148.176
port=3306
candidate_master=1 # 标记为优先候选
[server3]
hostname=192.168.148.177
port=3306
no_master=1 # 禁止被选为主库
EOF- 建议为 1 个核心从库设置
candidate_master=1 - 为备份/异地从库设置
no_master=1
ssh 检查检测
masterha_check_ssh --conf=/etc/mha/mha1.cnf主从复制检测
masterha_check_repl --conf=/etc/mha/mha1.cnf创建 service 管理文件
这里设置的自动重启服务,并且不会清除 amoeba 配置文件宕机的节点配置。增加--remove_dead_master_conf会自动删除 MHA 配置文件中宕机的配置信息,Restart可以设置不自动重启
cat > /usr/lib/systemd/system/mha_manager.service << EOF
[Unit]
Description=MHA for MySQL Master Failover
After=mysqld.service network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/masterha_manager \
--conf=/etc/mha/mha1.cnf \
--ignore_last_failover
Restart=always
RestartSec=15s
StartLimitInterval=0
StandardOutput=file:/var/log/mha/mha1/manager.log
StandardError=file:/var/log/mha/mha1/manager.log
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable mha_manager
systemctl start mha_manager自动切换 master 的测试
# 1.在mysql-slave节点查看masterIP地址
mysql -uroot -p'123456' -e'show slave status\G'
# 2.停止mysql-master节点停止MySQL服务
systemctl stop mysqld
# 3.检测mysql-master自动切换状态
mysql -uroot -p'123456' -e'show slave status\G'MHA 故障还原
-- 重新启动刚才停掉的mysql节点
-- 指定master节点地址
CHANGE MASTER TO MASTER_HOST='192.168.148.173', \
MASTER_PORT=3306,MASTER_AUTO_POSITION=1, MASTER_USER='repl', \
MASTER_PASSWORD='123456';
-- 开启同步
start slave;
-- 查看同步状态
show slave status\GMHA 配置 VIP 漂移
设置 perl 脚本
需要提前安装 ifconfig:yum -y install net-tools
修改 my $vip 变量的值,设置虚拟 IP 地址
修改 ens32 网卡名
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
# 声明变量
my (
$command, $ssh_user, $orig_master_host,
$orig_master_ip, $orig_master_port, $new_master_host,
$new_master_ip, $new_master_port
);
# VIP 配置
my $vip = '192.168.148.160/24'; # 虚拟IP地址和子网掩码
my $key = '0'; # 网络接口别名后缀
my $ssh_start_vip = "/sbin/ifconfig ens32:$key $vip"; # 启动VIP的命令
my $ssh_stop_vip = "/sbin/ifconfig ens32:$key down"; # 停止VIP的命令
# 解析命令行参数
GetOptions(
'command=s' => \$command,
'ssh_user=s' => \$ssh_user,
'orig_master_host=s' => \$orig_master_host,
'orig_master_ip=s' => \$orig_master_ip,
'orig_master_port=i' => \$orig_master_port,
'new_master_host=s' => \$new_master_host,
'new_master_ip=s' => \$new_master_ip,
'new_master_port=i' => \$new_master_port,
);
exit &main();
# 主函数
sub main {
if ($command eq "stop" || $command eq "stopssh") {
# 停止VIP的逻辑
my $exit_code = 1;
eval {
print "Disabling the VIP on old master: $orig_master_host \n";
&stop_vip();
$exit_code = 0;
};
if ($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ($command eq "start") {
# 启动VIP的逻辑
my $exit_code = 10;
eval {
print "Enabling the VIP - $vip on the new master - $new_master_host \n";
&start_vip();
$exit_code = 0;
};
if ($@) {
warn $@;
exit $exit_code;
}
exit $exit_code;
}
elsif ($command eq "status") {
# 检查脚本状态
print "Checking the Status of the script..OK \n";
`ssh $ssh_user\@$orig_master_host \" $ssh_start_vip \"`;
exit 0;
}
else {
# 显示用法信息
&usage();
exit 1;
}
}
# 启动VIP的子程序
sub start_vip() {
`ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
# 停止VIP的子程序
sub stop_vip() {
`ssh $ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}
# 显示使用帮助的子程序
sub usage {
print "Usage: master_ip_failover --command=start|stop|stopssh|status "
. "--orig_master_host=host --orig_master_ip=ip --orig_master_port=port "
. "--new_master_host=host --new_master_ip=ip --new_master_port=port\n";
}添加执行权限:chmod +x /usr/local/bin/master_ip_failover
修改 mha 配置文件
/etc/mha/mha1.cnf
[server default]
master_ip_failover_script=/usr/local/bin/master_ip_failover #添加脚本位置重启动 mha 管理端
systemctl restart mha_manager提示:
如果启动 mha 进程失败,需要进行 mha 的连接检测
masterha_check_ssh --conf=/etc/mha/mha1.cnf ssh 连接检测
masterha_check_repl --conf=/etc/mha/mha1.cnf 主从复制检测
VIP 漂移测试
# 在master节点测试虚拟VIP是否生成
ip a
# 停止mysql服务
systemctl stop mysqld
# 到slave节点查看是否生成虚拟VIP
# mysql集群恢复
systemctl start mysqld
# 连接mysql开启同步
CHANGE MASTER TO MASTER_HOST='192.168.148.176', \
MASTER_PORT=3306,MASTER_AUTO_POSITION=1, MASTER_USER='repl', \
MASTER_PASSWORD='123456';
start slave;MHA 的优势与局限性
优势
- 快速自动故障转移:能在秒级完成故障检测和转移,大幅减少停机时间
- 数据一致性保障:通过半同步复制和二进制日志应用,最大程度减少数据丢失
- 架构简单成本低:相比商业解决方案,MHA 开源免费且架构相对简单
- 对应用透明:通过 VIP 机制,应用无需修改连接配置
- 灵活的部署选项:Manager 可单独部署也可与从节点共用
局限性
- 主从架构限制:只支持一主多从,不支持多主写入
- 数据延迟风险:异步复制下可能存在数据不一致窗口期
- 环境要求较高:需要 SSH 免密登录、网络稳定等前提条件
- 维护复杂度:需要定期检查复制状态和监控系统
- 版本兼容性:不同 MySQL 版本可能需要特定 MHA 版本支持
MySQL-主主复制+Keepalived
在 MySQL 双主架构 中,仅部署 Keepalived 是一种 轻量级高可用方案,适用于中小规模业务。
架构概述
- 两台 MySQL 互为主从(双主复制)。
- Keepalived 管理 VIP,当主节点 MySQL 故障时,VIP 漂移到备节点,实现 秒级故障切换。
环境准备
| 服务 | IP 地址 | 配置 | 系统 |
|---|---|---|---|
| mysql-masterA+Keepalived | 192.168.148.170 | 4C8G | openEuler 24.03(LTS) |
| mysql-masterB+Keepalived | 192.168.148.178 | 4C8G | openEuler 24.03(LTS) |
| Keepalived+HAProxy | 192.168.148.175 | 2C4G | openEuler 24.03(LTS) |
MySQL 主主复制配置
my.cn 配置
-- 节点1配置
[mysqld]
server-id = 1 # 保持和备节点不同
log-bin = mysql-bin
binlog-format = ROW
binlog-row-image = FULL
auto-increment-increment = 2
auto-increment-offset = 1 # 自增ID奇数和备节点不同
log-slave-updates = ON
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
slave_parallel_workers = 4
gtid_mode = ON
enforce_gtid_consistency = ON
relay_log_info_repository=TABLE # 使用表存储更可靠
master_info_repository=TABLE
sync_relay_log_info=1 # 每次事务提交都同步
-- 节点2配置
[mysqld]
server-id = 2 # 和主节点不同
log-bin = mysql-bin
binlog-format = ROW
binlog-row-image = FULL
auto-increment-increment = 2
auto-increment-offset = 2 # 自增ID偶数和主库不同
log-slave-updates = ON
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
slave_parallel_workers = 4
gtid_mode = ON
enforce_gtid_consistency = ON
relay_log_info_repository=TABLE # 使用表存储更可靠
master_info_repository=TABLE
sync_relay_log_info=1 # 每次事务提交都同步关键配置说明表:
| 配置项 | 节点 1 值 | 节点 2 值 | 作用说明 |
|---|---|---|---|
server-id | 101 | 102 | 集群内唯一标识,必须不同 |
auto-increment-offset | 1 | 2 | 双主架构下避免自增 ID 冲突 |
sync_binlog | 1 | 1 | 确保 binlog 不丢失,但影响 IO 性能 |
gtid_mode | ON | ON | 启用 GTID 简化复制管理 |
slave_parallel_workers | 4 | 4 | 提升从库应用日志速度 |
创建同步账号
-- 在两个节点上分别执行(密码建议不同)
CREATE USER 'repl'@'192.168.148.%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.148.%';
FLUSH PRIVILEGES;建立复制关系
主节点 1 配置同步主节点 2
CHANGE MASTER TO
MASTER_HOST='192.168.148.178', -- 主节点2的IP
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='mysql-bin.000001', -- 主节点2的binlog文件名
MASTER_LOG_POS=154; -- 主节点2的binlog位置
START SLAVE;
-- 查看基本状态
SHOW SLAVE STATUS\G
-- 重点关注:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
Last_SQL_Error:主节点 2 配置主同步节点 1
CHANGE MASTER TO
MASTER_HOST='192.168.148.170', -- 主节点1的IP
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='mysql-bin.000001', -- 主节点1的binlog文件名
MASTER_LOG_POS=154; -- 主节点1的binlog位置
START SLAVE;
-- 查看基本状态
SHOW SLAVE STATUS\G
-- 重点关注:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
Last_SQL_Error:Binlog 位置一致性
- 初始配置时,需确保两个主节点的数据完全一致(可通过
mysqldump备份恢复)。 - 使用
SHOW MASTER STATUS命令分别在两个节点上获取准确的File和Position值。
- 初始配置时,需确保两个主节点的数据完全一致(可通过
Keepalived 虚拟 VIP 漂移
官网地址:https://www.keepalived.org/
Keepalived 核心作用
- 虚拟 IP(VIP)管理:提供
192.168.148.160作为应用统一接入点 - 健康检测:通过自定义脚本监控 MySQL 服务状态
- 自动故障转移:主节点故障时 VIP 自动漂移到备用节点
服务搭建
安装服务(两个节点均需执行)
# CentOS/RHEL
dnf install -y keepalived
# Ubuntu/Debian
apt-get install -y keepalived
# 验证安装
keepalived --version环境准备
# 创建用户
useradd -Ms /sbin/nologin keepalived_script
# 创建日志目录
mkdir -p /var/log/keepalived
touch /var/log/keepalived/mysql_health.log
touch /var/log/keepalived/keepalived_notify.log
chown -R keepalived_script:keepalived_script /var/log/keepalived
# 创建脚本目录
mkdir -p /opt/script
chown -R keepalived_script:keepalived_script /opt/script
# visudo 授权
keepalived_script ALL= NOPASSWD: /usr/local/mysql/bin/*,/usr/bin/*主节点配置
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_MASTER # 唯一标识,建议用主机名
enable_script_security
script_user keepalived_script # 统一运行脚本用户
}
vrrp_script chk_mysql {
script "/opt/script/chk_mysql.sh" # 脚本路径
interval 2 # 检测间隔(秒)
timeout 2 # 脚本执行超时时间
rise 2 # 成功2次即认为恢复
fall 2 # 失败2次即认为宕机
weight -20 # 检测失败时优先级降低值
user keepalived_script # 指定运行用户
}
vrrp_instance VI_1 {
state BACKUP # 两台配置此处均是BACKUP,设为BACKUP将根据优先级决定主或从
interface ens32 # 绑定网卡(需根据实际调整)
virtual_router_id 51 # 虚拟路由ID(集群内唯一)
priority 100 # 初始优先级(0-255)
advert_int 1 # 心跳间隔(秒)
nopreempt # 非抢占模式
authentication {
auth_type PASS
auth_pass 1111 # 集群通信密码
}
# VIP配置
virtual_ipaddress {
192.168.148.160/24 dev ens32 label ens32:0 # VIP配置
}
track_script {
chk_mysql # 绑定健康检查脚本
}
# 单播模式防ARP广播问题
unicast_src_ip 192.168.148.170 # 主节点IP
unicast_peer {
192.168.148.178 # 备节点IP
}
# 状态通知脚本
notify "/opt/script/notify.sh"
}
EOF备节点配置
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_BACKUP # 唯一标识,建议用主机名
enable_script_security
script_user keepalived_script # 统一运行脚本用户
}
vrrp_script chk_mysql {
script "/opt/script/chk_mysql.sh" # 脚本路径
interval 2 # 检测间隔(秒)
timeout 2 # 脚本执行超时时间
rise 2 # 成功2次即认为恢复
fall 2 # 失败2次即认为宕机
weight -20 # 检测失败时优先级降低值
user keepalived_script # 指定运行用户
}
vrrp_instance VI_1 {
state BACKUP # 两台配置此处均是BACKUP,设为BACKUP将根据优先级决定主或从
interface ens32 # 网卡名
virtual_router_id 51 # 必须与主节点相同
priority 90 # 优先级低于主节点
advert_int 1
nopreempt # 非抢占模式
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟VIP
virtual_ipaddress {
192.168.148.160/24 dev ens32 label ens32:0
}
track_script {
chk_mysql
}
# 防止脑裂
unicast_src_ip 192.168.148.178 # 主节点IP
unicast_peer {
192.168.148.170 # 备节点IP
}
# 状态通知脚本
notify "/opt/script/notify.sh"
}
EOF检测MySQL状态脚本
sudo tee /opt/script/chk_mysql.sh <<'EOF'
#!/bin/bash
# MySQL健康检查脚本(带自定义日志)
# 返回值:0=健康,1=异常
# 配置区
LOG_DIR="/var/log/keepalived"
LOG_FILE="$LOG_DIR/mysql_health.log"
MYSQL_USER="keepalived_check"
MYSQL_PASS="Check@123" # 建议使用加密密码
MYSQL_HOST="localhost"
MAX_LOG_SIZE=10 # 日志文件大小上限(MB)
# 自定义日志函数
log() {
local timestamp=$(sudo date "+%Y-%m-%d %H:%M:%S.%3N")
local status=${2:-"INFO"}
echo "[$timestamp] [PID:$$] [$status] $1" >> "$LOG_FILE"
}
# 日志轮转检查
rotate_log() {
[ -f "$LOG_FILE" ] || return
local size=$(sudo du -m "$LOG_FILE" | sudo awk '{print $1}')
[ "$size" -ge $MAX_LOG_SIZE ] && {
sudo mv "$LOG_FILE" "${LOG_FILE}.$(sudo date +%Y%m%d%H%M)"
log "触发日志轮转"
}
}
check_service (){
# 级别1:系统服务状态
if ! systemctl is-active --quiet mysqld; then
log "MySQL服务未运行" "ERROR"
# sudo systemctl start mysqld || return 1
fi
# 级别2:TCP端口检测
sudo timeout 1 bash -c "cat < /dev/null > /dev/tcp/127.0.0.1/3306" || {
log "3306端口无响应" "ERROR"
return 1
}
# 级别3:MySQL协议检测
if ! /usr/local/mysql/bin/mysqladmin ping -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASS" --silent 2>/dev/null; then
log "MySQL协议级检测失败" "ERROR"
return 1
fi
# 级别4:复制状态检测
repl_status=$(/usr/local/mysql/bin/mysql -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASS" -e "SHOW SLAVE STATUS\G")
if [ -n "$repl_status" ]; then
if ! sudo grep -q "Slave_IO_Running: Yes" <<< "$repl_status"; then
log "Slave_IO_Running 线程停止" "ERROR"
#return 1
fi
if ! sudo grep -q "Slave_SQL_Running: Yes" <<< "$repl_status"; then
log "Slave_SQL_Running 线程停止" "ERROR"
#mysql -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASS" -e "STOP SLAVE; START SLAVE"
#return $?
fi
fi
return 0
}
# 主流程
rotate_log
# 主执行流程
if ! check_service; then
# 连续3次失败后触发故障转移
FAIL_COUNT=$(sudo grep -c "ERROR" $LOG_FILE | sudo tail -n1)
[ ${FAIL_COUNT:-0} -ge 3 ] && {
log "触发故障转移" "ACTION"
sudo systemctl restart keepalived
}
exit 1
fi
exit 0
EOF
chmod +x /opt/script/chk_mysql.sh
chown keepalived_script:keepalived_script /opt/script/chk_mysql.shVIP状态切换通知脚本
sudo tee /opt/script/notify.sh <<'EOF'
#!/bin/bash
# Keepalived
# 仅做日志记录和外部告警,不操作 HAProxy 或 VIP
STATE=$3 # MASTER / BACKUP / FAULT
VIP="192.168.148.160"
PRIO=$4
IP=$(hostname -I | awk '{print $1}')
LOG_FILE="/var/log/keepalived/keepalived_notify.log"
TIMESTAMP=$(date "+%F %T")
log() {
local LEVEL=${2:-INFO}
echo "${TIMESTAMP} [${LEVEL}] [${IP}] [${STATE}] [${PRIO}] ${1}" >> "$LOG_FILE"
}
# 记录状态变化
case "$STATE" in
MASTER)
log "节点成为 MASTER,VIP 已绑定"
;;
BACKUP)
log "节点成为 BACKUP,VIP 已释放"
;;
FAULT)
log "节点进入 FAULT 状态"
;;
STOP)
log "节点进入 STOP 状态"
;;
*)
log "未知状态: $STATE" "ERROR"
exit 1
;;
esac
# 可选:钉钉告警(取消注释并替换 token 即可)
# curl -s -H 'Content-Type: application/json' \
# -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"[$HOST] Keepalived $STATE $VIP\"}}" \
# https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN >/dev/null &
exit 0
EOF
sudo chmod +x /opt/script/notify.sh
sudo chown keepalived_script:keepalived_script /opt/script/notify.shMySQL-主主复制+Keepalived+HAProxy
本案例采用MYSQL双主+(Keepalived+HAProxy)集群架构。
架构概述
该方案通过以下组件实现高可用数据库服务:
- MySQL 主主复制:双主节点数据同步
- HAProxy:对数据库读写分离负载均衡
- Keepalived:虚拟 IP(VIP)漂移管理,它可以管理 haproxy,也可以单独管理 mysql
环境准备
| 服务 | IP 地址 | 配置 | 系统 |
|---|---|---|---|
| mysql-masterA | 192.168.148.170 | 4C8G | openEuler 24.03(LTS) |
| mysql-masterB | 192.168.148.178 | 4C8G | openEuler 24.03(LTS) |
| Keepalived+HAProxy | 192.168.148.179 | 2C4G | openEuler 24.03(LTS) |
| Keepalived+HAProxy | 192.168.148.175 | 2C4G | openEuler 24.03(LTS) |
MySQL 主主复制配置
my.cn 配置
-- 节点1配置
[mysqld]
server-id = 1 # 保持和备节点不同
log-bin = mysql-bin
binlog-format = ROW
binlog-row-image = FULL
auto-increment-increment = 2
auto-increment-offset = 1 # 自增ID奇数和备节点不同
log-slave-updates = ON
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
slave_parallel_workers = 4
gtid_mode = ON
enforce_gtid_consistency = ON
relay_log_info_repository=TABLE # 使用表存储更可靠
master_info_repository=TABLE
sync_relay_log_info=1 # 每次事务提交都同步
-- 节点2配置
[mysqld]
server-id = 2 # 和主节点不同
log-bin = mysql-bin
binlog-format = ROW
binlog-row-image = FULL
auto-increment-increment = 2
auto-increment-offset = 2 # 自增ID偶数和主库不同
log-slave-updates = ON
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
slave_parallel_workers = 4
gtid_mode = ON
enforce_gtid_consistency = ON
relay_log_info_repository=TABLE # 使用表存储更可靠
master_info_repository=TABLE
sync_relay_log_info=1 # 每次事务提交都同步关键配置说明表:
| 配置项 | 节点 1 值 | 节点 2 值 | 作用说明 |
|---|---|---|---|
server-id | 101 | 102 | 集群内唯一标识,必须不同 |
auto-increment-offset | 1 | 2 | 双主架构下避免自增 ID 冲突 |
sync_binlog | 1 | 1 | 确保 binlog 不丢失,但影响 IO 性能 |
gtid_mode | ON | ON | 启用 GTID 简化复制管理 |
slave_parallel_workers | 4 | 4 | 提升从库应用日志速度 |
创建同步账号
-- 在两个节点上分别执行(密码建议不同)
CREATE USER 'repl'@'192.168.148.%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.148.%';
FLUSH PRIVILEGES;建立复制关系
主节点 1 配置同步主节点 2
CHANGE MASTER TO
MASTER_HOST='192.168.148.178', -- 主节点2的IP
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='mysql-bin.000001', -- 主节点2的binlog文件名
MASTER_LOG_POS=154; -- 主节点2的binlog位置
START SLAVE;
-- 查看基本状态
SHOW SLAVE STATUS\G
-- 重点关注:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
Last_SQL_Error:主节点 2 配置主同步节点 1
CHANGE MASTER TO
MASTER_HOST='192.168.148.170', -- 主节点1的IP
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='mysql-bin.000001', -- 主节点1的binlog文件名
MASTER_LOG_POS=154; -- 主节点1的binlog位置
START SLAVE;
-- 查看基本状态
SHOW SLAVE STATUS\G
-- 重点关注:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
Last_SQL_Error:Binlog 位置一致性
- 初始配置时,需确保两个主节点的数据完全一致(可通过
mysqldump备份恢复)。 - 使用
SHOW MASTER STATUS命令分别在两个节点上获取准确的File和Position值。
- 初始配置时,需确保两个主节点的数据完全一致(可通过
HAProxy 故障自动转移恢复
HAProxy提供高可用性、负载均衡以及基于TCP和HTTP的应用代理,支持虚拟主机,它是免费、快速并且可靠的一种负载均衡解决方案。适合处理高负载站点的七层数据请求。类似的代理服务可以屏蔽内部真实服务器,防止内部服务器遭受攻击。
- 资源隔离:避免 MySQL 与 HAProxy 竞争 CPU/内存资源
- 安全分层:前端流量不直接接触数据库服务器
- 扩展灵活:可独立扩展 HAProxy 集群(如使用 Keepalived+多 HAProxy 节点)
- 网络优化:可部署在 DMZ 区与内网数据库区之间
安装服务
# CentOS/RHEL
sudo dnf install haproxy -y
# Ubuntu/Debian
sudo apt-get install haproxy -y配置文件
基本配置
sudo tee /etc/haproxy/haproxy.cfg <<'EOF'
# ======================
# HAProxy 2.9+ MySQL负载均衡配置模板
# ======================
# ======================
# Global 全局配置段
# ======================
global
# 日志配置(需配合syslog使用)
log 127.0.0.1 local0 info # 主日志(info级别)
log 127.0.0.1 local1 notice # 重要通知日志
# 安全隔离配置
# chroot /var/lib/haproxy # 将进程锁定在指定目录(防目录遍历攻击)
user haproxy # 以低权限用户运行
group haproxy # 以低权限组运行
daemon # 以守护进程方式运行
insecure-fork-wanted # 允许执行外部程序
external-check # 开启自定义检测脚本
# ======================
# Defaults 默认参数段
# ======================
defaults
log global # 继承全局日志配置
mode tcp # TCP四层代理模式(MySQL必须使用)
option tcplog # 启用TCP协议日志记录
option dontlognull # 忽略空连接日志(防日志洪水攻击)
retries 3 # 后端失败后的重试次数(防雪崩),对非幂等操作(如POST)建议设为0
# 超时控制(单位毫秒)
timeout connect 3000ms # 连接后端超时(内网建议2000-3000ms)
timeout client 50000ms # 客户端空闲超时(长连接场景需延长)
timeout server 50000ms # 服务端响应超时(OLTP建议>30s)
maxconn 3000 # 每个frontend的独立连接数限制,应小于global的maxconn
# ======================
# Frontend 前端配置段
# ======================
frontend mysql_frontend
bind *:3306 # 监听所有IP的3306端口
mode tcp
default_backend mysql_cluster # 默认转发到后端组
# ======================
# Backend 后端配置段
# ======================
backend mysql_cluster
mode tcp # TCP代理模式
balance leastconn # 轮询负载均衡算法
#option tcp-check #开启tcp检查
# option mysql-check user haproxy_check # 默认mysql检查,需要创建mysql无密码的账号
option external-check # 启用外部检查
external-check command "/etc/haproxy/check_mysql.sh"
# 后端服务器定义
server mysql_master_a 192.168.148.170:3306 inter 2000 check rise 2 fall 3
server mysql_master_b 192.168.148.178:3306 inter 2000 check rise 2 fall 3 backup
# backup参数是备用
# ======================
# stats 统计界面
# ======================
listen stats
bind *:9020
mode http
stats enable
stats hide-version
stats uri /haproxy?stats
stats realm "HAProxy Statistics"
stats auth admin:admin # 建议修改为复杂密码
stats refresh 30s
stats admin if TRUE
http-request deny if !{ src 192.168.148.0/24 } # 限制管理界面访问IP
EOFmysql 健康检查脚本
# 安装mysql客户端(如果使用TCP检查不用安装)
dnf -y install mysql
# 创建检查脚本
sudo tee /opt/script/check_mysql.sh <<'EOF'
#!/bin/bash
# 使用HAProxy内置变量传递参数
IP="${3:-$1}" # 兼容旧版HAProxy参数位置
PORT="${4:-$2}"
/bin/mysqladmin ping -u haproxy_check -p"123456" -h "$IP" -P "$PORT" --connect-timeout=1 &>/dev/null
exit $?
EOF
# 设置权限
chmod +x /opt/script/check_mysql.sh
chown haproxy:haproxy /opt/script/check_mysql.sh
# 创建HAProxy健康检查用户
CREATE USER 'haproxy_check'@'192.168.148.%' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON *.* TO 'haproxy_check'@'192.168.1.%' WITH GRANT OPTION;
FLUSH PRIVILEGES
## 默认mysql检查:option mysql-check user haproxy_check
CREATE USER 'haproxy_check'@'%' IDENTIFIED BY '';
FLUSH PRIVILEGES;语法验证
# 配置文件语法验证
haproxy -c -f /etc/haproxy/haproxy.cfg
# 日志
journalctl -u haproxy启动服务
# 开机自启并立即启动
systemctl enable haproxy --now
# 查看状态
systemctl status haproxyKeepalived 虚拟 VIP 漂移
官网地址:https://www.keepalived.org/
Keepalived 核心作用
- 虚拟 IP(VIP)管理:提供
192.168.148.160作为应用统一接入点 - 健康检测:通过自定义脚本监控 MySQL 服务状态
- 自动故障转移:主节点故障时 VIP 自动漂移到备用节点
服务搭建
安装服务(两个节点均需执行)
# CentOS/RHEL
dnf install -y keepalived
# Ubuntu/Debian
apt-get install -y keepalived
# 验证安装
keepalived --version环境准备
useradd -Ms /sbin/nologin keepalived_script
touch /var/log/keepalived_notify.log
chown keepalived_script:keepalived_script /var/log/keepalived_notify.log
# 创建脚本目录
mkdir -p /opt/script
chown -R keepalived_script:keepalived_script /opt/script
# visudo 授权
keepalived_script ALL= NOPASSWD: /usr/bin/systemctl *,/usr/sbin/ip *VIP 抢占模式
keepalive 通过组播,单播等方式(自定义),实现 keepalive 主备推选。工作模式分为抢占和非抢占(通过参数 nopreempt 来控制)。
主服务正常工作时,虚拟 IP 会在主上,备不提供服务,当主服务优先级低于备的时候,备会自动抢占虚拟 IP,这时,主不提供服务,备提供服务。也就是说,工作在抢占模式下,不分主备,只管优先级。
不管 keepalived.conf 里的 state 配置成 master 还是 backup,只看谁的 priority 优先级高(一般而言,state 为 MASTER 的优先级要高于 BACKUP)。priority 优先级高的那一个在故障恢复后,会自动将 VIP 资源再次抢占回来!!
当 state 状态都设置成 backup,如果不配置 nopreempt 参数,那么也是看 priority 优先级决定谁抢占 vip 资源,即也是抢占模式。
主节点配置
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_MASTER # 唯一标识,建议用主机名
enable_script_security
script_user keepalived_script # 统一运行脚本用户
}
# 检测haproxy
vrrp_script chk_haproxy {
script "systemctl is-active --quiet haproxy" # 检查 HAProxy
interval 2 # 检查间隔(秒)
timeout 2 # 脚本超时时间
weight -10 # 确保主节点失败后优先级低于备节点
rise 2 # 成功2次即恢复
fall 2 # 失败2次即判定宕机
user keepalived_script
}
vrrp_instance VI_1 {
state MASTER # 主节点
interface ens32 # 绑定网卡(需根据实际调整)
virtual_router_id 51 # 虚拟路由ID(集群内唯一)
priority 100 # 初始优先级(0-255)高于备节点
advert_int 1 # 心跳间隔(秒)
preempt # 开启抢占
preempt_delay 5 # 延迟5秒抢占,防止抖动
authentication {
auth_type PASS
auth_pass 1111 # 集群通信密码
}
virtual_ipaddress {
192.168.148.160/24 dev ens32 label ens32:0 # VIP配置
}
# 仅检测HAProxy进程存活,不检测MySQL
track_script {
chk_haproxy
}
# 防止脑裂
unicast_src_ip 192.168.148.179 # 本机IP
unicast_peer {
192.168.148.175 # 对端IP
}
notify "/opt/script/notify.sh"
}
EOF备节点配置
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_BACKUP # 唯一标识,建议用主机名
enable_script_security
script_user keepalived_script
}
# 检测haproxy
vrrp_script chk_haproxy {
script "/usr/bin/systemctl is-active --quiet haproxy" # 检查 HAProxy
interval 2 # 检查间隔(秒)
timeout 2 # 脚本超时时间
weight -10 # 确保主节点失败后优先级低于备节点
rise 2 # 成功2次即恢复
fall 2 # 失败2次即判定宕机
user keepalived_script
}
vrrp_instance VI_1 {
state BACKUP # 主节点
interface ens32 # 绑定网卡(需根据实际调整)
virtual_router_id 51 # 虚拟路由ID(集群内唯一)
priority 99 # 初始优先级(0-255)低于主节点
advert_int 1 # 心跳间隔(秒)
preempt # 开启抢占
preempt_delay 5 # 延迟5秒抢占,防止抖动
authentication {
auth_type PASS
auth_pass 1111 # 集群通信密码
}
virtual_ipaddress {
192.168.148.160/24 dev ens32 label ens32:0 # VIP配置
}
# 仅检测HAProxy进程存活,不检测MySQL
track_script {
chk_haproxy
}
# 防止脑裂
unicast_src_ip 192.168.148.175 # 本机IP
unicast_peer {
192.168.148.179 # 对端IP
}
# 状态通知脚本
notify "/opt/script/notify.sh"
}
EOF状态通知脚本(双节点)
sudo tee /opt/script/notify.sh <<'EOF'
#!/bin/bash
# Keepalived状态通知脚本
STATE=$3
VIP="192.168.148.160"
PRIO=$4
IP=$(hostname -I | awk '{print $1}')
INTERFACE="ens32"
# 日志文件
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S.%3N")
log() {
local LEVEL=${2:-INFO}
[ ! -e ${LOG_FILE} ] && touch ${LOG_FILE}
echo "${TIMESTAMP} [${LEVEL}] [${INTERFACE}:${IP}] [${STATE}] [${PRIO}] ${1}" >> ${LOG_FILE}
}
# 状态处理
case "$STATE" in
MASTER)
# 启动HAProxy(如果未运行)
if ! sudo systemctl is-active --quiet haproxy; then
sudo systemctl start haproxy && log "HAProxy已启动" || log "HAProxy启动失败" "ERROR"
fi
;;
BACKUP)
# 先停止HAProxy(如果运行中)
if sudo systemctl is-active --quiet haproxy; then
sudo systemctl stop haproxy && log "HAProxy已停止" || log "HAProxy停止失败" "ERROR"
fi
;;
STOP)
log "进入FAULT状态!紧急处理" "ERROR"
# 先停止HAProxy(如果运行中)
if sudo systemctl is-active --quiet haproxy; then
sudo systemctl stop haproxy && log "HAProxy已停止" || log "HAProxy停止失败" "ERROR"
fi
# 移除VIP(如果存在)
if sudo ip a show $INTERFACE | grep -q $VIP; then
sudo ip addr del $VIP/24 dev $INTERFACE
log "VIP已移除: $VIP"
fi
;;
*)
log "未知状态: $STATE" "ERROR"
exit 1
;;
esac
exit 0
EOF
chmod +x /opt/script/notify.sh
chown keepalived_script:keepalived_script /opt/script/notify.shVIP 禁止抢占模式
这种方式通过参数 nopreempt来控制。不管 priority 优先级,只要 MASTER 机器发生故障,VIP 资源就会被切换到 BACKUP 上。并且当 MASTER 机器恢复后,也不会去将 VIP 资源抢占回来,直至 BACKUP 机器发生故障时,才能自动切换回来。
nopreempt 这个参数只能用于 state 为 backup 的情况,所以在配置的时候要把 master 和 backup 的 state 都设置成 backup,这样才会实现 keepalived 的非抢占模式!
主节点配置
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_MASTER # 唯一标识,建议用主机名
enable_script_security
script_user keepalived_script # 统一运行脚本用户
}
# 检测haproxy
vrrp_script chk_haproxy {
script "systemctl is-active --quiet haproxy" # 检查 HAProxy
interval 2 # 检查间隔(秒)
timeout 2 # 脚本超时时间
weight -10 # 确保主节点失败后优先级低于备节点
rise 2 # 成功2次即恢复
fall 2 # 失败2次即判定宕机
user keepalived_script
}
vrrp_instance VI_1 {
state BACKUP # 两个节点保持一致
interface ens32 # 绑定网卡(需根据实际调整)
virtual_router_id 51 # 虚拟路由ID(集群内唯一)
priority 100 # 初始优先级(0-255)高于备节点
advert_int 1 # 心跳间隔(秒)
nopreempt # 关闭抢占
authentication {
auth_type PASS
auth_pass 1111 # 集群通信密码
}
virtual_ipaddress {
192.168.148.160/24 dev ens32 label ens32:0 # VIP配置
}
# 仅检测HAProxy进程存活,不检测MySQL
track_script {
chk_haproxy
}
# 防止脑裂
unicast_src_ip 192.168.148.179 # 本机IP
unicast_peer {
192.168.148.175 # 对端IP
}
notify "/opt/script/notify.sh"
}
EOF备节点配置
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id MYSQL_HA_BACKUP # 唯一标识,建议用主机名
enable_script_security
script_user keepalived_script
}
# 检测haproxy
vrrp_script chk_haproxy {
script "/usr/bin/systemctl is-active --quiet haproxy" # 检查 HAProxy
interval 2 # 检查间隔(秒)
timeout 2 # 脚本超时时间
weight -10 # 确保主节点失败后优先级低于备节点
rise 2 # 成功2次即恢复
fall 2 # 失败2次即判定宕机
user keepalived_script
}
vrrp_instance VI_1 {
state BACKUP # 两个节点保持一致
interface ens32 # 绑定网卡(需根据实际调整)
virtual_router_id 51 # 虚拟路由ID(集群内唯一)
priority 100 # 初始优先级(0-255)低于主节点
advert_int 1 # 心跳间隔(秒)
nopreempt # 关闭抢占
authentication {
auth_type PASS
auth_pass 1111 # 集群通信密码
}
virtual_ipaddress {
192.168.148.160/24 dev ens32 label ens32:0 # VIP配置
}
# 仅检测HAProxy进程存活,不检测MySQL
track_script {
chk_haproxy
}
# 防止脑裂
unicast_src_ip 192.168.148.175 # 本机IP
unicast_peer {
192.168.148.179 # 对端IP
}
# 状态通知脚本
notify "/opt/script/notify.sh"
}
EOF状态通知脚本(双节点)
sudo tee /opt/script/notify.sh <<'EOF'
#!/bin/bash
# Keepalived 非抢占模式通知脚本
# 仅做日志记录和外部告警,不操作 HAProxy 或 VIP
STATE=$3 # MASTER / BACKUP / FAULT
VIP="192.168.148.160"
PRIO=$4
IP=$(hostname -I | awk '{print $1}')
LOG_FILE="/var/log/keepalived_notify.log"
TIMESTAMP=$(date "+%F %T")
log() {
local LEVEL=${2:-INFO}
echo "${TIMESTAMP} [${LEVEL}] [${IP}] [${STATE}] [${PRIO}] ${1}" >> "$LOG_FILE"
}
# 记录状态变化
case "$STATE" in
MASTER)
log "节点成为 MASTER,VIP 已绑定"
;;
BACKUP)
log "节点成为 BACKUP,VIP 已释放"
;;
FAULT)
log "节点进入 FAULT 状态"
;;
*)
log "未知状态: $STATE" "ERROR"
exit 1
;;
esac
# 可选:钉钉告警(取消注释并替换 token 即可)
# curl -s -H 'Content-Type: application/json' \
# -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"[$HOST] Keepalived $STATE $VIP\"}}" \
# https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN >/dev/null &
exit 0
EOF
sudo chmod +x /opt/script/notify.sh
sudo chown keepalived_script:keepalived_script /opt/script/notify.sh系统优化
系统参数调优(keepalived两节点)
# 防止ARP冲突
echo "net.ipv4.conf.all.arp_ignore = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.arp_announce = 2" >> /etc/sysctl.conf
sysctl -p故障转移测试
# 1.通过VIP连接MySQL
mysql -uroot -p'123456' -h192.168.148.160 -e'select @@server_id'
# 2.到查询到的mysql服务器关闭mysql
systemctl stop mysqld
# 3.在查询检测故障迁移是否成功
mysql -uroot -p'123456' -h192.168.148.160 -e'select @@server_id'
# 4.将刚才停止的mysql服务重启
systemctl start mysqld
# 5.检测MySQL故障自动恢复
mysql -uroot -p'123456' -h192.168.148.160 -e'select @@server_id'
# 6.检测VIP漂移
systemctl stop keepalived故障排查指南
VIP 无法漂移
# 检查VRRP报文是否被拦截
tcpdump -i ens32 vrrp -n
# 验证健康检查脚本
mysql -h127.0.0.1 -uroot -pYourPassword -e 'SELECT 1'脑裂问题处理
# 强制释放VIP
ip addr del 192.168.148.160/24 dev ens32
# 检查哪个节点应持有VIP
ps aux | grep -E '[m]ysql|[k]eepalived'状态监控指标
# 检查Keepalived进程状态
keepalived -v
# 查看VIP绑定情况
ip -br addr show异常关机(重置源数据)
-- 1. 停止复制
STOP SLAVE;
-- 2. 重置所有复制信息(危险操作!确保主库数据完整)
RESET SLAVE ALL;
-- 3. 重新配置复制
CHANGE MASTER TO
MASTER_HOST='192.168.148.170',
MASTER_USER='repl',
MASTER_PASSWORD='your_password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=1184;
-- 4. 启动复制
START SLAVE;
-- 5. 验证状态
SHOW SLAVE STATUS\GMySQL-高可用:MyCAT
flowchart TB
%% 客户端层
C1[客户端01]
C2[客户端02]
C3[客户端03]
%% 代理层
subgraph HAProxy+Keepalived集群
H1[HAProxy\nkeepalived\n192.168.148.179]:::red
H2[HAProxy\nkeepalived\n192.168.148.175]:::red
VIP[VIP\n192.168.148.160\nVRRP]:::vip
end
%% MyCat层
subgraph MyCat集群
M1[MyCat\n192.168.148.150]:::blue
M2[MyCat\n192.168.148.151]:::blue
M3[MyCat\n192.168.148.152]:::blue
end
%% MySQL层
subgraph MySQL主主复制
DB1[MySQL Master\n192.168.148.170\n写]:::green
DB2[MySQL Master\n192.168.148.178\n写]:::green
end
%% 连接关系
C1 --> VIP
C2 --> VIP
C3 --> VIP
VIP --> H1 & H2
H1 --> M1 & M2 & M3
H2 --> M1 & M2 & M3
M1 -->|写| DB1
M1 -->|读| DB2
M2 -->|写| DB2
M2 -->|读| DB1
M3 -->|写| DB1
M3 -->|读| DB2
%% 样式定义
classDef red fill:#ffcccc,stroke:#ff0000
classDef blue fill:#cce5ff,stroke:#0066cc
classDef green fill:#ccffcc,stroke:#009900
classDef vip fill:#ffffcc,stroke:#ffcc00架构概述
- 客户端层:通过VIP访问系统
- 代理层:HAProxy+keepalived实现MyCat负载均衡
- 数据层:MySQL主主复制+MyCat读写分离
环境准备
| 服务 | IP 地址 | 配置 | 系统 |
|---|---|---|---|
| mysql-masterA | 192.168.148.170 | 4C8G | openEuler 24.03(LTS) |
| mysql-masterB | 192.168.148.178 | 4C8G | openEuler 24.03(LTS) |
| Keepalived+HAProxy | 192.168.148.179 | 2C4G | openEuler 24.03(LTS) |
| Keepalived+HAProxy | 192.168.148.175 | 2C4G | openEuler 24.03(LTS) |
| VIP | 192.168.148.160 | - | - |
| mycat-01 | 192.168.148.150 | 4C8G | openEuler 24.03(LTS) |
| mycat-02 | 192.168.148.151 | 4C8G | openEuler 24.03(LTS) |
| mycat-03 | 192.168.148.153 | 4C8G | openEuler 24.03(LTS) |
MySQL 主主复制配置
my.cn 配置
-- 节点1配置
[mysqld]
server-id = 1 # 保持和备节点不同
log-bin = mysql-bin
binlog-format = ROW
binlog-row-image = FULL
auto-increment-increment = 2
auto-increment-offset = 1 # 自增ID奇数和备节点不同
log-slave-updates = ON
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
slave_parallel_workers = 4
gtid_mode = ON
enforce_gtid_consistency = ON
relay_log_info_repository=TABLE # 使用表存储更可靠
master_info_repository=TABLE
sync_relay_log_info=1 # 每次事务提交都同步
-- 节点2配置
[mysqld]
server-id = 2 # 和主节点不同
log-bin = mysql-bin
binlog-format = ROW
binlog-row-image = FULL
auto-increment-increment = 2
auto-increment-offset = 2 # 自增ID偶数和主库不同
log-slave-updates = ON
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
slave_parallel_workers = 4
gtid_mode = ON
enforce_gtid_consistency = ON
relay_log_info_repository=TABLE # 使用表存储更可靠
master_info_repository=TABLE
sync_relay_log_info=1 # 每次事务提交都同步关键配置说明表:
| 配置项 | 节点 1 值 | 节点 2 值 | 作用说明 |
|---|---|---|---|
server-id | 101 | 102 | 集群内唯一标识,必须不同 |
auto-increment-offset | 1 | 2 | 双主架构下避免自增 ID 冲突 |
sync_binlog | 1 | 1 | 确保 binlog 不丢失,但影响 IO 性能 |
gtid_mode | ON | ON | 启用 GTID 简化复制管理 |
slave_parallel_workers | 4 | 4 | 提升从库应用日志速度 |
创建同步账号
-- 在两个节点上分别执行(密码建议不同)
CREATE USER 'repl'@'192.168.148.%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.148.%';
FLUSH PRIVILEGES;建立复制关系
主节点 1 配置同步主节点 2
CHANGE MASTER TO
MASTER_HOST='192.168.148.178', -- 主节点2的IP
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='mysql-bin.000001', -- 主节点2的binlog文件名
MASTER_LOG_POS=154; -- 主节点2的binlog位置
START SLAVE;
-- 查看基本状态
SHOW SLAVE STATUS\G
-- 重点关注:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
Last_SQL_Error:主节点 2 配置主同步节点 1
CHANGE MASTER TO
MASTER_HOST='192.168.148.170', -- 主节点1的IP
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='mysql-bin.000001', -- 主节点1的binlog文件名
MASTER_LOG_POS=154; -- 主节点1的binlog位置
START SLAVE;
-- 查看基本状态
SHOW SLAVE STATUS\G
-- 重点关注:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
Last_SQL_Error:Binlog 位置一致性
- 初始配置时,需确保两个主节点的数据完全一致(可通过
mysqldump备份恢复)。 - 使用
SHOW MASTER STATUS命令分别在两个节点上获取准确的File和Position值。
- 初始配置时,需确保两个主节点的数据完全一致(可通过
MyCat配置
Github:https://github.com/MyCATApache/Mycat-Server
mycat是阿里开源的一个分布式数据库中间层。MyCat的读写分离是基于后端MySQL集群的主从同步来实现的,而MyCat提供语句的分发功能。MyCat1.4开始支持MySQL主从复制状态绑定的读写分离机制,让读更加安全可靠。
安装JDK
# 添加执行权限
chmod +x jdk-8u131-linux-x64.rpm
# rpm安装
rpm -ivh jdk-8u131-linux-x64.rpm
# 编辑环境变量配置文件
vim /etc/profile
# 添加以下代码
export JAVA_HOME=/usr/java/jdk1.8.0_131
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:$CLASSPATH
export JAVA_PATH=${JAVA_HOME}/bin:${JRE_HOME}/bin
export PATH=$PATH:${JAVA_PATH}
# 让profile立即生效
source /etc/profile
# 查看java版本
java -version安装MyCAT
进入官网 http://www.mycat.org.cn/ 可以看到当前最新版本为 Mycat2,企业中一般不会选择最新版本,一是不够稳定,二是如果出现问题,解决方案不是太容易找到。
建议选择 1.6 版本下载和使用。
tar -zxf Mycat-server-1.6.7.6-release-20220524173810-linux.tar.gz -C /usr/local/进入到mycat解压后的目录下,可以看到有以下几个目录:
• bin:主要存放二进制可执行文件
• conf:mycat的配置文件
• lib:mycat需要依赖的第三库文件
• logs:打印mycat日志输出的地方
• version.txt:存放mycat的版本信息
环境变量配置
将mycat的二进制可执行文件配置到 /etc/profile 的PATH环境变量中。
# 环境变量配置文件
echo "export PATH=$PATH:/usr/local/mycat/bin" >> /etc/profile
# 使配置生效
source /etc/profile修改配置信息
user用户
mycat的核心配置文件在 conf 目录下的 server.xml。
该配置文件中可以配置访问 mycat 的用户名、密码,管理端口及数据端口等信息。
<!-- 超级用户root配置 -->
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<property name="defaultSchema">TESTDB</property>
<!--
No MyCAT Database selected 错误前会尝试使用该schema
如果为null,会报错
-->
<!-- 表级DML权限设置(示例) -->
<privileges check="false">
<schema name="TESTDB" dml="0110">
<table name="tb01" dml="0000"/>
<table name="tb02" dml="1111"/>
</schema>
</privileges>
</user>
<!-- 普通用户user配置 -->
<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
<property name="defaultSchema">TESTDB</property>
</user>schema节点
节点中各属性配置:
- name 在 server.xml 中进行配置,应与该配置名称包括大小写在内,完全一致。
- checkSQLschema 是否自动检查数据表的名字
- sqlMaxLimit 一次最大读取的数据量
- dataNode 数据的分片节点名称
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1"> </schema>dataNode节点
在该节点中主要配置的是分片信息。
节点中各属性配置:
- name 应与 schema 节点中的 dataNode 属性值相同
- dataHost 用于在 dataHost 节点中 name 属性的配置,以此关联;
- database 在数据库中真实存在的database名称
在之前验证主从复制功能时,创建了数据库 test,这里也配置上该数据库.
<dataNode name="dn1" dataHost="db_host" database="test" />物理数据库配置
主要是配置的dataHost节点。
dataHost:节点中各属性配置:
name:与 dataNode节点中 dataHost 属性值相同;
maxCon:最大连接数
minCon:最小连接数
balance:读操作的负载均衡类型,取值范围主含义如下:
取值 含义 0 所有读操作都将发送到 writeHost 服务器中 1 所有读操作都将发送到 readHost 服务器中 2 读操作将随机发送到 writeHost 或 readHost 服务器中 writeType:写操作的类型,取值范围及含义如下: | 取值 | 含义 | | --- | --- | | 0 | 所有写操作都将发送到 writeHost 服务器中 | | 1 | 所有写操作都将发送到 readHost 服务器中 | | 2 | 写操作将随机发送到 writeHost 或 readHost 服务器中 |
dbType:数据库类型
dbDriver:数据库驱动
switchType:自动切换
xml<dataHost name="db_host" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
writeHost、readHost节点
节点中各属性配置:
- host 服务名称,应有唯一性
- url 数据库服务器的IP和端口,如果是采用容器启动的数据库,这里的IP和端口都需要配置为容器所在物理机的IP和端口
- user 访问数据库的用户名
- password 访问数据库的密码
使用之前配置的主从数据库,master 节点用于写,slave 节点用于读。
需要注意的是,readHost 节点被包裹在 writeHost 节点中。
<writeHost host="hostM1" url="192.168.148.170:3306" user="root" password="123456">
<readHost host="hostM2" url="192.168.148.178:3306" user="root" password="123456"/>
</writeHost>完整配置结构
<!-- conf/schema.xml -->
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<table name="tb_test" primaryKey="id" dataNode="dn1,dn2" rule="mod-long"/>
</schema>
<dataNode name="dn1" dataHost="dh1" database="db1"/>
<dataNode name="dn2" dataHost="dh2" database="db1"/>
<dataHost name="dh1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="master1_ip:3306" user="root" password="123456">
<readHost host="hostS1" url="master2_ip:3306" user="root" password="123456"/>
</writeHost>
</dataHost>
<dataHost name="dh2" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM2" url="master2_ip:3306" user="root" password="123456">
<readHost host="hostS2" url="master1_ip:3306" user="root" password="123456"/>
</writeHost>
</dataHost>重新启动mycat
修改完配置信息后,通过指令重启mycat服务。
mycat restartmycat服务的命令
mycat可以支持以下命令:
• console:以交互控制台的方式启动mycat,当要停止服务时,可以直接输入 ctrl + c;
• start :以后台服务的方式启动mycat;
• stop :停止mycat服务;
• restart:重新启动mycat服务;
• status:查看mycat服务的状态;
• dump:使用mycat备份数据
连接mycat
mycat提供了类似数据库的管理方式,可以使用 MySQL 命令连接mycat,登录到mycat的9066(默认管理端口)执行相应的SQL语句,也可以通过JDBC方式进行远程连接,来管理Mycat。
输入以下指令,即可进入到mycat控制台。
- -u 访问mycat的用户名为,默认为 root;
- -p 访问mycat的密码,默认使用 123456,均与mysql登录参数及含义相同;
- -h 是启动mycat的服务器IP,这里不能使用locathost,必须是IP地址;
- -P 是mycat的管理端口,默认为 9066;
- -D mycat中的默认数据库 TESTDB。
mysql -uroot -p123456 -h 192.168.137.3 -P9066 -DTESTDB
该端口主要用于管理mycat,不能用于如增加、删除、修改、查询等操作
查看所有的可用命令
show @@help;查看所有的数据节点
show @@datanodeHAProxy
安装服务
# CentOS/RHEL
sudo dnf install haproxy -y
# Ubuntu/Debian
sudo apt-get install haproxy -yHAProxy配置
# /etc/haproxy/haproxy.cfg
frontend mysql_front
bind *:3306
mode tcp
default_backend mysql_back
backend mysql_back
mode tcp
balance roundrobin
server mycat1 192.168.148.150:8066 check
server mycat2 192.168.148.151:8066 check
server mycat3 192.168.148.152:8066 checkkeepalived
安装服务
# CentOS/RHEL
dnf install -y keepalived
# Ubuntu/Debian
apt-get install -y keepalived
# 验证安装
keepalived --versionkeepalived配置
# keepalived配置(/etc/keepalived/keepalived.conf)
vrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state MASTER # 另一台设为BACKUP
interface eth0
virtual_router_id 51
priority 100 # 另一台设为90
virtual_ipaddress {
192.168.148.160/24
}
track_script {
chk_haproxy
}
}启动服务
# 启动顺序
1. 启动两台MySQL并验证主主复制
SHOW SLAVE STATUS\G
2. 启动两台MyCat
./mycat start
3. 启动HAProxy+keepalived
systemctl start haproxy
systemctl start keepalived