高可用架构
一、读写分离
1.1 读写分离架构
应用
│
┌────┴────┐
│ 负载均衡 │
└────┬────┘
│
┌───────┴────────┐
│ │
写请求 读请求
│ │
▼ ▼
主库(Master) 从库(Slave)
│
┌───────┴───────┐
│ │
从库1 从库2
1.2 实现方案
sql
-- 方案1:应用层实现(配置多数据源)
-- 在代码中区分读写操作,写操作走主库,读操作走从库
-- 方案2:使用中间件
-- MySQL Router(官方)
mysql-router --bootstrap master:3306 --user mysql
-- 自动路由读写请求
-- ProxySQL(推荐)
-- 配置 ProxySQL
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES
(0, '192.168.1.100', 3306), -- 主库(写组)
(1, '192.168.1.101', 3306), -- 从库1(读组)
(1, '192.168.1.102', 3306); -- 从库2(读组)
-- 配置读写分离规则
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup) VALUES
(1, 1, '^SELECT', 1), -- SELECT 语句走读组
(2, 1, '^INSERT|UPDATE|DELETE', 0); -- 写语句走写组
-- 方案3:客户端组件
-- ShardingSphere-JDBC:在 JDBC 层实现
-- 配置见主从复制章节
二、高可用方案
2.1 MHA(Master High Availability)
MHA 是目前比较成熟的主从高可用方案,能在 30 秒内完成主库故障切换。
架构:
Manager 节点(监控)
│
┌────┴────┐
│ │
主库 从库1(备用主库)
│
从库2
MHA 工作原理:
故障检测 → 选举新主库 → 补齐 relay log → 提升为主库 → 切换 VIP/配置
bash
# MHA 配置示例 (/etc/mha/app1.cnf)
[server default]
manager_log=/var/log/mha/manager.log
manager_workdir=/var/log/mha
user=root
password=xxx
ssh_user=root
repl_user=repl
repl_password=xxx
[server1]
hostname=192.168.1.100
master_binlog_dir=/var/log/mysql
[server2]
hostname=192.168.1.101
candidate_master=1 # 设置为候选主库
[server3]
hostname=192.168.1.102
no_master=1 # 不作为候选主库
# 启动 MHA Manager
masterha_manager --conf=/etc/mha/app1.cnf
MHA 故障切换流程:
1. Manager 检测到主库不可用(ping/连接超时)
2. 尝试 SSH 连接主库确认
3. 从所有从库中选一个数据最新的作为新主库
4. 从其他从库补齐该从库缺失的 relay log
5. 提升该从库为新主库
6. 将其他从库指向新主库
7. 移除旧主库 VIP(如果有),绑定到新主库
8. 发送告警通知
2.2 Keepalived + 主从切换
bash
# 主库 VIP 配置(/etc/keepalived/keepalived.conf)
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.200/24 # VIP
}
# MySQL 健康检查
track_script {
chk_mysql
}
}
# 健康检查脚本
vrrp_script chk_mysql {
script "/etc/keepalived/check_mysql.sh"
interval 2
weight -20
}
bash
#!/bin/bash
# /etc/keepalived/check_mysql.sh
# MySQL 健康检查
mysql -u root -p'xxx' -e "SELECT 1" > /dev/null 2>&1
if [ $? -ne 0 ]; then
# MySQL 挂了,尝试重启
systemctl restart mysql
sleep 5
# 重启失败,让 Keepalived 降权触发切换
mysql -u root -p'xxx' -e "SELECT 1" > /dev/null 2>&1
if [ $? -ne 0 ]; then
exit 1
fi
fi
exit 0
2.3 MySQL InnoDB Cluster(官方方案)
MySQL 5.7+ 提供的官方高可用方案,基于 Group Replication。
架构:
MySQL Router
│
┌────┼────┐
│ │ │
│ │ │
M1──M2──M3(Group Replication)
(Primary) (Secondary) (Secondary)
sql
-- 配置 Group Replication
-- 每个节点的配置文件
[mysqld]
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_format = ROW
log_slave_updates = ON
transaction_write_set_extraction = XXHASH64
loose-group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
loose-group_replication_start_on_boot = OFF
loose-group_replication_local_address = "192.168.1.100:33061"
loose-group_replication_group_seeds = "192.168.1.100:33061,192.168.1.101:33061,192.168.1.102:33061"
loose-group_replication_bootstrap_group = OFF
loose-group_replication_single_primary_mode = ON -- 单主模式
-- 创建复制用户
SET SQL_LOG_BIN=0;
CREATE USER 'repl'@'%' IDENTIFIED BY 'xxx';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
-- 引导第一个节点
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;
-- 其他节点加入
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='xxx'
FOR CHANNEL 'group_replication_recovery';
START GROUP_REPLICATION;
2.4 高可用方案对比
| 方案 | 切换时间 | 优点 | 缺点 |
|---|---|---|---|
| MHA | 10~30秒 | 成熟稳定、MySQL 原生兼容 | 需额外部署、有数据丢失风险 |
| Keepalived | 5~30秒 | 简单、VIP 切换快 | 需要脚本配合、无数据一致性保证 |
| InnoDB Cluster | 自动切换 | 官方方案、强一致性 | 复杂度高、版本限制 |
| Orchestrator | 秒级 | 自动管理、Web 界面 | 需 Raft 集群 |
| ProxySQL + MHA | 秒级 | 对应用透明 | 维护成本高 |
三、故障切换与容灾设计
3.1 主库故障切换流程
sql
-- 手动切换步骤
-- 步骤1:确认主库不可用
-- 通过监控系统或手动登录确认
-- 步骤2:从从库中选择新主库
-- 选数据最新的从库
SHOW SLAVE STATUS\G
-- 比较各从库的 Master_Log_File 和 Read_Master_Log_Pos
-- 最好的候选从库:
-- 数据最新(binlog 位置最大)
-- 硬件配置最好
-- 网络延迟最小
-- 步骤3:提升候选从库为新主库
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;
-- 步骤4:切换其他从库到新主库
CHANGE MASTER TO
MASTER_HOST = '新主库IP',
MASTER_USER = 'repl',
MASTER_PASSWORD = 'xxx',
MASTER_LOG_FILE = 'mysql-bin.00000X',
MASTER_LOG_POS = YYYYY;
START SLAVE;
-- 步骤5:更新应用连接配置
-- 修改应用数据源,指向新主库
3.2 容灾设计
同城双活:
机房A(主)←→ 机房B(备)
距离:< 50km
延迟:< 5ms
两地三中心:
城市A:主机房
城市A:同城备份机房
城市B:异地灾备机房(异步复制)
容灾策略:
| 策略 | RPO | RTO | 成本 |
|---|---|---|---|
| 同城双活 | < 1秒 | 秒级 | 高 |
| 异地异步备份 | 分钟级 | 小时级 | 中 |
| 两地三中心 | 秒级 | 分钟级 | 极高 |
| 云平台跨可用区 | < 1秒 | 自动化 | 按需 |
四、实战案例
案例1:ProxySQL + MHA 高可用架构
bash
# ProxySQL 配置
# 1. 添加 MySQL 服务器
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES
(0, '192.168.1.100', 3306), # 写组
(0, '192.168.1.101', 3306), # 写组(备用)
(1, '192.168.1.101', 3306), # 读组
(1, '192.168.1.102', 3306); # 读组
# 2. 配置监控用户
UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_password';
# 3. 配置读写分离规则
INSERT INTO mysql_query_rules
(rule_id,active,match_pattern,destination_hostgroup,apply) VALUES
(1,1,'^SELECT.*FOR UPDATE',0,1), # SELECT FOR UPDATE 走写
(2,1,'^SELECT',1,1), # SELECT 走读
(3,1,'^INSERT|^UPDATE|^DELETE',0,1); # 写语句走写
# 4. MHA 切换后自动更新 ProxySQL
# MHA 切换脚本中调用 ProxySQL 管理接口
# mysql -h127.0.0.1 -P6032 -uadmin -padmin -e "
# DELETE FROM mysql_servers WHERE hostgroup_id = 0 AND hostname = '旧主库IP';
# INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (0, '新主库IP', 3306);
# LOAD MYSQL SERVERS TO RUNTIME;
# "
案例2:数据库故障演练
sql
-- 故障演练流程
-- 场景1:模拟主库宕机
-- 1. 停止主库 MySQL
systemctl stop mysql
-- 2. 观察 MHA/Keepalived 是否自动切换
-- 3. 验证读写是否正常
-- 4. 记录切换时间
-- 场景2:模拟网络分区
-- 1. 主库网络断开
iptables -A INPUT -s 从库IP -j DROP
-- 2. 观察复制状态
-- 3. 恢复网络后验证数据一致性
-- 场景3:模拟从库延迟
-- 1. 从库上执行大查询
SELECT *, SLEEP(1) FROM big_table;
-- 2. 观察延迟监控
-- 3. 检查读请求是否切换到其他从库
-- 演练结果记录
-- 切换时间、数据丢失量、业务影响时长
总结
| 方案 | 适用场景 | 关键点 |
|---|---|---|
| 读写分离 | 读多写少 | 中间件负载均衡、延迟处理 |
| MHA | 主从架构 | 30秒切换、需要 SSH、可能丢数据 |
| Keepalived | 简单主备 | VIP 切换、配合健康检查 |
| InnoDB Cluster | 强一致性 | Group Replication、官方支持 |
| 两地三中心 | 极端容灾 | 异步复制、RPO/RTO 均衡 |
设计原则:
- 没有完美的 HA 方案,选择最适合业务场景的
- 定期进行故障演练,验证 HA 方案的有效性
- 监控和告警比 HA 方案本身更重要
- 做好"最坏情况"预案(全量备份恢复)