MySQL 8.4 上使用社区版 MHA-Go

摘要

0.它是什么、适不适合你

MHA-Go 是原版 MySQL MHAGo 重写:继续做「一主多从 + 外部 Manager 选主」,但换成单二进制、YAML 配置、只认 GTID。它不是原版 0.58 的补丁包,命令、配置、RPM 全部不通用。

版本支持(先看清再装)

详细边界与选型背景见 MySql-MHA的构建方法 里「社区版 MHA-Go 的版本边界」。这里只保留动手所需的结论:

MySQL 版本 MHA-Go
8.4.x 主力支持(发布基线)
9.7 ER/EA 前向兼容;节点配置写 version_series: "9.7"
5.7 / 8.0 / 9.6 不支持
非 GTID(文件位点复制) 不支持

因此:MySql-MHA的构建方法 里的 mysql-8.0.30 + 原版 MHA 不能直接换成 MHA-Go;要先把数据面迁到 8.4 + GTID(或官方支持的 9.7),再部署 Manager。

和原版 MHA、InnoDB Cluster 的取舍

原版 MHA 0.58 MHA-Go InnoDB Cluster
复制模型 异步/半同步主从 同左,仅 GTID Group Replication
选主方式 外部 Manager 外部 Manager 组内自动选主
部署形态 Manager + 每台 Node(Perl) 单二进制,不必每台装 Node GR + Shell + Router
适合 历史 5.7/8.0 存量 必须保留传统主从、又已上 8.4/9.7 新部署首选

新业务优先 MySQL 8.4 InnoDB Cluster 的构建方法。只有应用、运维流程已经绑死「异步一主多从 + VIP/ProxySQL」,才评估 MHA-Go。项目较新,上线前务必做故障演练。

优缺点(选型时先看)

把它理解成:面向 MySQL 8.4 的现代化外部 MHA,不是「MHA 0.58 的简单 Go 翻译」。相对原版,核心路径从「SSH + Node + 解析 binlog/relay-log」转向「SQL 探测 GTID / 复制线程 / 半同步状态」,切换步骤也更结构化。

优点

优点 说明
与 8.4 匹配 主力目标就是 8.4.x + GTID 单主,避开原版依赖的已删除语句和 Perl 包袱
部署干净 单 Manager 二进制,无 Perl、不强制每台装 Node;YAML 配置;有预编译包和 SHA256SUMS
发现方式现代化 用 SQL 看角色、GTID、read_only、复制线程、延迟、半同步,而不是绑死在 mha4mysql-node
流程可审计 候选优先级 → fencing → 晋升 → GTID 追平 → writer_endpoint → 校验;switch / failover-execute 支持 --dry-run
写入口抽象清晰 VIP/Proxy 走 writer_endpoint(含 precheck/verify),与 fencing(旧主还能不能写)分开建模
有 8.4 集成测试痕迹 仓库含 test/integration/mysql84,比只在 README 声称「支持 8.x」更可信一些
salvage 策略显式 strict / salvage-if-possible / availability-first 把「补不齐就停」还是「继续切」写进配置

缺点 / 风险

缺点 说明
成熟度不足 项目很新,缺少 MHA 0.58 那种多年大规模踩坑史;技术路线新 ≠ 生产验证充分
版本覆盖窄 不支持 5.7 / 8.0 / 9.6,也不支持非 GTID;不能当老 MHA 全场景替代品
9.7 仅 Partial 前向兼容轨道,不是和 8.4 同级的发布基线
默认对「旧主 SQL 突然不可达」偏保守 起步常是 salvage-if-possible 且无 SSH salvage;kill -9/断电后若有未传到从库的 GTID,failover 可能中止
Manager 仍是单点 默认 local-memory lease 只防同进程并发,防不了双 Manager,也解决不了脑裂
fencing / VIP 要自己做实 工具给能力,网络分区、旧主复活继续写、VIP 双活、应用重连都要靠演练
与 8.4 认证仍有缝 切主重建复制时当前实现不自动加 GET_SOURCE_PUBLIC_KEY=1;非 TLS 下 IO 线程可能起不来
切完仍有运维负担 成功 failover 后 manager 会退出;要改 YAML、再拉起监控,并验证旧主如何以从库加回
生态偏薄 案例、排障资料少于 InnoDB Cluster / 云托管 HA;进阶能力要读 full 配置和 operations

