前言

在生产环境中,数据库高可用性是系统稳定运行的关键保障。MySQL 主从复制提供了数据冗余和读写分离的能力,但单点故障问题仍然存在。

本文将详细介绍通过 MySQL 主主复制 + Keepalived 实现高可用数据库集群的方案,使用 VIP(虚拟 IP)作为应用的统一访问入口,实现故障自动转移与恢复,并采用非抢占模式确保集群稳定性。


在上一篇博客中MySQL数据库(六)—— MHA集群搭建与故障切换,我们介绍了MHA集群,这也是一种成熟的MySQL高可用解决方案。二者核心区别如下:

主主复制+Keepalived与MHA的核心区别对比表

对比维度 主主复制+Keepalived MHA
架构基础 双节点互为主从,依赖Keepalived管理VIP 一主多从,依赖MHA Manager和Node组件管理集群
故障检测粒度 节点级(服务器/网络故障),需额外脚本检测服务 服务级(MySQL进程/复制异常),原生支持精细检测
故障转移速度 秒级(VIP快速漂移) 10-30秒(需选新主、补全数据)
数据一致性 依赖复制同步,存在延迟和冲突风险 自动选最新从库并补全数据,一致性更优
扩展性 差,仅支持双节点 好,可扩展多从库提升读性能
脑裂风险 较高 较低
运维复杂度 低(架构简单) 高(需维护额外组件)
  • 主主复制+Keepalived是简单快速、成本低但数据一致性风险较高的双节点方案,适合中小规模非核心业务。
  • MHA是架构复杂、切换稍慢但数据可靠性更强的一主多从方案,适合对一致性要求高的核心业务。

一、环境规划

1.1 节点信息

节点名称 IP 地址 角色 服务组件
Node1 192.168.10.14 初始主节点 MySQL + Keepalived
Node2 192.168.10.15 初始备节点 MySQL + Keepalived
VIP 192.168.10.180 应用访问入口 虚拟 IP(由 Keepalived 管理)
客户端 192.168.10.120 测试客户端 MySQL
+-------------------------------------------------------+
|                  应用服务器                           |
|                                                       |
|                访问: 192.168.10.180                   |
+--------------------------|----------------------------+
                           |
                           | VIP: 192.168.10.180
                           |
         +-----------------+------------------+
         |                                    |
         |                                    |
+--------|---------+                +---------|--------+
|    Node1         |                |    Node2         |
| IP: 192.168.10.14|                | IP: 192.168.10.15|
|                  |                |                  |
| MySQL (Master1)  |<-------------->| MySQL (Master2)  |
| Keepalived (MASTER)               | Keepalived (BACKUP)|
|                  |                |                  |
+------------------+                +-------------------+

说明: 
MySQL双向复制:Node1和Node2之间互相复制数据。
Keepalived:Node1初始为MASTER状态,持有VIP;Node2为BACKUP状态。
当Node1的MySQL服务失败,Keepalived会将VIP转移到Node2,同时应用服务器访问VIP的请求会被路由到Node2

1.2 服务版本

  • 操作系统: CentOS 7.9
  • MySQL: 5.7
  • Keepalived: 1.3.5(yum 安装版本)

1.3 搭建思路

(1)安装 MySQL 数据库;
(2)配置 MySQL 互为主从;
(3)安装 keepalived 软件并配置故障转移;
(4)模拟 master 故障切换
(5)实现故障恢复

1.4 基础环境配置

# 关闭防火墙和增强功能
systemctl stop firewalld.service
setenforce 0

二、MySQL 主主复制配置

MySQL 主主复制实质上是双向的主从复制,两台服务器互为 Master 和 Slave。

MySQL的安装步骤可以参考:MySQL安装管理指南

2.1 修改 MySQL 配置文件

两台服务器均需进行以下配置,注意 server-id 和 auto_increment_offset 的区别:

2.1.1 Node1 (192.168.10.14) 配置

# /etc/my.cnf
[mysqld]
# 服务器唯一标识,Node1 设为 1,Node2 设为 2
server-id = 1

# 开启二进制日志
log-bin = mysql-bin

# 中继日志配置
relay-log = relay-log

# 自增步长设为2,避免主键冲突
auto_increment_increment = 2

