MySQL MHA-Go 部署说明

本文说明 mha_go.yml 的支持边界与基础使用方式。mha_go.yml 部署的是 Go 重写的 MHA 管理器(mha-go),它不依赖 Perl MHA 工具链,以一个静态二进制和一份 YAML 配置运行在 manager 节点上。

当前 mha_go.yml 的定位是:在已经由 dbbot master_slave.yml 部署完成的一主多从 GTID 拓扑上部署 mha-go manager。如果还没有 MySQL 主从拓扑,请先运行 master_slave.yml

1. 原理

mha-go 围绕 GTID 单主复制做故障检测、在线切换与故障转移:

  • GTID-only:只支持 GTID 复制,不支持 binlog file/position 模式。
  • 拓扑发现:通过 SQL 探测主从角色、GTID、只读状态、复制线程、复制延迟与半同步状态。
  • 候选主选择slave_ips 的顺序映射为 candidate_priority,第一个从库优先级最高。
  • 故障恢复策略:dbbot 默认使用 availability-first,旧主整机不可达时允许提升最先进的存活从库;旧主上尚未复制出去的事务可能丢失。
  • 写入口抽象:默认 writer_endpoint.kind: none;开启 VIP 后由 /usr/local/bin/mha_ip_failover.sh 切换写入口。
  • 执行形态:manager 以 mysql 用户通过 systemd 常驻,日志使用 JSON 输出到 journal。

2. 与传统 MHA 的对比

维度传统 MHA (mha.yml)MHA-Go (mha_go.yml)
实现语言PerlGo,单个静态二进制
MySQL 支持主要面向 5.7 历史拓扑8.4.x9.7.0 ER/EA
复制模式binlog 位置 / GTID 均可仅 GTID
配置格式INI (app1.cnf)YAML (cluster.yaml)
运行形态masterha_manager 前台或 nohupmha-manager.service
运行用户通常 rootmysql 用户
写入口传统 VIP hook 脚本writer_endpoint 抽象,默认不启用
Dry-runswitch / failover-execute 原生支持 --dry-run

新建 MySQL 8.4/9.7 GTID 主从集群优先使用 mha_go.yml;只有维护 MySQL 5.7 历史拓扑时才继续使用传统 mha.yml

3. 支持边界

  • 目标架构:一主多从 + 一个 MHA-Go manager。
  • MySQL 版本:8.4.x9.7.0 ER/EA。
  • 复制模式:必须开启 gtid_mode=ONenforce_gtid_consistency=ON
  • 默认部署:纯异步复制,semi_sync.policy: disabledsalvage.policy: availability-first
  • 半同步:只有已经安装并启用 MySQL semi-sync 插件时,才建议把策略改为 preferredrequired
  • 不支持:MySQL 5.78.09.6、非 GTID 复制。

MySQL 9.7.x 使用 glibc2.28 包,不支持 CentOS/RHEL 7 系列;这类系统请使用 MySQL 8.4.x 作为 MHA-Go 前置主从集群。

4. 拓扑约定

mha_go.yml 复用 [dbbot_mysql] 主机组,通过 master_ip / slave_ips / manager_ip 区分节点:

  • master_ip:当前主库,在 cluster.yaml 中登记为 db1
  • slave_ips:从库列表,在 cluster.yaml 中登记为 db2db3 等。
  • manager_ip:运行 mha-manager 的节点,必须是 slave_ips 中的从库,不能是 master_ip。默认使用最后一个从库,使主库宿主机故障时不会同时带走故障转移控制器;第一个从库仍作为最高优先级候选主。

dbbot 默认测试环境:

192.168.161.11  primary
192.168.161.12  replica / preferred failover candidate
192.168.161.13  replica / manager

5. 关键变量

编辑 mysql_ansible/playbooks/vars/var_mha_go.yml

master_ip: 192.168.161.11
slave_ips:
  - 192.168.161.12
  - 192.168.161.13
sub_nets: "192.168.161.%"

manager_ip: "{{ slave_ips | last }}"
mha_go_cluster_name: app1

mha_go_semi_sync_policy: disabled
mha_go_semi_sync_wait_for_replica_count: 0
mha_go_semi_sync_timeout: 5s
mha_go_salvage_policy: availability-first
mha_go_salvage_timeout: 30s

常用变量:

变量默认值说明
manager_ip`{{ slave_ipslast }}`
mha_go_binary_dest/usr/local/bin/mhamanager 节点上的二进制路径
mha_go_config_dir/etc/mhacluster.yaml 目录
mha_go_log_dir/var/log/mha日志目录
mha_go_service_enabledtrue是否 enable systemd 服务
mha_go_semi_sync_policydisableddisabledpreferredrequired
mha_go_salvage_policyavailability-first主库不可达时的一致性/可用性取舍
mha_go_writer_endpoint_enabledfalse是否启用 VIP 写入口切换