一句话:适合认真评估的是 MySQL 8.4 + GTID 一主多从、又必须保留传统拓扑;不适合老版本 MySQL、非 GTID,或未做网络分区 / VIP / 旧主回加演练就直接挂核心库。能接受三节点 Group Replication 时,仍优先 MySQL 8.4 InnoDB Cluster 的构建方法

官方资料:

1.节点规划

先按 MySql8.4单节点、主从、双主的构建方法 搭好 一主两从 + GTID,再装 MHA-Go。本文假设:

1
2
3
4
mha-manager:  10.250.0.91     只跑 mha 二进制与配置(不要和当前主同机)
db1(primary): 10.250.0.11
db2(replica): 10.250.0.12 优先晋升候选
db3(replica): 10.250.0.13
  • Manager 需要能访问三台 MySQL 的 3306;默认拓扑发现走 SQL,不强制 Manager 到各库的 SSH。

  • 同一时刻只跑一个 manager。 默认 lease 是进程内 local-memory不是跨进程或跨主机互斥;起两个 manager 防不了脑裂。

  • VIP / 写入口脚本若要用 SSH 漂移地址,再单独配密钥与 sudo,见第 8 节。

  • 各机 /etc/hosts 或 DNS 能解析主机名更省事,但 cluster.yaml 里直接写 IP 也可以。

前置检查(每台 MySQL):

1
2
3
4
5
6
SHOW VARIABLES WHERE Variable_name IN (
'gtid_mode', 'enforce_gtid_consistency',
'log_bin', 'log_replica_updates'
);
-- 从库上执行;主库没有复制通道
SHOW REPLICA STATUS\G

gtid_modeenforce_gtid_consistency 必须为 ON

2.创建运维账号与复制账号

当前主库执行一次即可(经 GTID 复制到从库)。管理账号给 MHA-Go 做探测、切主;复制账号给切主后的 CHANGE REPLICATION SOURCE TO 使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 管理账号:host 用 CIDR,覆盖 Manager 与各 MySQL 节点所在网段
CREATE USER IF NOT EXISTS 'mha'@'10.250.0.0/24'
IDENTIFIED BY '<STRONG_PASSWORD>';

GRANT SELECT,
RELOAD,
PROCESS,
SUPER,
REPLICATION CLIENT,
REPLICATION SLAVE,
REPLICATION_SLAVE_ADMIN,
SYSTEM_VARIABLES_ADMIN,
SESSION_VARIABLES_ADMIN
ON *.* TO 'mha'@'10.250.0.0/24';

-- 若主从搭建时还没有独立复制账号,一并创建
CREATE USER IF NOT EXISTS 'repl'@'10.250.0.0/24'
IDENTIFIED BY '<REPL_STRONG_PASSWORD>';
GRANT REPLICATION SLAVE, REPLICATION CLIENT
ON *.* TO 'repl'@'10.250.0.0/24';

FLUSH PRIVILEGES;

上游 README 快速上手仍用这套偏宽的权限列表;更细的动态权限清单见 operations_zh.md。上游示例常用 'mha'@'10.250.%',MySQL 8.4 已弃用 Host 里的 % / _ 通配,生产更建议 CIDR。

从库应保持 read_only=ONsuper_read_only=ONMySql8.4单节点、主从、双主的构建方法 的异步主从模板有时不把 super_read_only 写进 my.cnf(防止宕机主库重启后进只读),因此每次 mysqld 或主机重启后,在确认角色后再检查:

1
2
3
-- 只在已确认的从库上执行
SET GLOBAL super_read_only = ON;
SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;

主库两者都应是 0,从库都应是 1。不要凭旧配置猜测谁是主。

8.4 复制认证:切主后 IO 线程可能起不来

8.4 默认 caching_sha2_password。按 MySql8.4单节点、主从、双主的构建方法主从之间若未启用 TLS,手工搭从库时要在 CHANGE REPLICATION SOURCE TO 里加 GET_SOURCE_PUBLIC_KEY=1

写作时(v0.1.4)MHA-Go 重建复制通道时会写 SOURCE_HOST/PORT/USER/PASSWORDSOURCE_AUTO_POSITION=1;只有配置了非 disabledsql.tls_profile 时才会附加 SOURCE_SSL=1不会自动加 GET_SOURCE_PUBLIC_KEY=1

因此实验/生产请二选一:

  1. 推荐:节点间启用 TLS,并在每个节点的 sql.tls_profile 写成 required(或至少 preferred),让切主后的通道走 SSL。

  2. 继续明文实验:切主或 failover 后立刻在各从库检查 SHOW REPLICA STATUS;若 Replica_IO_Running=No 且认证失败,手工补:

1
2
CHANGE REPLICATION SOURCE TO GET_SOURCE_PUBLIC_KEY=1;
START REPLICA;

上线前用一次真实 switch --dry-run 后再做一次真实 switch,确认复制能自动站起来,不要假设工具已经替你处理好了公钥交换。

3.在 Manager 上安装二进制

写作时最新版为 v0.1.4;安装前到 Releases 核对版本号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sudo mkdir -p /usr/local/soft /etc/mha/secrets
cd /tmp

MHA_VERSION=v0.1.4
case "$(uname -m)" in
x86_64) ASSET="mha_${MHA_VERSION}_linux_amd64" ;;
aarch64|arm64) ASSET="mha_${MHA_VERSION}_linux_arm64" ;;
*) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac

curl -fL -o "$ASSET" \
"https://github.com/fanderchan/mha_go/releases/download/${MHA_VERSION}/${ASSET}"
curl -fL -o SHA256SUMS \
"https://github.com/fanderchan/mha_go/releases/download/${MHA_VERSION}/SHA256SUMS"
grep " ${ASSET}$" SHA256SUMS | sha256sum -c -

sudo install -m 0755 "$ASSET" /usr/local/bin/mha
mha version

也可以从源码静态编译(需要 Go 1.25+):

1
2
3
4
5
git clone https://github.com/fanderchan/mha_go.git
cd mha_go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-extldflags=-static" -o mha ./cmd/mha
sudo install -m 0755 mha /usr/local/bin/mha

本文 systemd 把日志打到 journal,不必单独建 /var/log/mha。若自己改成文件日志,再创建该目录并收紧属主权限。

4.写 cluster.yaml 与密钥文件

起步配置以仓库 examples/cluster-8.4.yaml 为准;VIP、fencing、hooks、SSH salvage、secondary_checks 等见 cluster-8.4.full.yamloperations_zh.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
sudo tee /etc/mha/cluster.yaml >/dev/null <<'EOF'
name: app1

# 生产开 manager 前建议加上 secondary_checks,用存活从库二次确认主是否真死
# controller:
# secondary_checks:
# - name: from-db2
# observer_node: db2
# timeout: 2s
# - name: from-db3
# observer_node: db3
# timeout: 2s

topology:
kind: mysql-replication-single-primary

replication:
mode: gtid
semi_sync:
# 纯异步主从用 disabled;半同步插件装好后再改 preferred / required
policy: disabled
salvage:
# strict:规划阶段一旦发现缺失事务就直接阻断,不做 salvage
# salvage-if-possible(默认):尽量 GTID/SSH 补齐,补不齐则中止 failover
# availability-first:与上一项相同,但补齐失败只告警,仍继续晋升(可能丢未复制事务)
policy: salvage-if-possible
timeout: 30s

