MySQL高可用为什么还在反复踩坑?
MySQL高可用方案经历过几代演进:MHA、MMM早已过时, Orchestrator + Consul 是当前社区认可度最高的组合方案。但选对工具只是第一步——故障切换流程怎么设计、复制拓扑怎么配、切换后的数据一致性怎么验证,这些才是真正决定高可用能不能扛住生产事故的环节。
这篇文章从工程化视角,把Orchestrator和Consul集成的部署全流程拆开讲。
方案对比:为什么选Orchestrator
主流MySQL高可用方案横向对比:
| 方案 | 自动切换 | 拓扑感知 | 冗余切换保护 | 社区活跃度 |
|——|———|———|————-|———–|
| MHA | 半自动 | 无 | 无 | 停更 |
| MySQL InnoDB Cluster | 有 | Group Replication | 有 | 活跃 |
| Orchestrator | 有 | 完整拓扑图 | 有 | 活跃 |
Orchestrator的核心优势在于拓扑感知——它持续探测集群中每个节点的复制关系,维护一张实时拓扑图。发生故障时,它基于拓扑图找到最合适的从库提升为新主库(数据最新、复制延迟最小),而不是简单地把第一个从库切上去。
另外,Orchestrator支持恢复钩子——切换前后执行自定义脚本,可以集成Consul做服务发现更新、发告警通知、执行应用层重连逻辑。
Orchestrator部署与配置
Orchestrator的配置文件是JSON格式,以下是生产级配置:
{
"Debug": false,
"ListenAddress": ":3000",
"MySQLTopologyUser": "orchestrator_topo",
"MySQLTopologyPassword": "topo_password",
"MySQLTopologyCredentialsConfigFile": "",
"MySQLTopologySSL": false,
"BackendDB": "mysql",
"MySQLOrchestratorHost": "127.0.0.1",
"MySQLOrchestratorPort": 3306,
"MySQLOrchestratorDatabase": "orchestrator",
"MySQLOrchestratorUser": "orchestrator",
"MySQLOrchestratorPassword": "orch_pass",
"ReplicationCredentials": true,
"DetectSemiSyncMaster": true,
"DetectSemiSyncSlave": true,
"RecoveryPeriodBlockSeconds": 60,
"RecoveryIgnoreHostnamePatterns": [],
"FailMasterPromotionSQL": ["SET GLOBAL read_only=0"],
"PostMasterFailoverProcesses": [
"/usr/local/bin/post_failover.sh {failedHost} {successorHost} {successorIp}"
],
"PostUnsuccessfulFailoverProcesses": [
"/usr/local/bin/failover_failed_alert.sh {failedHost}"
],
"ApplyMySQLPromotionAfterMasterFailover": true,
"MasterFailoverLostInstancesDowntimeMinutes": 10,
"DetachLostSlavesAfterMasterFailover": true,
"CheckReplicationFilters": true,
"CheckReplicationLag": true,
"SkipMaxScaleCheck": true
}
关键配置项解释:
– RecoveryPeriodBlockSeconds: 60:切换后60秒内不再触发新的恢复,防止脑裂场景下反复切换
– DetectSemiSyncMaster/Slave:开启半同步复制检测
– PostMasterFailoverProcesses:切换成功后执行的钩子脚本(后面详细讲)
故障切换三阶段流程
Orchestrator的故障切换分三个阶段:检测、决策、执行。
检测阶段:Orchestrator通过持续的HTTP探测和MySQL连接检查监控主库存活状态。默认连续探测失败3次(间隔1秒)认定主库宕机,触发恢复流程。
决策阶段:从拓扑图中筛选候选从库,按以下优先级排序:
1. 启用了半同步复制的从库(数据更完整)
2. 复制延迟最小的从库(Seconds_Behind_Master)
3. 与主库binlog差异最小的从库
4. 同机房/同可用区的从库
执行阶段:
1. 在候选从库上执行STOP SLAVE; RESET SLAVE ALL;
2. 设置新主库read_only=0
3. 将其余从库CHANGE MASTER TO指向新主库
4. 执行PostMasterFailoverProcesses钩子脚本
Consul集成:让应用无感切换
主库切换后,应用需要知道新主库的地址。通过Consul的服务发现机制,应用不再直连MySQL IP,而是连接Consul的Service Name(如 mysql-master.service.consul),DNS解析到当前主库IP。
切换钩子脚本负责更新Consul中的服务注册:
#!/bin/bash
# /usr/local/bin/post_failover.sh
# Orchestrator主库切换后的Consul注册更新
# 参数: failedHost successorHost successorIp
FAILED_HOST=$1
SUCCESSOR_HOST=$2
SUCCESSOR_IP=$3
CONSUL_ADDR="127.0.0.1:8500"
SERVICE_NAME="mysql-master"
echo "[$(date)] 故障切换: ${FAILED_HOST} -> ${SUCCESSOR_HOST} (${SUCCESSOR_IP})"
# 1. 注销旧主库的Consul服务注册
curl -s --request PUT "http://${CONSUL_ADDR}/v1/agent/service/deregister/${SERVICE_NAME}-${FAILED_HOST}"
# 2. 注册新主库
cat > /tmp/consul_mysql.json <<EOF
{
"ID": "${SERVICE_NAME}-${SUCCESSOR_HOST}",
"Name": "${SERVICE_NAME}",
"Address": "${SUCCESSOR_IP}",
"Port": 3306,
"Tags": ["master", "mysql", "writable"],
"Check": {
"Interval": "5s",
"Timeout": "3s",
"TCP": "${SUCCESSOR_IP}:3306"
}
}
EOF
curl -s --request PUT "http://${CONSUL_ADDR}/v1/agent/service/register" -d @/tmp/consul_mysql.json
# 3. 发送告警
curl -s -X POST "https://hooks.example.com/alert" \
-H "Content-Type: application/json" \
-d "{\"text\": \"MySQL主库切换完成: ${FAILED_HOST} -> ${SUCCESSOR_HOST}\"}"
echo "Consul注册更新完成"
应用侧连接配置示例(Spring Boot):
spring:
datasource:
url: jdbc:mysql://mysql-master.service.consul:3306/app_db?useSSL=false
# Consul DNS会解析到当前主库IP,切换后自动生效
hikari:
maximum-pool-size: 20
connection-timeout: 3000
# 连接验证+快速失败,切换后秒级恢复
validation-timeout: 1000
半同步复制与GTID配置
异步复制存在主库宕机后从库丢数据的风险,半同步复制要求至少一个从库确认收到binlog后主库才commit,大幅降低数据丢失概率。
my.cnf关键配置:
# my.cnf — 主库配置
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
gtid-mode = ON
enforce-gtid-consistency = ON
# 半同步复制
plugin-load = "rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_timeout = 3000
rpl_semi_sync_master_wait_for_slave_count = 1
# 复制稳定性
sync-binlog = 1
innodb-flush-log-at-trx-commit = 1
binlog-checksum = CRC32
# my.cnf — 从库配置
[mysqld]
server-id = 2
log-bin = mysql-bin
binlog-format = ROW
gtid-mode = ON
enforce-gtid-consistency = ON
relay-log = relay-bin
log-slave-updates = ON
read-only = ON
# 半同步从库
plugin-load = "rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled = 1
rpl_semi_sync_master_timeout = 3000 意味着主库等待3秒超时后降级为异步复制——这是在数据安全性和可用性之间的权衡,3秒足够覆盖大多数网络抖动场景。
备份恢复验证:切换后的最后防线
高可用架构里的备份恢复经常被忽视——切换成功不代表数据完整,需要定期验证备份可恢复性。
xtrabackup全量+增量备份脚本:
#!/bin/bash
# MySQL xtrabackup备份脚本
# 用法: backup_mysql.sh full|incr
# 周日全量,周一至周六增量
BACKUP_DIR="/data/mysql-backup"
DATE=$(date +%Y%m%d)
DAY_OF_WEEK=$(date +%u) # 1=周一, 7=周日
MYSQL_USER="backup_user"
MYSQL_PASS="backup_pass"
MYSQL_CMD="xtrabackup --user=${MYSQL_USER} --password=${MYSQL_PASS}"
mkdir -p ${BACKUP_DIR}/${DATE}
if [ "$1" = "full" ] || [ ${DAY_OF_WEEK} -eq 7 ]; then
echo "[$(date)] 执行全量备份..."
${MYSQL_CMD} --backup --target-dir=${BACKUP_DIR}/${DATE}/full \
--parallel=4 --compress
# 记录全量备份路径供增量使用
echo "${BACKUP_DIR}/${DATE}/full" > ${BACKUP_DIR}/.last_full
else
LAST_FULL=$(cat ${BACKUP_DIR}/.last_full 2>/dev/null)
if [ -z "${LAST_FULL}" ]; then
echo "[ERROR] 未找到全量备份记录,先执行全量备份"
exit 1
fi
echo "[$(date)] 执行增量备份,基于: ${LAST_FULL}"
${MYSQL_CMD} --backup --target-dir=${BACKUP_DIR}/${DATE}/incr \
--incremental-basedir=${LAST_FULL} --parallel=4 --compress
fi
# 清理7天前的备份
find ${BACKUP_DIR} -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
echo "[$(date)] 备份完成"
恢复验证建议每周在隔离环境执行一次:把全量+增量备份恢复到测试库,运行 mysqlcheck --all-databases --check 验证表完整性,用 pt-table-checksum 对比生产与恢复数据的一致性。备份不验证等于没备份。
Orchestrator+Consul这套组合在生产环境中已被大量验证,核心是三个环节:拓扑感知选主准确、Consul服务发现让应用无感切换、半同步复制+GTID保障数据完整性。把切换钩子脚本、Consul注册更新和备份验证流程都跑通,高可用才能真正扛住故障。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-gao-ke-yong-jia-gou-yu-zi-dong-gu-zhang-qie-huan-shi/