11 KiB
tags, create time, status
| tags | create time | status | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
2026-05-16 00:00 | draft |
InnoDB Cluster
概述
InnoDB Cluster 是 MySQL 官方的高可用(HA)集群解决方案,它将 Group Replication(GR)底层协议封装成一个开箱即用的平台。
[!TIP] 为什么需要 InnoDB Cluster? Group Replication 虽然可靠,但手动配置极其繁琐:需要处理 SSL、参数调优、选举策略等。InnoDB Cluster 把 GR 的复杂性隐藏起来,提供一套标准化的 API(MySQL Shell)来完成部署、监控和运维,让开发者聚焦应用层而非基础设施。
它由三个组件构成:
| 组件 | 角色 | 运行时必需? |
|---|---|---|
| MySQL Shell | 管理工具,用于创建和维护集群 | ❌ |
| MySQL Router | 客户端代理,智能路由读写请求 | ✅ |
| Cluster Monitor | 内置监控器,嵌入在每个 MySQL 实例中 | ✅ |
核心能力包括:自动故障转移、多主/单写拓扑、数据一致性保证以及无缝连接。
架构总览
flowchart TB
subgraph "应用层"
App1["App 1"]
App2["App 2"]
end
Router["MySQL Router<br/>端口 6446(RW) / 6447(RO)"]
subgraph "InnoDB Cluster"
ICM["InnoDB Cluster Monitor<br/>MySQL Shell 守护进程"]
subgraph "Group Replication Set"
Node1["Instance 1<br/>Primary (RW)"]
Node2["Instance 2<br/>Secondary (RO)"]
Node3["Instance 3<br/>Secondary (RO)"]
end
end
subgraph "Metadata Store"
Metadb["metadata_db<br/>元数据存储实例"]
end
App1 --> Router
App2 --> Router
Router -->|"6446 RW"| Node1
Router -->|"6447 RO"| Node2
Router -->|"6447 RO"| Node3
Node1 -.Heartbeat.-> ICM
Node2 -.Heartbeat.-> ICM
Node3 -.Heartbeat.-> ICM
ICM --> Metadb
[!QUESTION] 思考:为什么 Router 需要两个端口? 写入必须到达唯一的 Primary 以保证数据一致性,读取则可以分发到任意 Secondary 节点提升并发能力。Router 通过两个端口天然实现了读写分离,应用层无需感知集群拓扑变化。
快速搭建
前置条件
# 准备 3 台服务器(或容器),每台安装 MySQL 8.0+
# 确保 mysql-shell 和 mysql-router 也已安装
# 最小配置(单机测试可共用一台机器不同端口)
for port in 33061 33062 33063; do
mysqld --defaults-file=/etc/mysql/my-${port}.cnf &
done
使用 MySQL Shell 一键部署
// 连接到第一个实例
dba.connect('root@localhost:33061')
// 检查实例是否符合集群要求
dba.checkInstanceConfiguration('root@localhost:33061')
// 配置实例(如果需要)
dba.configureInstance('root@localhost:33061')
// 创建集群
cluster = dba.createCluster('myCluster')
// 添加第二、第三个实例
cluster.addInstance('root@localhost:33062')
cluster.addInstance('root@localhost:33063')
// 查看集群状态
cluster.status()
输出示例:
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "localhost:33061",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"localhost:33061": {
"role": "PRIMARY",
"mode": "R/W",
"status": "ONLINE"
},
"localhost:33062": {
"role": "SECONDARY",
"mode": "R/O",
"status": "ONLINE"
},
"localhost:33063": {
"role": "SECONDARY",
"mode": "R/O",
"status": "ONLINE"
}
}
}
}
配置 MySQL Router
# 自动生成路由配置
mysqlrouter --bootstrap root@localhost:33061 --directory ./router --force
# 启动 Router
cd ./router && ./start.sh
# Router 自动暴露:
# - localhost:6446 → 写操作 → 转发到 Primary
# - localhost:6447 → 读操作 → 轮询到所有 Secondary
高可用与故障转移
InnoDB Cluster 的自动故障转移基于 Group Replication 的 Paxos 式选举机制。当当前 Primary 不可达时:
sequenceDiagram
participant App as 应用
participant Router as MySQL Router
participant P as Primary (A)
participant S1 as Secondary (B)
participant S2 as Secondary (C)
App->>Router: SELECT / INSERT
Router->>P: 转发写请求
P-->>S1: binlog / GTID 复制
P-->>S2: binlog / GTID 复制
Note over P: Primary 宕机
P--x Router: 连接断开
Router--x App: 返回 Error
S1->>S2: 心跳超时检测
S2->>S1: 无法联系 Primary
S1->>S1: 触发选举(GCS view change)
S1->>S2: 投票选举新 Primary
S2->>S1: 确认(B 票数最高)
B->>B: 成为新的 Primary
Note over Router: 监控器检测到拓扑变化
Router->>S1: 更新路由表
App->>Router: 重试写入
Router->>S1: 转发到新 Primary
[!NOTE] 故障转移关键参数
group_repification_member_weight(默认 50):影响选举权重,范围 0~100,值越高越优先成为 Primarygroup_repification_single_primary_mode:开启单写模式后,同一时刻只有一个可写节点- RPO = 0:在同步模式下,已提交的事务不会丢失;异步模式下可能存在少量数据损失
容忍节点数
| 节点总数 | 可容忍故障数 | 需在线最低数 |
|---|---|---|
| 3 节点 | 1 | 2 |
| 5 节点 | 2 | 3 |
| 7 节点 | 3 | 4 |
[!WARNING] 脑裂风险 当网络分区导致集群分裂成两组时,每组都认为自己是合法的。InnoDB Cluster 通过 "多数派原则"(Quorum) 解决——只有拥有多数节点的分区才能继续提供服务,其余进入只读模式。这就是为什么推荐部署奇数个节点:避免平分局面。
日常运维操作
以下操作均在 MySQL Shell 中执行(JavaScript 模式)。
查看集群状态
// 连接到集群中的任意实例即可
dba.connect('root@localhost:33061')
var cluster = dba.getCluster()
cluster.status() // 输出完整的拓扑和每个节点的健康信息
添加 / 移除节点
const cluster = dba.getCluster()
// 添加新节点
cluster.addInstance('root@localhost:33064')
// 安全移除节点(会先同步数据)
cluster.removeInstance('root@localhost:33062', {'safeClone': true})
// 注意:如果节点已宕机且无法恢复,使用 force 模式:
// cluster.removeInstance('root@localhost:33062', {'force': true})
切换主节点
// 手动将某个 Secondary 提升为 Primary
cluster.switchToMultiPrimaryMode() // 切换到多主模式
cluster.switchToSinglePrimaryMode() // 切回单主模式
// 在单主模式下指定哪个节点作为 Primary
cluster.setPrimaryInstance('root@localhost:33062')
升级集群
// 滚动升级(逐个节点升级,不停服)
cluster.checkInstanceUpgrade() // 检查各节点是否满足升级条件
cluster.checkStackUpgrade() // 整体栈升级评估
生产注意事项
必备前置步骤
# 每个 MySQL 实例必须配置的关键参数(my.cnf)
[mysqld]
# Group Replication 基础配置
server-id=1 # 全局唯一
gtid_mode=ON # 必须开启 GTID
enforce_gtid_consistency=ON # 必须启用
binlog_checksum=CRC32 # 增强数据完整性
transaction_write_set_extraction=XXHASH64 # Router 发现读写集用
loose-group_replication_bootstrap_group=OFF
[!TIP] 单机多实例测试技巧 开发阶段可以用一台机器跑三个实例,只需:
- 每个实例独立的数据目录、端口、socket 文件
- 独立的
.cnf配置文件group_replication_ip_whitelist="127.0.0.1"允许本地通信
常见陷阱
| 问题 | 原因 | 解决方案 |
|---|---|---|
节点加入后变为 ERROR |
server_uuid 冲突或 UUID 未生成 | 删除 data/auto.cnf 重启实例 |
Router 报 No connection |
Cluster Monitor 未运行 | 确保实例正常运行,Monitor 是嵌入式的 |
| 写入变慢 | 单写模式下游记事务等待组内确认 | 增加 transaction_alloc_block_size 或使用多主模式 |
| 脑裂后无法恢复 | 少数派分区无法 elect 新 primary | 手动引导:dba.startCluster() 在多数派分区执行 |
应用接入
Go 接入
[!TIP] 连接池配置建议 Go 的
sql.DB需要合理设置连接池参数,避免在故障切换瞬间全部连接失效。
dsn := "app_user:password@tcp(localhost:6446)/mydb?charset=utf8mb4&parseTime=True"
db, err := sql.Open("mysql", dsn)
if err != nil { log.Fatal(err) }
// 故障切换时的关键配置
db.SetMaxIdleConns(10) // 保持足够空闲连接快速恢复
db.SetConnMaxLifetime(5 * time.Minute) // 定期丢弃旧连接,避免持有死连接
db.SetMaxOpenConns(50)
要点解释:
- 应用只需连接 Router 的端口,无需知道后端有多少节点
SetConnMaxLifetime确保经过几次切换后,旧连接自然被回收,新连接会自动指向新 Primary- 建议在应用层配合重试逻辑(如指数退避),因为故障切换期间 Router 可能有几秒的延迟
Java 接入
String url = "jdbc:mysql://localhost:6446/mydb";
Properties props = new Properties();
props.setProperty("user", "app_user");
props.setProperty("password", "password");
props.setProperty("autoReconnect", "true"); // 启用自动重连
props.setProperty("maxReconnects", "5"); // 故障后最多重试 5 次
try (Connection conn = DriverManager.getConnection(url, props)) {
// 写操作走 6446,读操作走 6447
}
InnoDB Cluster vs 自建 GR
| 特性 | InnoDB Cluster | 自建 GR |
|---|---|---|
| 部署复杂度 | 低(Shell 自动化) | 高(手动配置每个节点) |
| Router 集成 | 内置 MySQL Router | 需自行搭配 ProxySQL 等 |
| 监控界面 | shell cluster.status() |
需查 performance_schema |
| 升级维护 | dba.patchInstance() |
手动逐个升级 |
| 灵活性 | 较低(Oracle 锁定) | 高(自定义参数) |
| 适用场景 | 快速落地、中小型团队 | 深度定制、大规模生产 |
关联笔记
- hhs/MySQL/07-高可用与分布式/32-Group Replication — InnoDB Cluster 底层就是 GR
- hhs/MySQL/07-高可用与分布式/31-MHA与Orchestrator — 不同 HA 方案的对比
- hhs/GORM/13-多数据库支持 — GORM 在集群环境中的使用注意事项