MySQL教程 / 第 240 节

第24章:高可用方案

高可用是保证业务连续性的关键

24.1 高可用概述

24.1.1 什么是高可用?

定义:

  • High Availability(HA)
  • 系统能够持续运行,减少停机时间
  • 目标:99.9%、99.99%、99.999%可用性

可用性计算:

可用性 = (总时间 - 停机时间) / 总时间 × 100%

99.9%   → 年停机时间:8.76小时
99.99%  → 年停机时间:52.56分钟
99.999% → 年停机时间:5.26分钟

24.1.2 为什么需要高可用?

问题场景:

  • ❌ 主库故障,业务中断
  • ❌ 硬件故障,数据丢失
  • ❌ 人为误操作,数据损坏

高可用的目标:

  • ✅ 故障自动检测
  • ✅ 故障自动切换
  • ✅ 数据不丢失
  • ✅ 业务不中断

24.1.3 高可用方案对比

方案自动切换数据一致性复杂度推荐度
主从复制一般⭐⭐⭐
MHA⭐⭐⭐⭐
MGR很好⭐⭐⭐⭐⭐
Orchestrator⭐⭐⭐⭐
Galera Cluster很好⭐⭐⭐⭐

24.2 MHA(Master High Availability)⭐⭐⭐⭐

24.2.1 MHA概述

什么是MHA?

  • Master High Availability Manager
  • 日本DeNA公司开发
  • 自动主从故障切换工具

MHA架构:

MHA Manager(管理节点)
    ↓ 监控
主库 → 从库1
    → 从库2
    → 从库3

故障切换流程:
1. 检测主库故障
2. 选择新主库
3. 应用差异日志
4. 切换到新主库
5. 配置其他从库

MHA组件:

  • MHA Manager:管理节点,监控和故障切换
  • MHA Node:数据节点,在每个MySQL服务器上安装

24.2.2 MHA安装

环境准备:

# 服务器规划
192.168.1.10  master   主库
192.168.1.11  slave1   从库1(候选主库)
192.168.1.12  slave2   从库2
192.168.1.13  manager  MHA管理节点

# 配置SSH免密登录(所有节点)
ssh-keygen -t rsa
ssh-copy-id root@192.168.1.10
ssh-copy-id root@192.168.1.11
ssh-copy-id root@192.168.1.12
ssh-copy-id root@192.168.1.13

安装MHA Node(所有MySQL节点):

# 安装依赖
yum install perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager

# 下载MHA Node
wget https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58-0.el7.centos.noarch.rpm

# 安装
rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm

# 验证
ls /usr/bin/
# 应该看到:
# save_binary_logs
# apply_diff_relay_logs
# filter_mysqlbinlog
# purge_relay_logs

安装MHA Manager(管理节点):

# 先安装MHA Node
rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm

# 下载MHA Manager
wget https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-manager-0.58-0.el7.centos.noarch.rpm

# 安装
rpm -ivh mha4mysql-manager-0.58-0.el7.centos.noarch.rpm

# 验证
ls /usr/bin/
# 应该看到:
# masterha_check_ssh
# masterha_check_repl
# masterha_manager
# masterha_master_switch
# masterha_check_status
# masterha_stop

24.2.3 MHA配置

创建MHA配置文件(/etc/mha/app1.cnf):

[server default]
# MHA Manager工作目录
manager_workdir=/var/log/mha/app1
# MHA Manager日志
manager_log=/var/log/mha/app1/manager.log

# MySQL用户(用于监控和切换)
user=mha
password=mha_password

# 复制用户
repl_user=repl
repl_password=repl_password

# SSH用户
ssh_user=root

# 监控间隔(秒)
ping_interval=3

# 主库故障后,从库等待时间
master_ip_failover_script=/usr/local/bin/master_ip_failover
# VIP切换脚本

# 发送报告
report_script=/usr/local/bin/send_report

# 二次检查
secondary_check_script=/usr/local/bin/masterha_secondary_check -s 192.168.1.11 -s 192.168.1.12

# 主库
[server1]
hostname=192.168.1.10
port=3306
# 候选主库优先级(数字越小优先级越高)
candidate_master=1