# Node1 自增起始值为1
auto_increment_offset = 1

# 行级复制,保证数据一致性
binlog_format = ROW

# 开启 GTID
gtid_mode = ON
enforce_gtid_consistency = ON

# 禁止 Slave 自动启动,需手动开启
skip_slave_start = 1

# 可选:减少复制错误风险
log_slave_updates = ON

在这里插入图片描述

2.1.2 Node2 (192.168.10.15) 配置

# /etc/my.cnf
[mysqld]
server-id = 2
log-bin = mysql-bin
relay-log = relay-log
auto_increment_increment = 2
auto_increment_offset = 2    # Node2 自增起始值为2
binlog_format = ROW
gtid_mode = ON
enforce_gtid_consistency = ON
skip_slave_start = 1
log_slave_updates = ON

配置修改完成后,重启两台 MySQL 服务:

systemctl restart mysqld

2.2 创建复制账户和安全检查账户

在两台 MySQL 服务器上执行以下 SQL 命令,创建用于复制的账户:

-- 创建复制用户
CREATE USER IF NOT EXISTS 'myslave'@'%' IDENTIFIED BY '123456';

-- 授权复制权限
GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'%';

-- 创建检查用户
CREATE USER IF NOT EXISTS 'checker'@'localhost' IDENTIFIED BY 'CheckPass123';
-- USAGE权限表示"无权限",仅用于连接验证,是最安全的选项。
GRANT USAGE ON *.* TO 'checker'@'localhost';  

-- 创建测试用户
CREATE USER IF NOT EXISTS 'test'@'%' IDENTIFIED BY '123456';
-- 授权
GRANT ALL ON *.* TO 'test'@'%';

-- 刷新权限
FLUSH PRIVILEGES;

在这里插入图片描述

2.3 配置双向同步

2.3.1 在 Node1 上配置到 Node2 的复制

CHANGE MASTER TO
  MASTER_HOST = '192.168.10.15',
  MASTER_USER = 'myslave',
  MASTER_PASSWORD = '123456',
  MASTER_AUTO_POSITION = 1;

START SLAVE;

2.3.2 在 Node2 上配置到 Node1 的复制

CHANGE MASTER TO
  MASTER_HOST = '192.168.10.14',
  MASTER_USER = 'myslave',
  MASTER_PASSWORD = '123456',
  MASTER_AUTO_POSITION = 1;

START SLAVE;

2.4 验证复制状态

在两台服务器上执行以下命令,检查复制状态:

SHOW SLAVE STATUS\G;

确认以下关键指标:

  • Slave_IO_Running: Yes
  • Slave_SQL_Running: Yes
  • Seconds_Behind_Master: 0(表示无延迟)

在这里插入图片描述

三、Keepalived 非抢占模式配置

3.1 安装 Keepalived

两台服务器上安装 Keepalived:

yum install -y keepalived

在这里插入图片描述

3.2 配置 Keepalived

3.2.1 Node1 配置 (192.168.10.14)

创建或编辑 vim /etc/keepalived/keepalived.conf

! Configuration File for keepalived

global_defs {
    smtp_server 127.0.0.1
    # 路由器标识,每个节点唯一
    router_id MYSQL_HA_14
    # 必须注释
    #vrrp_strict
}

# MySQL 健康检查脚本配置
vrrp_script chk_mysql {
    # 检查脚本路径
    script "/etc/keepalived/check_mysql.sh"
    
    # 每 2 秒检查一次
    interval 2
   
    # 连续 2 次失败视为故障
    fall 2
    
    # 1 次成功视为恢复
    rise 1
}

# 虚拟路由配置
vrrp_instance VI_1 {
    # 两台均设为 BACKUP(非抢占关键)
    state BACKUP
    
    # 网卡名(根据实际系统情况修改)
    interface ens33
    
    # 虚拟路由 ID(两台必须相同)
    virtual_router_id 51
    
    # Node1 初始优先级 100(高于 Node2)
    priority 100
    
    # 非抢占模式(故障恢复不抢回 VIP)
    nopreempt
    
    # VRRP 通告间隔
    advert_int 1

    # 认证配置
    authentication {
        auth_type PASS
        auth_pass 1111  # 认证密码(两台相同)
    }

    # 虚拟 IP 配置
    virtual_ipaddress {
        192.168.10.180/24   # VIP
    }

    # 关联健康检查脚本
    track_script {
        chk_mysql
    }
}