6. 前置条件

make_mha_go 执行前会检查:

  • 每个节点的 datadir 下存在 master_slave_finish.flag,即已经完成 master_slave.yml
  • 每个节点都开启 gtid_mode=ONenforce_gtid_consistency=ON
  • master_ipslave_ipsmanager_ip 都能在 inventory 中找到,且 manager_ip 必须属于 slave_ips

mha_go.yml 当前 role 顺序:

pre_check_and_set -> make_mha_go

它不会重装 MySQL,也不会重建复制关系。

7. 执行入口

cd /usr/local/dbbot/mysql_ansible/playbooks
python3 /usr/local/dbbot/portable-ansible/ansible-playbook \
  -i ../inventory/hosts.ini \
  mha_go.yml \
  -e dbbot_confirmation_input=confirm

使用 dbbot 公开默认密码测试时,还需要显式允许:

-e '{"fcs_allow_dbbot_default_passwd": true}'

8. 执行后产物

manager 节点上会生成:

  • /usr/local/bin/mha:当前 dbbot 内置的 mha-go 静态二进制。
  • /etc/mha/cluster.yaml:集群配置,topology.kind: mysql-replication-single-primaryreplication.mode: gtid
  • /etc/systemd/system/mha-manager.service:以 mysql:mysql 运行 mha manager --log-format json
  • /var/log/mha/:日志目录。

manager 节点的 datadir 下会生成 mha_go_finish.flag

启用 VIP 时还会生成:

  • /usr/local/bin/mha_ip_failover.shroot:mysql 0750 的 VIP 切换脚本。
  • /home/mysql/.ssh/id_rsa_dbbot_mha_go:仅供 manager 执行 VIP hook 的 RSA-3072 专用私钥。
  • /home/mysql/.ssh/known_hosts_dbbot_mha_go:由部署期采集并固定的节点 host key。
  • /usr/local/libexec/dbbot-mha-go-vip-ssh:root 管理的 forced-command 白名单,专用公钥不能获得普通 mysql shell。
  • /etc/sudoers.d/dbbot-mha-go-vip:只允许当前 VIP、掩码、网卡对应的 ip addr add/delarping 命令。
  • /var/lib/dbbot/mha-go-vip.yml:用于在关闭 VIP 功能时准确清理旧地址和配套权限的受管状态。

9. 常用命令

在 manager 节点执行:

mha version
mha check-repl --config /etc/mha/cluster.yaml
mha switch --config /etc/mha/cluster.yaml --new-primary db2 --dry-run
mha failover-plan --config /etc/mha/cluster.yaml
systemctl status mha-manager
journalctl -u mha-manager -f

failover-planfailover-execute 会在主库仍存活时阻断,这是正常保护。

9.1 从库重启后的只读状态

MHA-Go 复用 master_slave.yml 的异步复制基础配置,my.cnf 保持 super_read_only=OFFmaster_slave.yml 首次建立复制时会在从库运行时设置 super_read_only=ON,但该状态不会通过基础模板持久化。当前 MHA-Go manager 会探测并报告只读状态异常,不会在 mysqld 重启后自动修改数据库变量。

每次数据库实例或主机重启后,在恢复业务流量前必须重新发现实际拓扑。只在已经确认的从库上执行:

SHOW REPLICA STATUS\G
SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;
SET GLOBAL super_read_only = ON;
SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;

验收时应保证当前主库的 read_onlysuper_read_only 均为 0,每个从库均为 1,复制线程正常,然后重新执行:

mha check-repl --config /etc/mha/cluster.yaml

不要根据旧的 master_ipcluster.yaml 角色判断当前真实主库,也不要在真实主库上执行 SET GLOBAL super_read_only=ON。异步拓扑没有把该值写死为 ON,是为了避免当前主库 crash 后在尚未发生切换时自行恢复为只读;重启后的从库只读状态需要由运维流程持续保证。

9.2 整机故障、Salvage 与 manager 生命周期

dbbot 生成的 cluster.yaml 不包含节点级 ssh 配置。MHA-Go 的 SQL 探测、候选主选择、提升和复制重指向不依赖 OS SSH,也不会默认执行 SSH binlog salvage。只有启用 VIP 后,外部 VIP hook 才需要 manager 上的 mysql 用户通过 SSH 登录各节点;剧本会建立并验证这条专用链路。因此,未启用 VIP 的部署测试通过,不能证明 VIP SSH 路径可用。