writer_endpoint:
kind: none

# 未显式配置 fencing 时:旧主 SQL 仍可达则会做默认 required 的 read_only fence
# fencing 管「旧主还能不能写」;writer_endpoint 管「新写流量去哪」——两件事

nodes:
- id: db1
host: 10.250.0.11
port: 3306
version_series: "8.4"
expected_role: primary
sql:
user: mha
password_ref: file:/etc/mha/secrets/admin-password
replication_user: repl
replication_password_ref: file:/etc/mha/secrets/repl-password
# 节点间已配 TLS 时改为 required / preferred,切主会带 SOURCE_SSL=1
tls_profile: disabled

- id: db2
host: 10.250.0.12
port: 3306
version_series: "8.4"
expected_role: replica
candidate_priority: 100
# no_master: true # 禁止晋升(只读/报表从库常用)
# ignore_fail: true # 该节点评估错误降为警告,不阻断整体
sql:
user: mha
password_ref: file:/etc/mha/secrets/admin-password
replication_user: repl
replication_password_ref: file:/etc/mha/secrets/repl-password
tls_profile: disabled

- id: db3
host: 10.250.0.13
port: 3306
version_series: "8.4"
expected_role: replica
candidate_priority: 90
sql:
user: mha
password_ref: file:/etc/mha/secrets/admin-password
replication_user: repl
replication_password_ref: file:/etc/mha/secrets/repl-password
tls_profile: disabled
EOF

sudo install -d -m 0700 /etc/mha/secrets
printf '%s\n' '<STRONG_PASSWORD>' | sudo tee /etc/mha/secrets/admin-password >/dev/null
printf '%s\n' '<REPL_STRONG_PASSWORD>' | sudo tee /etc/mha/secrets/repl-password >/dev/null
sudo chmod 0600 /etc/mha/secrets/admin-password /etc/mha/secrets/repl-password
sudo chown root:root /etc/mha/secrets/admin-password /etc/mha/secrets/repl-password
sudo chmod 0640 /etc/mha/cluster.yaml

要点:

  • candidate_priority 越大越优先被选为新主;上面让 db2 优先于 db3。只读从库用 no_master: trueexpected_role: observer 也可表示观察者角色。

  • 密码用 file: 引用,不要写进 YAML,也不要放进 systemd 的 Environment=。另两种:env:...(临时/容器)、plain:...(仅 demo)。

  • MySQL 9.7 集群把每个节点的 version_series 改成 "9.7" 即可。

  • 起步模板故意没有节点 ssh 段。 旧主 SQL 已死、又需要补缺失 GTID 时:要么按 full 示例配 SSH salvage(本机 mysql 客户端、对端 mysqlbinlog),要么接受 salvage-if-possible 中止,要么显式改成 availability-first 并接受可能丢数。

5.检查复制健康与命令分工

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

正常时大致如下:

1
2
3
4
5
6
7
Cluster: app1  mode=mysql-replication-single-primary  primary=db1  nodes=3
- db1 role=primary health=alive addr=10.250.0.11:3306 ro=false sro=false
- db2 role=replica health=alive addr=10.250.0.12:3306 ro=true sro=true
replica: source=db1 io=true sql=true lag=0s autopos=true
- db3 role=replica health=alive addr=10.250.0.13:3306 ro=true sro=true
replica: source=db1 io=true sql=true lag=0s autopos=true
Assessment: OK

Assessment: OK 之前不要开 manager,也不要做真实 switch