3.2.2 Node2 配置 (192.168.10.15)

创建或编辑 vim /etc/keepalived/keepalived.conf

global_defs {
    smtp_server 127.0.0.1
    router_id MYSQL_HA_15
    #vrrp_strict
}

vrrp_script chk_mysql {
    script "/etc/keepalived/check_mysql.sh"
    interval 2
    fall 2
    rise 1
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    
    # Node2 初始优先级低于 Node1
    priority 90
    
    # 非抢占模式
    nopreempt
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.10.180/24 
    }
    track_script {
        chk_mysql
    }
}

3.3 MySQL 健康检查脚本

Node1和Node2上创建配置文件存储密码(避免明文)

# (权限设为 600,属主 root)
vim /etc/keepalived/.my.cnf
[client]
user=checker
password=CheckPass123
# 设置文件属主为 root(默认创建可能已是 root,但明确执行更稳妥)
chown root:root /etc/keepalived/.my.cnf

# 设置权限为 600(仅 root 可读可写,其他用户无任何权限)
chmod 600 /etc/keepalived/.my.cnf

在这里插入图片描述
在这里插入图片描述

Node1和Node2上创建健康检查脚本

vim /etc/keepalived/check_mysql.sh

#!/bin/bash

# 尝试连接 MySQL 并执行简单查询(如 SELECT 1)
if /usr/local/mysql/bin/mysql --defaults-extra-file=/etc/keepalived/.my.cnf -e "SELECT 1;" >/dev/null 2>&1; then
    exit 0  # MySQL 可正常使用
else
    exit 1  # MySQL 无法连接或查询失败
fi

给脚本添加执行权限:

chmod +x /etc/keepalived/check_mysql.sh

3.4 启动 Keepalived 服务

在两台服务器上启动 Keepalived 并设置开机自启:


systemctl start keepalived
systemctl enable keepalived

检查服务状态:

systemctl status keepalived

在这里插入图片描述

3.5 验证 VIP 绑定

在初始状态下,VIP 应该绑定在优先级较高的 Node1 上。可以使用以下命令验证:

ip addr show ens33

应该可以看到 VIP 192.168.10.180 绑定在 Node1 的网卡上。
在这里插入图片描述
在这里插入图片描述

3.6 测试使用VIP连接MySQL

# 在客户端使用vip连接mysql,测试能否正常使用
mysql -utest -p -h192.168.10.180 -P3306
show databases;
use test;
select * from test.info;

在这里插入图片描述
在这里插入图片描述

# 测试数据能否正常插入
insert into info values(3,'this is client');
# 在Node1和Node2上查看数据是否同步
select * from test.info;

在这里插入图片描述

在这里插入图片描述

四、故障转移与恢复流程

4.1 故障转移(Node1 节点故障)

当当前主节点(Node1)发生故障时,系统会自动进行故障转移:

# 关闭Node1节点mysql服务
systemctl stop mysqld
# 观察vip是否漂移到node2
ip addr show ens33

在这里插入图片描述

# 在客户端测试使用vip是否可以正常访问数据库
mysql -utest -p -h192.168.10.180 -P3306
show databases;
insert into info values(4,'this is client_2');
# 在node2(新主库)上查看数据是否正常插入
select * from test.info;

在这里插入图片描述

4.2 故障恢复(原主节点恢复)

当原主节点(Node1)故障修复后重新上线:

  1. 健康恢复:Node1 的 MySQL 服务恢复正常,健康检查脚本返回成功(exit 0)

  2. 非抢占行为:由于配置了 nopreempt 参数,Node1 不会主动抢回 VIP

  3. 状态维持:VIP 仍然保留在 Node2,Node1 作为热备节点运行

  4. 数据同步:Node1 自动从 Node2 同步故障期间的数据更新,保持数据一致性

# node1上恢复mysql
systemctl start mysqld
# 查看Node1节点数据
select * from info;

在这里插入图片描述