默认 availability-first 会在旧主整机不可达、无法证明或抢救旧主独有事务时继续提升最先进的存活从库。这符合传统 MHA 的可用性优先语义,但纯异步复制下可能丢失只提交在旧主且尚未传到任何从库的事务。若业务必须优先避免这种风险,可改为 salvage-if-possiblestrict;旧主完全不可达且没有节点级 SSH/agent salvage 时,这两种策略会阻断自动提升并需要人工处理。

主库直接探测达到阈值后,manager 还会等待从库复制 IO 线程不再确认旧主可达,以降低 manager 单点网络隔离造成的误切换。总故障确认时间因此还受 MySQL replica_net_timeout、复制 heartbeat 和操作系统 TCP 超时影响,并不只由 monitor.intervalfailure_thresholdreconfirm_timeout 三项相加决定。

一次故障转移成功后 manager 按设计正常退出,因为原 cluster.yaml 的角色已经过期。先恢复或重建旧主、按真实拓扑更新 master_ip / slave_ips,再重新运行 mha_go.yml。若执行被阻断或中途失败,当前内置二进制会返回非零,Restart=on-failure 会拉起服务重试;仍应先查看 journal 中的第一处 blocking/failed step,不能把持续重启当作已恢复。

10. 卸载

mha_go_unsafe_uninstall.yml 会先停止并禁用 mha-manager.service,删除 mha-go manager 文件和配置的 VIP,再按当前 mysql_port 清理 MySQL 实例数据、日志和运行目录:

cd /usr/local/dbbot/mysql_ansible/playbooks
python3 /usr/local/dbbot/portable-ansible/ansible-playbook \
  -i ../inventory/hosts.ini \
  mha_go_unsafe_uninstall.yml \
  -e dbbot_confirmation_input=confirm

如果你只想保留 MySQL 主从拓扑,不要执行这个入口;它会一并清理当前 mysql_port 对应的 MySQL 实例。

11. 启用 VIP 写入口

默认不启用 VIP。如果需要固定写入口:

mha_go_writer_endpoint_enabled: true
vip: 192.168.161.10
vip_netmask: "32"
net_work_interface: enp1s0

开启后剧本会:

  • 检查 VIP 未错误驻留在非主库节点,并首次绑定到 master_ip
  • 在 manager 生成独立 SSH key,把带 forced-command 限制的公钥写入所有 MySQL 节点,并以 BatchMode=yes、严格 host-key 校验逐节点验证;白名单外命令会被拒绝。
  • 安装参数级 sudo 白名单;不会授予无参数限制的 /usr/sbin/ip,也不会授予 scp
  • 部署 VIP hook,并在切换时从旧主移除 VIP、添加到新主、发送 gratuitous ARP。

vipvip_netmasknet_work_interface 必须与真实网络一致。dbbot 192.168.161.* 默认测试 inventory 中的样例环境通常使用 enp1s0;其他环境请先用 ip routeip addr 确认真实网卡名。修改 VIP、掩码或网卡时,修改 var_mha_go.yml 后重新运行剧本,让 VIP 脚本、sudo 白名单和当前主库绑定一起更新;不要只手改其中一个文件。若旧 VIP 仍在非主库,剧本会停止并要求先清理,避免重复地址。

mha_go_writer_endpoint_enabled 改回 false 并重跑剧本时,dbbot 会依据 /var/lib/dbbot/mha-go-vip.yml 删除旧 VIP、sudo policy、专用 SSH 授权和脚本。不要在关闭前手工删除该状态文件。修改 manager_ip 并重跑时,非 manager 节点上旧的 mha-manager.service 会先被停止和删除,旧 binary/config/finish flag 也会清理,避免本地内存 lease 下出现双 manager;旧 journal 日志不会被这一步删除。

12. 注意事项

  • mha_go.yml 是增量部署入口,不是 MySQL 主从初始化入口。
  • manager_ip 必须是 slave_ips 中的从库。不要把 manager 与当前主库放在同一节点,否则主机级故障会同时中断数据库和故障转移控制器。
  • 默认 availability-first 优先恢复写可用性,不代表零数据丢失;纯异步复制的 RPO 取决于故障发生时事务是否已经到达存活从库。
  • MySQL 9.7.0 的 cluster.yaml 会自动写入 version_series: "9.7";MySQL 8.4.x 会写入 "8.4"
  • dbbot 默认半同步策略是 disabled,避免在未安装 semi-sync 插件的纯异步主从上误报。
  • cluster.yaml 权限为 0640,属主为 mysql:mysql,因为 systemd 服务以 mysql 用户读取它。