场景 用哪个命令
健康检查 / 监控探针 check-repl
主库还活着,计划内换主 switch(可加 --new-primary
主库已确认死亡,人工触发 failover-plan / failover-execute(可加 --candidate
常驻自动切主 manager

全局参数(各业务子命令通用):

参数 说明
--config 配置文件路径(必填)
--discoverer sql|static 默认 sql,靠 SQL 发现实时角色
--log-level debug / info / warn / error
--log-format textjson
--dry-run manager / switch / failover-execute 可预演

6.在线切换(计划内)

mha-manager 已在跑,先停掉再切,避免自动探测与手工 switch 并发(lease 只护当前进程内操作):

1
2
3
sudo systemctl stop mha-manager
mha switch --config /etc/mha/cluster.yaml --new-primary db2 --dry-run
mha switch --config /etc/mha/cluster.yaml --new-primary db2

成功后建议顺序:

  1. cluster.yaml:新主 expected_role: primary,旧主改为 replica,并调整 candidate_priority

  2. mha check-repl,确认拓扑与配置一致;同时核对各从库 SHOW REPLICA STATUS(尤其非 TLS 时的认证)。

  3. 尽快 systemctl start mha-manager,恢复自动 HA。旧主可以稍后修好,再以从库身份加回配置并 check-repl

省略 --new-primary 时,会按 candidate_priority 等规则选最优从库;生产建议显式指定。

7.故障转移与常驻监控

一次性计划 / 执行

主库仍存活时,failover-plan / failover-execute 往往会阻塞或拒绝——它们面向「主已不可用」的场景。

1
2
3
4
5
6
7
mha failover-plan --config /etc/mha/cluster.yaml
mha failover-plan --config /etc/mha/cluster.yaml --candidate db2
mha failover-execute --config /etc/mha/cluster.yaml --dry-run
# 确认主库已挂且业务可接受当前 salvage 策略的后果后:
mha failover-execute --config /etc/mha/cluster.yaml
# 或指定候选:
# mha failover-execute --config /etc/mha/cluster.yaml --candidate db2

普通从库在规划时已不可达,通常不阻断 failover(可用 ignore_fail 降噪);事后把节点修好再加回配置即可,不必非 Clone 不可——GTID 一致时直接改挂常更轻。

用 systemd 跑 manager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sudo tee /etc/systemd/system/mha-manager.service >/dev/null <<'EOF'
[Unit]
Description=MHA Go Manager
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/mha manager --config /etc/mha/cluster.yaml --log-format json
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now mha-manager
sudo systemctl status mha-manager
journalctl -u mha-manager -f

警告⚠️
成功 failover 后 manager 会正常退出(exit 0)。 旧配置里的角色已过期。正确顺序是:先更新 YAML 反映新主 → check-repl → 立刻 systemctl start mha-manager;旧主可以稍后修复再加回。Restart=on-failure 只处理崩溃和非零退出,不会在「成功切主后」带着过期配置自动拉起。

实验室演练建议

  1. check-repl 为 OK;若要测自动切,先启用并确认只有一个 mha-manager

  2. 在主库制造故障前写几笔可核对数据。注意:起步配置默认 salvage-if-possible无 SSH salvage——kill -9 mysqld 后若仍有未传到从库的 GTID,manager / failover-execute 很可能中止晋升,这是预期行为,不是工具坏了。

  3. 想练「一定会切」:先把业务写停干净、确认从库已追上,再杀主;或临时改成 availability-first(接受可能丢数);或按 full 示例配好 SSH salvage。

  4. 观察 journalctl -u mha-manager:成功则更新 YAML、恢复监控;失败则读阻断原因,不要强行多起 manager。

  5. 应用必须能重连重试;写事务要幂等。

8.写入口(VIP)与 fencing

起步配置里 writer_endpoint.kind: none,MHA-Go 只负责数据库角色切换,不替你改应用连接。

若要用 VIP / Proxy:

  • cluster-8.4.full.yaml 打开 writer_endpointkind: vipproxy),配置 command、建议再配 precheck_command / verify_command

  • 同时理解 fencing:未配置时,旧主 SQL 可达会做默认 read_only fence;旧主完全不可达时靠 salvage 策略决定是否继续。VIP 漂走 ≠ 旧主一定写不进去,直连旧主仍可能脑裂,需要 stonith / 云路由等更强隔离时再加 fencing steps。

  • 配合 dbbotmha_go.yml 时,可用其生成的 mha_ip_failover.sh、受限 SSH 与 sudo;手工环境需保证 VIP 不会双活、ARP 能刷新、脚本失败时策略符合预期。

  • Keepalived / ProxySQL 只解决路由,不能替代 MHA-Go 的晋升逻辑。

9.和半同步、丢数的关系

  • 本文起步默认是 salvage-if-possible + 纯异步:补不齐缺失 GTID 时 failover 中止,一般不会「默默切成功并丢数」。真正「补不齐也继续」的是 availability-first

  • semi_sync.policy: disabled 表示 MHA-Go 不把半同步状态纳入健康判断。要半同步时:先按 MySql8.4单节点、主从、双主的构建方法 安装并启用 rpl_semi_sync_source / rpl_semi_sync_replica,再把 policy 改成 preferred(半同步异常打 warning)或 required(异常打 error 并视为不健康)。

  • strict:规划阶段发现缺失事务就阻断,不做 salvage,最保守,也最容易要人工介入。

  • 旧主整机不可达、又没有 SSH salvage 时:salvage-if-possible / strict 都可能阻塞自动晋升。

高可用缩短停写,不等于 RPO=0。细节见 MySql8.4单节点、主从、双主的构建方法

10.日常命令速查

子命令 作用
check-repl 一次性健康检查
manager 常驻监控;主库确认死亡后自动 failover,成功后退出
switch 计划内在线切换(主仍健康时用;先停 manager)
failover-plan 只生成计划;可 --candidate
failover-execute 执行故障转移;可 --dry-run / --candidate
1
2
3
4
5
6
7
mha version
mha check-repl --config /etc/mha/cluster.yaml
sudo systemctl stop mha-manager
mha switch --config /etc/mha/cluster.yaml --new-primary db2 --dry-run
mha failover-plan --config /etc/mha/cluster.yaml --candidate db2
systemctl status mha-manager
journalctl -u mha-manager -f

11.什么时候别用它

第 0 节「优缺点」里的风险,落到决策上就是下面几条:

  • 还在用 MySQL 5.7 / 8.0 / 9.6,或还没开 GTID:用不上 MHA-Go。

  • 可以接受三节点组复制:优先 InnoDB Cluster,少维护一套外部 Manager。

  • 需要跨机房自动容灾:看 ClusterSet / 云厂商多可用区,不要指望单套异步复制 + MHA-Go 扛地域故障。

  • 没做切主演练、应用没有重试与幂等,或未搞清默认 salvage 在无 SSH 时可能中止:不要把自动 manager 直接挂生产。

  • 需要同时跑多个 manager「互备」:当前默认 lease 做不到,别靠它防脑裂。

常见面试题(收口)

  • 原版 MHA 停更后,社区 Go 重写版支持 8.4.x / 9.7不支持 5.7、8.0、9.6,且只认 GTID。

  • 优点是部署干净、SQL/GTID 驱动、dry-run 与 writer_endpoint 清晰;缺点是项目新、场景窄、Manager 单点、默认 salvage 偏保守、非 TLS 认证有缝。

  • 默认 salvage-if-possible:补不齐就中止;availability-first 才是可用性优先、可能丢未复制事务。

  • MHA-Go 解决传统主从的外部选主;InnoDB Cluster 是另一条产品线,新部署优先后者。

  • Manager 单活;成功 failover 后要先改配置再显式拉起;计划内 switch 前先停 manager。

  • 8.4 非 TLS 切主后可能缺 GET_SOURCE_PUBLIC_KEY,上线前必须验证复制能自动恢复。