# 在node1上手动开启slave
start slave;
# 观察Node1数据是否同步
select * from info;

在这里插入图片描述

4.3 强制切换回原主(可选操作)

如果需要在原主节点恢复后手动将 VIP 切换回 Node1,可以执行以下操作:

在 Node2 上执行(主动放弃 VIP):

systemctl restart keepalived

在非抢占模式下,通常需要重启 Keepalived 服务来触发切换。
在这里插入图片描述

在这里插入图片描述

五、方案优势与注意事项

5.1 方案优势

  1. 高可用性:自动故障检测和转移,确保数据库服务持续可用

  2. 无缝切换:VIP 机制使应用无需修改配置即可实现故障转移

  3. 数据一致性:基于 GTID 的主主复制保证数据双向同步

  4. 防止脑裂:VRRP 协议和优先级机制确保同一时间只有一台主节点

  5. 故障恢复安全:非抢占模式避免原主恢复后抢回 VIP 导致的服务波动

  6. 灵活可扩展:可轻松添加更多节点或扩展为多层级复制架构

5.2 注意事项

  1. 网络配置:确保防火墙允许 VRRP 协议(IP 协议号 112)和 MySQL 端口(3306)通信

  2. 复制监控:定期检查主主复制延迟和状态,确保数据同步正常

  3. 数据初始化:使用 mysqldump 或 XtraBackup 初始化数据时确保数据一致性

  4. 密码安全:脚本中的 MySQL 密码应妥善保管,建议使用权限受限的专用监控账户

  5. 脑裂预防:确保 Keepalived 配置中的 virtual_router_id 在同一网段内唯一

  6. 性能考虑:主主复制会增加服务器负载,需监控系统资源使用情况

六、一些踩过的坑

6.1 将vrrp_strict注释

  • 在 Keepalived 配置中,vrrp_strict 是一个严格模式开关,当它未被注释(启用)时,会导致一些网络限制。
  • vrrp_strict 启用时,会强制开启多项安全限制,其中 最关键的一项是 “禁止响应来自非 VRRP 成员的 IP 数据包”,包括 ICMP(ping)请求。
  • 因此,在需要通过 VIP 对外提供服务的架构中,必须注释掉 vrrp_strict,否则 VIP 将无法承担业务流量的转发功能。如果有安全需求,建议通过防火墙规则(如 iptables)手动限制 VIP 的访问来源,而非依赖 vrrp_strict 的强制限制。

6.2 非抢占模式下不要配置weight

  1. 抢占模式记牢“必须加weight”:核心是通过动态调整优先级触发备节点主动抢占,否则无法检测“应用故障但节点存活”。
  2. 非抢占模式记牢“必须删weight”:核心是通过“失效判定”让备节点被动接管,加了weight会导致主节点仍发心跳,阻碍切换。
  3. 状态配置建议:抢占模式主节点设MASTER、备节点设BACKUP;非抢占模式建议均设BACKUP(靠优先级决定初始主备)+ nopreempt,避免逻辑混乱。

可参考博客:Keepalived核心配置解析:抢占/非抢占模式与weight参数的适配方案

6.3 健康检查脚本中使用绝对路径

  • 现象描述:

    • 使用相对路径时,健康检查脚本返回status 1(失败)
    • 手动执行同一脚本返回0(成功)
  • 问题根源:

    • 编译安装MySQL时,将/usr/local/mysql/bin/添加到了PATH环境变量
    • keepalived执行脚本时的环境变量与用户终端环境变量不一致
  • 解决方案:

    • 使用绝对路径调用命令
    • 创建软链接到系统标准路径

总结

本文详细介绍了基于 MySQL 主主复制和 Keepalived 非抢占模式的高可用数据库集群方案。通过双向数据同步和虚拟 IP 漂移机制,实现了数据库服务的自动故障转移和恢复。非抢占模式的配置确保了故障恢复过程中的系统稳定性,避免了频繁的主备切换带来的风险。

此方案适用于对数据库可用性要求较高的生产环境,提供了无缝故障转移能力,同时保证了数据一致性。实际部署时,需要根据具体业务需求和系统环境进行适当调整和优化,并建立完善的监控告警机制,确保整个数据库集群的稳定运行。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