# 从库1(候选主库)
[server2]
hostname=192.168.1.11
port=3306
candidate_master=1

# 从库2
[server3]
hostname=192.168.1.12
port=3306
# 不作为候选主库
no_master=1

创建MySQL监控用户:

-- 在所有MySQL节点上执行
CREATE USER 'mha'@'%' IDENTIFIED BY 'mha_password';
GRANT ALL PRIVILEGES ON *.* TO 'mha'@'%';
FLUSH PRIVILEGES;

创建VIP切换脚本(/usr/local/bin/master_ip_failover):

#!/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
);

my $vip = '192.168.1.100/24';
my $key = '1';
my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig eth0:$key down";

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 {
    print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";

    if ( $command eq "stop" || $command eq "stopssh" ) {
        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" ) {
        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";
        exit 0;
    }
    else {
        &usage();
        exit 1;
    }
}

sub start_vip() {
    `ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}

sub stop_vip() {
    return 0  unless  ($ssh_user);
    `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

24.2.4 MHA使用

检查SSH连接:

masterha_check_ssh --conf=/etc/mha/app1.cnf

# 输出:
# All SSH connection tests passed successfully.

检查复制状态:

masterha_check_repl --conf=/etc/mha/app1.cnf

# 输出:
# MySQL Replication Health is OK.

启动MHA Manager:

# 前台启动(测试用)
masterha_manager --conf=/etc/mha/app1.cnf

# 后台启动
nohup masterha_manager --conf=/etc/mha/app1.cnf > /var/log/mha/app1/manager.log 2>&1 &

# 查看状态
masterha_check_status --conf=/etc/mha/app1.cnf

# 输出:
# app1 (pid:12345) is running(0:PING_OK), master:192.168.1.10

手动故障切换:

# 在线切换(主库正常)
masterha_master_switch --conf=/etc/mha/app1.cnf --master_state=alive \
  --new_master_host=192.168.1.11 --new_master_port=3306 \
  --orig_master_is_new_slave --running_updates_limit=10000

# 故障切换(主库故障)
masterha_master_switch --conf=/etc/mha/app1.cnf --master_state=dead \
  --dead_master_host=192.168.1.10 --dead_master_port=3306 \
  --new_master_host=192.168.1.11 --new_master_port=3306

停止MHA Manager:

masterha_stop --conf=/etc/mha/app1.cnf

24.3 MGR(MySQL Group Replication)⭐⭐⭐⭐⭐

24.3.1 MGR概述

什么是MGR?

  • MySQL Group Replication
  • MySQL 5.7.17+官方提供
  • 基于Paxos协议的多主复制
  • 自动故障检测和切换

MGR特点:

  • ✅ 自动故障检测
  • ✅ 自动故障切换
  • ✅ 数据强一致性
  • ✅ 支持单主和多主模式
  • ✅ 官方支持

MGR架构:

单主模式(推荐):
节点1(主)← 写
节点2(从)← 读
节点3(从)← 读

多主模式:
节点1(主)← 读写
节点2(主)← 读写
节点3(主)← 读写

24.3.2 MGR安装配置

环境准备:

# 服务器规划
192.168.1.10  node1  主节点
192.168.1.11  node2  从节点
192.168.1.12  node3  从节点

# MySQL版本要求:5.7.17+或8.0+

配置节点1(/etc/my.cnf):

[mysqld]
# 基本配置
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_format = ROW
log_bin = mysql-bin
log_slave_updates = ON
binlog_checksum = NONE

# MGR配置
plugin_load_add = 'group_replication.so'
transaction_write_set_extraction = XXHASH64

# 组复制配置
loose-group_replication_group_name = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
loose-group_replication_start_on_boot = OFF
loose-group_replication_local_address = "192.168.1.10:33061"
loose-group_replication_group_seeds = "192.168.1.10:33061,192.168.1.11:33061,192.168.1.12:33061"
loose-group_replication_bootstrap_group = OFF

# 单主模式
loose-group_replication_single_primary_mode = ON
loose-group_replication_enforce_update_everywhere_checks = OFF

# 多主模式(可选)
# loose-group_replication_single_primary_mode = OFF
# loose-group_replication_enforce_update_everywhere_checks = ON

# IP白名单
loose-group_replication_ip_whitelist = "192.168.1.0/24"

配置节点2和节点3:

# 只需修改以下参数
server_id = 2  # 节点2
server_id = 3  # 节点3

loose-group_replication_local_address = "192.168.1.11:33061"  # 节点2
loose-group_replication_local_address = "192.168.1.12:33061"  # 节点3

创建复制用户(所有节点):

-- 创建复制用户
SET SQL_LOG_BIN=0;
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl_password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;

-- 配置复制通道
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='repl_password'
FOR CHANNEL 'group_replication_recovery';

启动MGR(节点1):

-- 安装插件
INSTALL PLUGIN group_replication SONAME 'group_replication.so';

-- 启动组复制(第一个节点需要bootstrap)
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

-- 查看状态
SELECT * FROM performance_schema.replication_group_members;

启动MGR(节点2和节点3):

-- 安装插件
INSTALL PLUGIN group_replication SONAME 'group_replication.so';

-- 启动组复制
START GROUP_REPLICATION;

-- 查看状态
SELECT * FROM performance_schema.replication_group_members;

24.3.3 MGR监控

查看组成员:

SELECT * FROM performance_schema.replication_group_members;

-- 输出:
-- CHANNEL_NAME              MEMBER_ID                            MEMBER_HOST  MEMBER_PORT  MEMBER_STATE
-- group_replication_applier aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 192.168.1.10 3306         ONLINE
-- group_replication_applier bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb 192.168.1.11 3306         ONLINE
-- group_replication_applier cccccccc-cccc-cccc-cccc-cccccccccccc 192.168.1.12 3306         ONLINE

查看主节点:

-- 单主模式下查看主节点
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;

-- MEMBER_ROLE = PRIMARY:主节点
-- MEMBER_ROLE = SECONDARY:从节点

查看复制状态:

SHOW STATUS LIKE 'group_replication%';

24.3.4 MGR故障切换

自动故障切换:

# 模拟主节点故障
# 在节点1上执行
systemctl stop mysqld

# 在节点2或节点3上查看
SELECT * FROM performance_schema.replication_group_members;

# 结果:
# 节点1状态变为UNREACHABLE或消失
# 自动选举新主节点(节点2或节点3)

手动切换主节点:

-- 在当前主节点上执行
SELECT group_replication_set_as_primary('新主节点的MEMBER_ID');

-- 示例
SELECT group_replication_set_as_primary('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb');

24.4 Orchestrator ⭐⭐⭐⭐

24.4.1 Orchestrator概述

什么是Orchestrator?

  • GitHub开源的MySQL高可用和复制拓扑管理工具
  • 自动故障检测和切换
  • Web界面管理
  • 支持复杂的复制拓扑

Orchestrator特点:

  • ✅ 自动发现复制拓扑
  • ✅ 自动故障检测和恢复
  • ✅ Web界面可视化
  • ✅ 支持手动和自动切换
  • ✅ 支持复杂拓扑

24.4.2 Orchestrator安装

# 下载
wget https://github.com/openark/orchestrator/releases/download/v3.2.6/orchestrator-3.2.6-1.x86_64.rpm

# 安装
rpm -ivh orchestrator-3.2.6-1.x86_64.rpm

# 配置文件
vi /etc/orchestrator.conf.json

# 启动
systemctl start orchestrator
systemctl enable orchestrator

# 访问Web界面
http://192.168.1.13:3000

24.4.3 Orchestrator配置

{
  "Debug": false,
  "ListenAddress": ":3000",
  "MySQLTopologyUser": "orchestrator",
  "MySQLTopologyPassword": "orchestrator_password",
  "MySQLOrchestratorHost": "127.0.0.1",
  "MySQLOrchestratorPort": 3306,
  "MySQLOrchestratorDatabase": "orchestrator",
  "MySQLOrchestratorUser": "orchestrator",
  "MySQLOrchestratorPassword": "orchestrator_password",
  "RecoveryPeriodBlockSeconds": 3600,
  "RecoveryIgnoreHostnameFilters": [],
  "AutoFailoverEnabled": true,
  "FailureDetectionPeriodBlockMinutes": 60
}

24.5 高可用方案选择

24.5.1 方案对比

特性MHAMGROrchestrator
自动切换
数据一致性很好
切换速度30-60秒10-30秒30-60秒
复杂度
官方支持
社区活跃度
适用场景中小型中大型中大型

24.5.2 选择建议

小型项目(<10台服务器):

  • 推荐:主从复制 + MHA
  • 理由:简单、成本低

中型项目(10-50台服务器):

  • 推荐:MGR单主模式
  • 理由:自动切换、数据一致性好

大型项目(>50台服务器):

  • 推荐:MGR + ProxySQL + 分库分表
  • 理由:高可用、高性能、可扩展

对数据一致性要求极高:

  • 推荐:MGR
  • 理由:基于Paxos协议,强一致性

对切换速度要求高:

  • 推荐:MGR
  • 理由:切换速度快(10-30秒)

24.6 高可用最佳实践

24.6.1 设计原则

1. 至少3个节点
   - 避免脑裂
   - 提高可用性

2. 跨机房部署
   - 避免单点故障
   - 提高容灾能力

3. 监控告警
   - 实时监控复制状态
   - 故障及时告警

4. 定期演练
   - 定期进行故障切换演练
   - 验证高可用方案

5. 数据备份
   - 高可用不等于备份
   - 定期备份数据

6. 文档化
   - 记录架构设计
   - 记录操作流程

24.6.2 常见问题

问题1:脑裂

场景:网络分区导致多个主库

解决方案:
1. 使用奇数个节点(3、5、7)
2. 使用仲裁节点
3. 使用Paxos/Raft协议(MGR)

问题2:数据丢失

场景:主库故障,binlog未同步到从库

解决方案:
1. 使用半同步复制
2. 使用MGR
3. 设置sync_binlog=1

问题3:切换时间长

场景:故障切换需要几分钟

解决方案:
1. 使用MGR(切换快)
2. 优化MHA配置
3. 减少binlog大小

问题4:从库延迟

场景:从库延迟导致切换后数据不一致

解决方案:
1. 监控主从延迟
2. 使用并行复制
3. 优化从库性能

24.7 本章总结

本章学习内容:

  • ✅ 高可用概述
  • MHA方案(安装、配置、使用)⭐⭐⭐⭐
  • MGR方案(安装、配置、监控、故障切换)⭐⭐⭐⭐⭐
  • ✅ Orchestrator方案
  • ✅ 高可用方案选择
  • ✅ 高可用最佳实践

重点掌握:

  1. 高可用目标:故障自动检测和切换
  2. MHA:自动主从故障切换
  3. MGR:基于Paxos的多主复制
  4. 至少3个节点避免脑裂
  5. 定期演练故障切换
  6. 高可用不等于备份

方案对比:

  • MHA:成熟稳定,适合中小型
  • MGR:官方支持,数据一致性好,适合中大型
  • Orchestrator:可视化管理,适合复杂拓扑

MGR优势:

  • 自动故障检测和切换
  • 数据强一致性(Paxos)
  • 切换速度快(10-30秒)
  • 官方支持

最佳实践:

  • 至少3个节点
  • 跨机房部署
  • 监控告警
  • 定期演练
  • 数据备份

面试重点:

  • 高可用方案有哪些
  • MHA和MGR的区别
  • 如何避免脑裂
  • 如何保证数据不丢失
  • 故障切换流程

下一章预告: 分库分表


练习题

  1. 什么是高可用?99.99%可用性对应多少停机时间?
  2. MHA的工作原理是什么?
  3. MGR和MHA有什么区别?
  4. 什么是脑裂?如何避免?
  5. MGR的单主模式和多主模式有什么区别?
  6. 如何配置MGR?
  7. 如何监控MGR状态?
  8. 高可用方案如何选择?
  9. 高可用和备份有什么区别?
  10. 设计一个高可用方案

继续学习: 第25章:分库分表