vault backup: 2026-05-21 12:20:51

This commit is contained in:
hhs
2026-05-21 12:20:51 +08:00
parent e176563c0b
commit 2e84683d9c
69 changed files with 2008 additions and 6155 deletions
@@ -0,0 +1,291 @@
---
tags: [MySQL, Binary Log, binlog, WAL]
create time: 2026-05-16 00:00
---
# Binary Log(Binlog)
## 概述
Binlog 是 MySQL Server 层生成的**逻辑日志**,记录所有修改数据的 SQL 语句(或行变更)。它是主从复制、时间点恢复和增量备份的基础。
### 知识地图
```mermaid
flowchart LR
A["Binary Log"] --> B["主从复制<br/>从库通过 relay log 重放 binlog"]
A --> C["时间点恢复 PITR<br/>恢复到任意精确时刻"]
A --> D["增量备份<br/>基线备份 + binlog 增量"]
A --> E["审计分析<br/>追踪数据变更历史"]
A --> F["崩溃一致性<br/>与 Redo Log 2PC 协作"]
style A fill:#6A0DAD,color:#fff
```
> [!TIP] 读这一页之前建议先了解
> 对 Binlog 的理解建立在 InnoDB 存储引擎基础之上。**建议先看**: [[hhs/MySQL/04-存储引擎/19-InnoDB 深度解析]] → 本文档 → [[hhs/MySQL/07-高可用与分布式/30-主从复制详解]]
## Binlog 与 Redo Log 的根本区别
```mermaid
graph LR
subgraph Redo_Log["Redo Log"]
R1["InnoDB Engine 专属"]
R2["物理日志<br/>记录页级修改"]
R3["循环写入<br/>固定大小"]
R4["保证崩溃恢复"]
end
subgraph Binlog["Binlog"]
B1["Server Layer 通用<br/>所有引擎都写"]
B2["逻辑日志<br/>记录 SQL 或行变更"]
B3["追加写入<br/>可无限增长"]
B4["保证主从一致 + PITR"]
end
style R1 fill:#C44569,color:#fff
style B1 fill:#00B6BC,color:#fff
```
### 关键对比
| 特性 | Redo Log | Binlog |
|------|----------|--------|
| **层级** | InnoDB 引擎层 | MySQL Server 层 |
| **范围** | 只有 InnoDB 使用 | 所有引擎都可使用 |
| **内容** | 物理:页偏移+字节修改 | 逻辑:SQL 或行变更 |
| **写入方式** | 循环覆盖 | 追加 append-only |
| **用途** | Crash Recovery | 主从复制、PITR、审计 |
| **2PC 协调** | Phase 1: Redo prepare | Phase 2: Binlog fsync → redo commit |
## Binlog Format 三种模式
### Statement 模式
每条修改数据的 SQL 作为一条 event 写入。主从复制时,从库直接执行这条 SQL:
```
# At 12345
#260516 10:30:00 server id 1 end_log_pos 12378 CRC32 0xabc123
UPDATE orders SET status = 'shipped' WHERE id = 100;
```
> [!QUESTION] Statement 模式简单直观,那为什么还要有其他模式?
>
> 关键问题:**NOW()、RAND()、UUID() 这些非确定性函数**。主库和从库的执行时间不同,结果就不同——这会导致主从数据不一致。例如:`UPDATE scores SET grade = NOW()` 在主库和执行时是 10:30:00,但从库可能在 10:30:01 执行,导致两个库的该记录值不同。
| 优点 | 缺点 |
|------|------|
| 体积小,binlog 少 | 某些函数(NOW(), RAND())结果在主从不一致 |
| 无需额外存储空间 | INSERT ... SELECT 产生大量 binlog |
| | 复杂触发器行为可能不同 |
### Row 模式
只记录行的实际变化(增删改前后的数据),不关心 SQL 是什么。从库拿到的是「数据快照」,直接重放即可:
```
### @1=100 (BIGINT)
### @2='pending' (VARCHAR(20))
### @3=50.00 (DECIMAL(10,2))
### @4='2026-05-16 10:30:00' (DATETIME)
### UPDATE `app_db`.`orders`
### WHERE @1=100
### @2='pending'
### @3=50.00
### @4='2026-05-16 10:30:00'
### SET
### @1=100
### @2='shipped'
### @3=50.00
### @4='2026-05-16 10:30:00'
```
> [!TIP] 理解 Row 模式的关键
> 上面每行 `@n=值 (类型)` 表示第 n 个列的值和 MySQL 内部类型。WHERE 子句定位原行,SET 子句给出新值。虽然比 Statement 模式冗长很多,但**完全不受函数非确定性影响**——这就是为什么生产环境强烈推荐 ROW。
| 优点 | 缺点 |
|------|------|
| 主从数据绝对一致 | 体积极大(尤其是大批量 UPDATE)|
| 支持 DDL 复制 | DELETE 全表扫描时极度膨胀 |
| 不依赖函数确定性 | |
| | BINLOG 格式无法直接阅读 |
### Mixed 模式
Statement 为主,遇到不确定情况切 Row:
| 触发条件 | 切换为 Row |
|---------|-----------|
| 表没有主键 | ✅ |
| 使用了 UUID() / RAND() | ✅ |
| INSERT DELAYED | ✅ |
| UPSERT (REPLACE/ON DUPLICATE KEY) | ✅ |
```ini
binlog_format = STATEMENT # 传统默认(已废弃)
binlog_format = ROW # 生产推荐 ✅
binlog_format = MIXED # 折中方案
```
> [!WARNING] 强烈建议使用 ROW 模式
> Oracle 官方在 5.7+ 已将默认值改为 ROW。Statement 模式下「主库正确但从库错误」是常见的线上事故原因。
### 总结:如何选择?
> [!QUESTION] 如果生产环境必须三选一,你会选哪个?
>
> 答案:**ROW**。理由很简单——数据一致性永远排在体积和可读性之前。虽然 ROW 模式的 binlog 体积更大,但它消除了所有不确定性,而节省空间带来的好处远不及一次主从不一致造成的损失。
### Row vs Statement 对特殊场景的影响
除了非确定性函数(`NOW()`、`RAND()`)之外,还有两类场景在 STATEMENT 模式下容易引发主从不一致:
| 场景 | 问题原因 |
|------|---------|
| **字符集 / Collation 不一致** | 主库和从库 collation 不同时,STATEMENT 模式下的字符串比较可能得出不同结果;ROW 模式传输原始字节,不受影响 |
| **自增 ID(auto_increment)** | 主从并发写入时,STATEMENT 模式下从库的自增值可能与主库不同步,导致重复或跳跃 |
| **INSERT_DELAYED** | 5.6+ 已移除,执行时机不确定 |
**结论**:生产环境一律使用 `binlog_format = ROW`,避免上述所有隐患。
## Binlog 文件结构
```
/var/lib/mysql/
├── mysql-bin.000001 ← 二进制日志文件
├── mysql-bin.000002 ← 自动增长
├── mysql-bin.000003
├── mysql-bin.index ← 索引文件,列出所有 binlog
└── binary.log ← 备用名称(某些配置)
```
### Binlog 文件组织
```mermaid
graph TD
A["mysql-bin.index"] -->|指向| B["mysql-bin.000001"]
A -->|指向| C["mysql-bin.000002"]
A -->|指向| D["mysql-bin.nnn"]
B --> E["Format Description Event"]
B --> F["Query Event"]
B --> G["Rows / Xid Events"]
C --> H["Format Description Event"]
style A fill:#FF8C00,color:#fff
```
> [!QUESTION] 为什么需要 index 文件?
> Binlog 是 append-only 的,新的事务不断写入。index 文件记录了所有有效的 binlog 文件列表,这样 MySQL 启动时可以快速定位到最新的 binlog 位置,而不用扫描整个磁盘。
### 四种核心 Event
| Event 类型 | 作用 | 示例 |
|-----------|------|------|
| **Query Event** | 执行 DDL 或非事务型 DML | CREATE TABLE, ALTER TABLE |
| **Rows Event** | 记录行变更(ROW 模式)| INSERT_ROWS, UPDATE_ROWS, DELETE_ROWS |
| **Xid Event** | 事务提交标记 | COMMIT #1001 |
| **Format Description Event** | 描述 binlog 版本和格式 | 每个文件第一条 |
## Binlog 核心参数
```ini
[mysqld]
server_id = 1 # 必须唯一,用于主从识别
log_bin = /var/log/mysql/mysql-bin # 开启并指定路径
binlog_format = ROW # 推荐 ROW
binlog_row_image = FULL # 完整行(可选值:FULL / MINIMAL / NOBLOB)
binlog_expire_logs_seconds = 604800 # 7 天自动清理
max_binlog_size = 100M # 单文件最大大小
sync_binlog = 1 # 每次 commit 都刷盘(安全)
gtid_mode = ON # GTID 模式
enforce_gtid_consistency = ON # 强制 GTID 兼容的事务
binlog_checksum = CRC32 # 完整性校验
```
### binlog_row_image 参数详解
> [!TIP] 为什么默认是 FULL?
> `FULL` 记录修改前后的所有列值,最简单也最安全。当某些列是 BLOB 类型且非常大时,可以改用 `MINIMAL`(只记录被修改的列 + 主键)来节省空间和性能。
| 值 | 行为 | 适用场景 |
|----|------|---------|
| **FULL** | 记录所有列的值 | 通用场景,默认值 ✅ |
| **MINIMAL** | 仅记录被修改的列 + 必要索引列 | 大表 UPDATE,减少 binlog 体积 |
| **NOBLOB** | 与 FULL 相同,但排除 BLOB/TEXT 列 | 包含大文本字段的表 |
### sync_binlog 与安全性
| 值 | 行为 | 性能损失 | 丢数据风险 |
|----|------|---------|-----------|
| **0** | OS 决定何时刷盘 | 最低 | 断电丢失整个缓冲区 |
| **1** | 每次 commit 都刷盘 | 最高 | 零 ❌ |
| **N** | 每 N 次 commit 刷一次 | 中等 | 最多丢 N 个事务 |
> [!TIP] sync_binlog 与 innodb_flush_log_at_trx_commit
> - 两者都设为 1 = 最强的 ACID 保证
> - 两者配合保证了即使服务器宕机也不会丢失任何已提交事务
> - 对 SSD 磁盘,sync_binlog=1 的性能损耗约 5%~10%,完全可接受
### gtid_mode 的作用
> [!TIP] 为什么推荐使用 GTID?
> 传统基于 Position 的主从复制在故障转移时需要手动计算 position,容易出错。GTID 为每个事务分配全局唯一 ID,复制时只需要知道"哪些事务已经执行过"即可,大大简化了主从切换和恢复流程。
### binlog 清理策略
> [!WARNING] 千万不要直接删除 binlog 文件!
>
> 误删会导致 MySQL 无法识别索引与磁盘文件的对应关系,引发启动失败或数据不一致。
| 方式 | 命令 | 说明 |
|------|------|------|
| **按时间自动清理** | `binlog_expire_logs_seconds = 604800` | 7 天过期自动回收 ✅推荐 |
| **手动清理** | `PURGE BINARY LOGS BEFORE '2026-05-09 00:00:00';` | 保留指定日期之前的 binlog |
| **清理到指定文件** | `PURGE BINARY LOGS TO 'mysql-bin.000003';` | 清除 000003 之前的所有 binlog |
| **清空所有(谨慎)** | `RESET MASTER;` | ⚠️ 仅用于测试环境 |
## 查看和解析 Binlog
### 服务端查询
```sql
-- 当前正在写的 binlog 文件(主从架构中查看主库状态)
SHOW MASTER STATUS\G
-- 列出所有 binlog 文件及大小
SHOW BINARY LOGS;
-- 查看当前 binlog 位置(5.7+ 推荐用法)
SHOW BINARY LOG STATUS\G
```
> [!TIP] `SHOW MASTER STATUS` vs `SHOW BINARY LOG STATUS`
> MySQL 8.0.23+ 更推荐使用 `BINARY LOG STATUS`,因为从库也可以执行这个命令(MASTER 一词在复制语境下对从库不语义准确)。两者功能相同。
### 命令行解析
```bash
# 命令行解析 binlog(-v 展开列名,DECODE-ROWS 以可读格式展示行变更)
mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000001
# 按时间范围解析(常用于精确时间点恢复)
mysqlbinlog --start-datetime="2026-05-16 00:00:00" \
--stop-datetime="2026-05-16 12:00:00" \
mysql-bin.000001 > decoded.sql
# 按 position 位置解析(精确到事务边界)
mysqlbinlog --start-position=1234 --stop-position=5678 mysql-bin.000001
```
> [!QUESTION] 如何定位一个错误操作的具体 position?
>
> 1. 用 `mysqlbinlog --start-datetime="..." --stop-datetime="..." file | grep "DELETE FROM"` 找到可疑 SQL
> 2. 查看 SQL 上方的 `# at XXXXX` 确定起始 position
> 3. 再结合 `--stop-position` 可以只回滚特定事务区间
## 关联笔记
- [[hhs/MySQL/07-高可用与分布式/30-主从复制详解]] — Binlog 是主从复制的基石
- [[hhs/MySQL/06-事务与并发控制/23-ACID 与原子性实现]] — Binlog 与 Redo Log 的两阶段提交协议
- [[hhs/MySQL/07-高可用与分布式/34-备份与恢复]] — 基于 binlog 的 PITR 时间点恢复
@@ -0,0 +1,355 @@
---
tags: [MySQL, 主从复制, Replication, Semi-Sync, GTID]
create time: 2026-05-16 10:30
---
# 主从复制
## 概述
MySQL 主从复制是通过 Binlog 将主库的数据变更传播到从库的过程。它是高可用架构的基础组件,也是读写分离的前提。
## 复制架构
```mermaid
flowchart LR
subgraph "Master"
M1["业务写入 → DML"] --> M2["Server 层生成 Binlog"]
M2 --> M3["Binlog Dump Thread"]
end
subgraph "Network"
M3 -.Binlog Stream.-> I1["IO Thread"]
end
subgraph "Slave"
I1 --> S1["Relay Log<br/>中继日志"]
S1 --> S2["SQL Thread"]
S2 --> S3["数据文件更新"]
end
style M2 fill:#C44569,color:#fff
style S2 fill:#00B6BC,color:#fff
```
### 三线程模型
| 线程 | 归属 | 职责 |
|------|------|------|
| **Binlog Dump Thread** | Master | 连接到一个 Slave 就创建一个,负责发送 binlog |
| **IO Thread** | Slave | 连接 Master,拉取 binlog 写入本地 Relay Log |
| **SQL Thread** | Slave | 读取 Relay Log 重放应用到本地数据库 |
## 同步模式
```mermaid
flowchart TD
subgraph "异步复制 Async"
A1["Client 写入 Master"] --> A2["Master 刷盘返回 OK"]
A2 -->|"后续"| A3["异步发给 Slave"]
end
subgraph "半同步复制 Semi-Sync"
S1["Client 写入 Master"] --> S2["Master 刷盘"]
S2 --> S3["等待 ≥1 个 Slave ACK"]
S3 -->|"超时 10s"| S4["降级为异步 ⚠️"]
S3 -->|"收到 ACK"| S5["返回 OK"]
S5 --> S6["异步发给其他 Slave"]
end
subgraph "组复制 Group Replication"
G1["Client 写入"] --> G2["Paxos 共识"]
G2 --> G3["多数派确认"]
G3 --> G4["并行回放"]
end
style A3 fill:#FF9F43,color:#000
style S5 fill:#00D866,color:#fff
style G3 fill:#00B6BC,color:#fff
```
### 异步复制(默认)
最简单的模式——Master 写完就不管了,异步发给 Slave。
```sql
-- MySQL 5.5 (已弃用)
-- CHANGE MASTER TO
-- MASTER_HOST='10.0.1.10', ...
-- ✅ MySQL 8.0+ 标准语法
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='10.0.1.10',
SOURCE_USER='repl_user',
SOURCE_PASSWORD='password',
SOURCE_PORT=3306,
SOURCE_AUTO_POSITION = 1; -- GTID 模式自动定位
START REPLICA;
```
> [!WARNING] 异步复制的风险
> Master 宕机后,尚未传输到 Slave 的数据会丢失。对于金融系统这是不可接受的。
### 半同步复制(Semi-Sync)
至少一个从库确认收到 Binlog 后,Master 才返回写成功。
```ini
# Master 端配置
plugin_load_add = semisync_master
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_timeout = 10000 # 10 秒超时后降级为异步
rpl_semi_sync_master_wait_point = AFTER_SYNC # 刷盘后再等待
```
```ini
# Slave 端配置
plugin_load_add = semisync_slave
rpl_semi_sync_slave_enabled = 1
```
> [!NOTE] 半同步不是银弹
> - 从库收到 Binlog ≠ 已从磁盘持久化
> - 如果 Master 和从库同时断电,仍可能丢数据
> - 网络分区时 Master 会超时降级为异步
>
> **最佳实践**:Semi-Sync + 定期备份 + PITR 恢复组合使用
## GTID 复制
GTID(Global Transaction Identifier)给每个事务分配全局唯一的 ID,替代传统的 position-based 复制。
```
GTID 格式: source_id:transaction_id
例: 3E11FA47-71CA-11E1-9E33-C80AA9429562:23
↑ UUID of server ↑ Sequence number
```
### GTID 复制流程
```mermaid
flowchart TD
A["Client 事务写入 Master"] --> B["Server 层分配 GTID<br/>source_id:next_seq"]
B --> C["写入 Binlog 事件<br/>带 GTID 标记"]
C --> D["Binlog Dump 线程发送<br/>GTID + 事件到 Slave"]
D --> E["Slave IO Thread 写入<br/>Relay Log (含 GTID)"]
E --> F{"GTID Set<br/>是否已包含此 GTID?"}
F -->|是| G["跳过重复事件 ✅"]
F -->|否| H["SQL Worker 重放事务"]
H --> I["更新 Slave gtid_executed"]
G --> J["继续下一个事件"]
I --> J
style B fill:#C44569,color:#fff
style F fill:#FFD166,color:#000
style H fill:#00B6BC,color:#fff
```
> [!NOTE] GTID 的一致性约束
> 启用 `enforce_gtid_consistency = ON` 后,以下操作被**禁止**:
> - 对非事务引擎(如 MyISAM)的 DML
> - 创建/删除临时表
> - `CREATE TABLE ... SELECT`(混合语句级和语句级二进制日志)
> - 对视图的 DML(无法追踪来源表)
### GTID vs 传统 Position
| 特性 | Position-based | GTID-based |
|------|---------------|------------|
| **定位方式** | 文件名 + position 号 | GTID set |
| **跨库切换** | 需要手动查 position | 自动定位 |
| **跳过事务** | 困难 | `SET gtid_next = '...'; BEGIN; COMMIT; SET gtid_next = AUTOMATIC;` |
| **一致性检查** | 手动比对 | `pt-table-checksum` + GTID 验证 |
```ini
# 启用 GTID
gtid_mode = ON
enforce_gtid_consistency = ON
master_info_repository = TABLE # 复制信息存在表中而非文件
relay_log_info_repository = TABLE
```
## 并行复制(Multithreaded Slave, MTS)
MySQL 5.6+ 引入从库并行回放,大幅提升复制延迟处理能力。
```ini
# MySQL 5.x(已弃用)
# slave_parallel_type = LOGICAL_CLOCK
# slave_parallel_workers = 8
# ✅ MySQL 8.0+ 标准命名
replica_parallel_type = LOGICAL_CLOCK # 基于 GTID 的并发回放
replica_parallel_workers = 8 # 并发 worker 数量
```
### MTS 并行模式对比
```mermaid
flowchart LR
subgraph "DATABASE(默认)"
D1["Relay Log 事件"] --> D2{"按 database\n分桶"}
D2 -->|"db_a"| W1["Worker 1"]
D2 -->|"db_b"| W2["Worker 2"]
D2 -->|"db_c"| W3["Worker 3"]
W1 & W2 & W3 --> J1["写入数据"]
end
subgraph "GROUP_TRANSACTIONS"
G1["Relay Log 事件"] --> G2{"判断事务\n依赖关系"}
G2 -->|"无冲突"| A1["组 1 → 并行执行"]
G2 -->|"有冲突"| A2["组 2 → 串行等待"]
G1 -->|"无冲突"| A1
A1 --> J2["写入数据"]
A2 --> J2
end
style A1 fill:#00D866,color:#fff
style A2 fill:#FF9F43,color:#000
```
| 维度 | `DATABASE` | `GROUP_TRANSACTIONS` |
|------|-----------|---------------------|
| **分组依据** | database 名称 | 事务间的写集依赖关系 |
| **并发度** | 受限于 database 数量 | 取决于并发事务比例,通常更高 |
| **精确度** | 粗粒度——同一 DB 内串行 | 细粒度——无依赖即可并行 |
| **适用场景** | schema 设计良好的系统 | 多库共享、跨库事务较多的场景 |
| **引入版本** | MySQL 5.6 | MySQL 5.7+ |
> [!TIP] 如何选择
> - 如果业务按 database 做数据隔离(每个 tenant 一个 DB),选 `DATABASE`
> - 如果多个业务共享少数几个 database,选 `GROUP_TRANSACTIONS`
### LOGICAL_CLOCK 原理
> [!INFO] `LOGICAL_CLOCK` = GTID 驱动的并行回放
> 无论选择 `DATABASE` 还是 `GROUP_TRANSACTIONS`,底层引擎都是 LOGICAL_CLOCK。
> 区别仅在于如何把连续事件流切分成可并行的逻辑组。
```mermaid
flowchart TD
A["Relay Log 事件流"] --> B{"按 database 分组"}
B --> C["DB1 的事件队列"]
B --> D["DB2 的事件队列"]
B --> E["DBn 的事件队列"]
C --> W1["Worker 1 执行 DB1 事件"]
D --> W2["Worker 2 执行 DB2 事件"]
E --> Wn["Worker n 执行 DBn 事件"]
style W1 fill:#00D866,color:#fff
style W2 fill:#00B6BC,color:#fff
style Wn fill:#FF9F43,color:#000
```
> [!TIP] Parallel Replication 的限制
> - 基于 database 的并行要求同一 database 内没有冲突事务
> - 跨 database 的操作无法并行
> - innodb_table_locks=OFF 且 autocommit=1 时才能并行(避免表锁冲突)
## 监控复制状态
```sql
-- MySQL 8.0+ 已弃用 SHOW SLAVE STATUS
-- SHOW SLAVE STATUS\G
-- ✅ 使用 performance_schema 视图
SELECT * FROM performance_schema.replication_connection_status;
SELECT * FROM performance_schema.replication_applier_status;
-- ⚠️ 以上两表各返回一行,多从库场景下需要关联:
-- connection: 一条代表 Master → Slave 的 IO 连接
-- applier: 每行代表一个 SQL Worker 线程
```
```sql
-- 实时延迟监控(MySQL 8.0+, GTID 模式)
SELECT
MAX(latency) / 1000000000 AS max_lag_seconds
FROM (
SELECT
MAX(clock_timestamp) - MIN(clock_start) AS latency
FROM performance_schema.replication_applier_status_by_worker
) lag;
```
## 常见问题与排查
### IO 线程断开(Slave_IO_Running: No)
```mermaid
flowchart TD
A["IO Thread 断连"] --> B{"原因定位"}
B -->|网络不通| C["检查防火墙 / 安全组端口"]
B -->|Binlog 已过期| D["purge binary logs / 调整 expire_logs_days"]
B -->|认证失败| E["核对 user 权限 & password"]
B -->|Server UUID 冲突| F["重置 slave server_uuid"]
C --> G["CHANGE REPLICATION SOURCE TO<br/>SOURCE_LOG_FILE = '...',<br/>SOURCE_LOG_POS = ..."]
D --> G
E --> G
F --> G
G --> H["START REPLICA"]
H --> I["确认 IO 恢复"]
style A fill:#FF6B6B,color:#fff
style I fill:#00D866,color:#fff
```
**常见排查步骤:**
```sql
-- 1. 查看具体错误信息
SELECT LAST_ERROR_MESSAGE FROM performance_schema.replication_connection_status;
-- 2. 检查 Master Binlog 是否已被清理
SHOW MASTER LOGS;
-- 3. 如果 Binlog 已过期,需要全量恢复(mysqldump + --single-transaction)
-- 或使用 xtrabackup 做 In-place Slave 重建
```
### SQL 线程卡住(Slave_SQL_Running: No)
这通常是由于数据不一致导致的——例如从库被手动修改过。
> [!WARNING] 不要直接跳过!
> 盲目 `sql_slave_skip_counter` 可能导致数据静默丢失。必须先确认事务内容。
```sql
-- ⚠️ MySQL 5.7 及以下(已弃用)
-- SET GLOBAL sql_slave_skip_counter = 1;
-- STOP SLAVE; START SLAVE;
-- ✅ MySQL 8.0+ GTID 模式:跳过指定事务
SET GTID_NEXT='3E11FA47-71CA-11E1-9E33-C80AA9429562:100';
BEGIN; COMMIT;
SET GTID_NEXT='AUTOMATIC';
-- 重新加入复制组
STOP REPLICA;
SET GLOBAL gtid_purged = '3E11FA47-71CA-11E1-9E33-C80AA9429562:1-100';
START REPLICA;
```
### 延迟过大如何诊断
```mermaid
flowchart LR
A["Seconds_Behind_Master 大"] --> B{"IO 延迟还是 SQL 延迟?"}
B -->|"Relay_Log_Space 持续增长"| C["SQL Worker 回放太慢<br/>→ 增加 replica_parallel_workers<br/>→ 考虑 GROUP_TRANSACTIONS"]
B -->|"Read_Master_Log_Pos 跟丢"| D["网络带宽不足 / Master<br/>写入量太大<br/>→ 检查网络 / 限制 binlog 传输大小"]
B -->|"DDL 执行中"| E["大表 ALTER TABLE 阻塞<br/>→ 使用 pt-online-schema-change"]
style C fill:#00B6BC,color:#fff
style D fill:#FF9F43,color:#000
style E fill:#C44569,color:#fff
```
---
## 关联笔记
- [[hhs/MySQL/07-高可用与分布式/29-Binary Log]] — Binlog 是主从复制的数据源
- [[hhs/MySQL/07-高可用与分布式/31-MHA与Orchestrator]] — 自动故障切换工具
- [[hhs/MySQL/07-高可用与分布式/32-Group Replication]] — Paxos 多主复制进阶方案
@@ -0,0 +1,345 @@
---
tags: [MySQL, MHA, Orchestrator, 高可用, 故障切换, MySQL HA]
create time: 2026-05-16 14:30
---
# MHA / Orchestrator 故障切换
## 概述
当主库宕机时,如何**快速且正确**地选出一个新的主库并完成流量切换?这就是高可用(HA)系统的核心问题——既要尽量减少停机时间,又要保证数据不丢失、不发生冲突。
本节将对比两种主流的 MySQL 自动故障切换方案:
- **MHA**:由日本 DeNA 开发的经典 Perl 方案,以数据一致性著称
- **Orchestrator**:由 Spotify 开发的现代 Go 工具,侧重拓扑管理和运维便利
> [!QUESTION] 核心思考
> 在讨论具体工具之前,先问一个问题:**如果只有一个从库,它应该立刻提升为主库吗?**
>
> 答案并不简单。如果主库只是短暂的网络抖动,切换就是"过度响应";但如果主库真的挂了还不切,服务就不可用了。如何在「反应太快」和「反应太慢」之间找到平衡,是 HA 设计的永恒命题。
## 故障场景分类
```mermaid
flowchart TD
A["主库异常"] --> Type{"异常类型"}
Type -->|"进程挂死"<| H1["检测快<br/>切换快<br/>可自动处理"]
Type -->|"磁盘损坏"<| H2["数据可能损坏<br/>需人工介入"]
Type -->|"脑裂 Split-Brain"<| H3["最危险!<br/>双主写入导致数据冲突"]
Type -->|"网络分区"<| H4["隔离误判<br/>假阳性切换"]
style H3 fill:#EE5A24,color:#fff
```
> [!NOTE] 关键认知
> HA 工具能自动处理的大多是**进程级故障**。一旦发生磁盘损坏或网络脑裂,就需要人工判断——这也是为什么理解底层原理比单纯部署工具更重要。
## MHA(Master High Availability)
由日本 DeNA 开发(2009年)的经典 MySQL HA 方案,以 Perl 编写。其核心设计思想是**「绝不丢失已提交的数据」**——在故障切换前,会尽力补齐最从库缺失的 binlog 事件。
```mermaid
sequenceDiagram
participant M as MHA Manager
participant DM as Dead Master
participant MS as Most Up-to-Date Slave
participant SS as Other Slaves
M->>DM: ping... no response
Note over M: 连续 3 次 ping 失败
M->>MS: SHOW MASTER STATUS
MS-->>M: relay log 最新的位置
M->>SS: SHOW RELAYLOG STATUS
SS-->>M: relay log 较旧的位置
Note over M: Step 1: 补齐最从库的 missing events
M->>MS: 应用其他 slave 的 relay log 中的缺失事件
Note over M: Step 2: 提升新主
M->>MS: CHANGE MASTER TO... (无 master)
Note over MS: 脱离子复制链,成为独立主库
Note over M: Step 3: 重新配置其他从库指向新主
M->>SS: CHANGE MASTER TO master=new-master-ip
Note over SS: 全部切换到新主
```
### MHA 的核心优势
| 特性 | 说明 |
|------|------|
| **数据一致性保证** | 选择 relay log 最新的 slave 作为新主 |
| **自动补齐** | 从其他 slave 获取 dead master 尚未传的 binlog |
| **VIP 漂移** | 通过 Keepalived + VIP 实现无缝切换 |
| **Phantom Manager** | Manager 节点本身也可以做数据管理 |
> [!TIP] 为什么选「最从」而非「任意从」?
>
> 假设 Master → SlaveA → SlaveB 的级联复制结构:
> - Master 挂了,SlaveA 可能比 SlaveB 更「新」
> - 如果直接提升 SlaveB,需要等待它追完所有relay log,延时长且风险大
> - MHA 的做法是:找到 relay log 最全的那个从库,确保数据丢失最小
### MHA 组成
MHA 采用 **Manager + Node** 的分布式架构:
```
Manager Node ← 运行 mha_manager,独立部署(不放在 MySQL 节点上)
Master Node ← 当前主库(可挂,不影响 Node 组件)
Slaves Nodes ← 多个从库(自动检测哪个最新)
App Servers ← 应用端通过 VIP 访问
```
> [!NOTE] 部署建议
> - Manager 应部署在**独立的第三方机器**上,不要和任何 MySQL 实例同机
> - 所有节点之间必须配置 **SSH 互信**,Manager 需要通过 SSH 执行远程操作
#### 安装与配置
```bash
# 安装 RPM 包(CentOS/RHEL 7+)
yum install mha4mysql-manager mha4mysql-node -y
```
```bash
# 配置文件 /etc/masterha/app1.cnf
[server default]
manager_workdir=/data/mha/app1
manager_log=/data/mha/app1/manager.log
user=mha_user # Manager 连接 MySQL 的管理账号
password=mha_pass
ssh_user=root # SSH 连接账号
repl_user=repl_user # 主从复制账号
repl_password=repl_pass
[server1]
hostname=10.0.1.10
candidate_master=1 # 允许被提升为主库
[server2]
hostname=10.0.1.11
candidate_master=1
[server3]
hostname=10.0.1.12
no_master=1 # 永远不做主库(例如数据量特别大的从库)
```
> [!TIP] candidate_master 的作用
>
> 默认情况下,MHA 优先选择 relay log 最新的从库,而不看它离 Master 的距离。但如果一个从库是「级联」链路末端(Master → S1 → S2),即使 S2 的 relay log 最全,也不应该提升它——因为它一旦变主,其他从库要重新追很长的 binlog。
>
> `candidate_master=1` 可以告诉 MHA:**这个节点虽然可能不是 relay log 最完整的,但仍然有资格被提升**。配合 `report_script` 还可以加入更多判断逻辑。
#### GTID 支持
MHA 0.56+ 版本开始支持 GTID 模式下的故障切换:
```ini
# app1.cnf 中开启 GTID 模式
[server default]
master_binlog_dir=/var/lib/mysql
remote_workdir=/tmp/mha
use_gtid_current_pos=1 # 使用 GTID 定位复制位置
```
> [!NOTE] GTID vs Position 的选择
>
> | 方式 | 优点 | 缺点 |
> |------|------|------|
> | binlog position | 兼容所有 MySQL 版本 | 需要精确计算每个 slave 丢失的事件 |
> | GTID current_pos | 简化了定位逻辑,切换更可靠 | 要求 MySQL 5.6+,且需提前开启 GTID |
如果生产环境已启用 GTID,强烈建议使用 GTID 模式;否则保持默认的 binlog position 即可。
### MHA 的局限
> [!WARNING] MHA 的问题
> 1. **Manager 单点**:虽然可以配 Phantom,但需要一个额外的管理入口
> 2. **Perl 技术栈**:生态维护活跃度下降
> 3. **不支持 GTID 之前**:GTID 出现前依赖 binlog position
> 4. **脑裂风险**:如果 Master 只是网络闪断而非真宕机,可能出现双主
>
> **替代方案**:Orchestrator + Go-HAProxy 或 Patroni(PostgreSQL 方向)
## Orchestrator
由 Spotify 开源的纯 Go 编写的 MySQL 拓扑发现和运维工具。核心定位是**「MySQL 拓扑的管理员」**——它首先帮你搞清楚「现在整个集群是什么样的」,然后在此基础上提供故障切换、手动切换、修复落后从库等操作。
```mermaid
flowchart TB
subgraph "Orchestrator 架构"
OAPI["Orchestrator HTTP API<br/>port 3000"]
ODB["SQLite/MySQL<br/>存储拓扑信息"]
OSCAN["Discovery Scan<br/>定期 SHOW SLAVE HOSTS"]
frontend["Web UI<br/>拓扑可视化"] --> OAPI
dashboard["Dashboard"] --> OAPI
OSCAN --> ODB
OAPI --> ODB
HAProxy["Go-HAProxy / <br/>MaxScale / ProxySQL"] -->|"重定向流量"| NEWMASTER["新主库"]
end
OAPI --> HAProxy
```
> [!NOTE] 设计理念差异
>
> | 维度 | MHA | Orchestrator |
> |------|-----|-------------|
> | 首要目标 | 自动故障切换(Failover) | 拓扑管理 + 运维辅助 |
> | 侵入性 | 需要在每个节点安装 node 组件 | **零侵入**,只读查询 |
> | 执行方式 | Manager 直接 SSH 操作各节点 | 通过 API 调用,外部脚本接管 |
> | 技术栈 | Perl | Go(单二进制文件部署) |
### Orchestrator 如何工作
Orchestrator 的核心是一个**后台扫描循环**:
```
每 5~30 秒 ──┬──► SHOW SLAVE HOSTS / SHOW REPLICA STATUS
├──► 读取各实例的 Binlog Position
├──► 构建完整拓扑树(含级联复制关系)
└──► 写入内部数据库 → Web UI 刷新显示
```
这意味着 Orchestrator **不主动修改任何 MySQL 配置**,它只是一个观察者 + 操作台。所有的故障切换需要外部工具(如 Go-HAProxy)配合完成。
### 自动故障切换(Auto-Failover)
虽然 Orchestrator 以拓扑管理见长,但它同样支持全自动故障切换:
```ini
# orchestrator.conf.json 关键配置
{
"DiscoverByShowSlaveHosts": true,
"DetectMasterFailoverWithSuperReadOnly": false,
"DetectorScript": "/usr/local/bin/orchestrator-detector.sh",
// 自动 Failover 的条件控制
"AutoFaildown": true, // Master 恢复后是否自动降级并重新纳入复制链
"AutoFailop": true, // Master 宕机时是否自动切换
"ExpireStatsSec": 600 // 统计信息过期时间
// 选择新主的策略
"PreferredCandidateDistances": [1, 2], // 优先选距离近的从库
"CheckSlaveLagThreadsCached": true // 自动检测 Slave 延迟
}
```
> [!QUESTION] Orchestrator 为什么不直接改 VIP?
>
> MHA 内置了 VIP 漂移功能,但 Orchestrator 选择了更松耦合的设计。原因是:
> 1. 不同环境使用不同的代理层(HAProxy / ProxySQL / MaxScale / 云厂商 LB)
> 2. 让外部系统决定「怎么切流量」更加灵活
> 3. Orchestrator 专注于做一件事:**准确知道拓扑结构并给出操作建议**
>
> 生产环境中常见的做法是写一个 wrapper 脚本,当 Orchestrator 检测到 Master 挂掉时自动调用它来切换 ProxySQL 或 HAProxy 的后端配置。
### 常用 API 示例
```bash
# 1. 查看某个应用下的所有实例拓扑
curl http://localhost:3000/api/topology/app/mydb
# 2. 找到最适合晋升为主库的候选者
curl http://localhost:3000/api/candidate-instances/app/mydb
# 3. 强制提升某实例为主库(手动干预)
curl -X POST http://localhost:3000/api/become-master/host/db/instance
# 4. 自动修复脱离的从库(重新对接到新主)
curl -X POST http://localhost:3000/api/recover/full/host/db/instance
# 5. 获取故障切换分析(不实际执行,只看推荐方案)
curl http://localhost:3000/api/fail-coordination/force/master-simulate/host/db/instance
```
> [!TIP] Simulate 模式
>
> `fail-coordination` 接口支持模拟切换流程而不真正执行。在生产环境中强烈建议先用这个命令**演练一次**,确认推荐的切换方案符合预期后再正式执行。
## 脑裂的处理
```mermaid
flowchart TD
A["网络分区导致<br/>Master 与 Slave 失联"] --> B{"HA 工具判断<br/>Master 存活?"}
B -->|否| C["提升 Slave 为新主"]
C --> D{"原 Master 恢复后<br/>是否还活着?"}
D -->|是| E["⚠️ 双主!两个库都在接受写请求"]
E --> F["用 binlog 比对<br/>找出冲突数据"]
F --> G["回滚冲突数据<br/>或将原 Master 降级为从库"]
D -->|否| H["原 Master 重启后<br/>作为从库接入"]
B -->|是| I["不做切换,继续等待"]
style E fill:#EE5A24,color:#fff
style H fill:#00D866,color:#fff
```
### 脑裂后的数据恢复步骤
当确认发生脑裂时,标准处理流程如下:
```
Step 1 ──► 停止新主的写入(防止更多冲突)
Step 2 ──► 导出两个库的 GTID 集合或 binlog position 范围
Step 3 ──► 使用 mysqlbinlog + diff 对比差异事件
Step 4 ──► 保留「正确」分支的数据
Step 5 ──► 将另一方的冲突数据标记为 rollback 或直接丢弃
Step 6 ──► 重建复制链路
```
> [!WARNING] 预防脑裂的策略
> 1. **STONITH(Shoot The Other Node In The Head)**:通过 IPMI / 远程管理卡强制关闭疑似脑裂节点——这是最彻底的方案
> 2. **Quorum(多数派机制)**:3 节点集群中,至少 2 节点存活才允许选举,避免少数派擅自决策
> 3. **带外心跳**:除了 TCP ping,再用 SSH 或其他独立通道探测,降低单通道误判概率
> 4. **延迟备份**:保留一台物理隔离的冷备,脑裂时可从中恢复,相当于最后的保险丝
> 5. **超时阈值调优**:不要为了减少误切把超时设得太长(如超过 30s),故障期间每一秒都很宝贵
## 选型建议
```mermaid
flowchart LR
A["需要 MySQL HA<br/>自动故障切换"] --> B{"技术栈偏好?"}
B -->|"传统运维体系<br/>已有 Perl 团队"| MHA["MHA<br/>经典稳定"]
B -->|"Go / 云原生<br/>需要 Web UI"| ORC["Orchestrator<br/>灵活现代"]
B -->|"想要官方支持"<| INN["InnoDB Cluster<br/>MySQL Shell"]
B -->|"不想自己维护 HA"| CLOUD["云厂商 RDS<br/>托管方案"]
style MHA fill:#4A90D9,color:#fff
style ORC fill:#00D866,color:#fff
style INN fill:#F5A623,color:#fff
style CLOUD fill:#EE5A24,color:#fff
```
### 关键对比维度
| 维度 | MHA | Orchestrator | InnoDB Cluster |
|------|-----|-------------|---------------|
| **数据丢失** | 极小(自动补齐 binlog) | 较小(依赖 GTID) | 零(基于 Group Replication) |
| **部署复杂度** | 中(需 SSH 互信 + RPM) | 低(单二进制文件) | 高(需 PXC/Galera) |
| **故障恢复时间** | 通常 < 10s | 通常 < 15s | 秒级但检测较慢 |
| **在线扩展** | 不支持 | 仅拓扑管理,不处理 DDL | 支持分布式 DDL |
| **社区活跃度** | 较低(DeNA 维护) | 高(VK / 社区活跃) | 官方维护 |
| **适用 MySQL 版本** | 5.6~5.7 | 5.6+(推荐 8.0) | 8.0+ |
> [!TIP] 实际生产中的常见选择
>
> - **中小团队(< 10 台 MySQL 实例)**:Orchestrator 是性价比最高的选择——上手简单、维护成本低、Web UI 对 DBA 友好
> - **大流量业务对数据一致性要求极高**:如果预算允许,上云厂商 RDS 的付费高可用版是最省心的
> - **自建全量多主写入**:考虑 InnoDB Group Replication 或 ProxySQL + Orchestrator 组合
## 关联笔记
- [[hhs/MySQL/07-高可用与分布式/30-主从复制详解]] — 主从复制的配置和监控
- [[hhs/MySQL/07-高可用与分布式/32-Group Replication]] — 内置多主复制,不需要外部 HA 工具
- [[hhs/Redis/06-主从与哨兵]] — Redis Sentinel 与 MySQL HA 方案的对比
@@ -0,0 +1,275 @@
---
tags: [MySQL, Group Replication, Paxos, 多主复制, 集群]
create time: 2026-05-16 00:00
---
# Group Replication(组复制)
## 概述
Group Replication(GR)是 Oracle 官方提供的 MySQL 高可用解决方案,基于 Paxos 共识算法实现多主复制,提供自动故障检测和成员管理。它代表了 MySQL 分布式能力的核心方向。
## GR 与主从复制的对比
```mermaid
flowchart LR
subgraph "传统主从"
RM["单主模型<br/>Master → Slave(s)"]
RS["异步/半同步复制"]
RF["故障切换需外部工具"]
end
subgraph "Group Replication"
RG["多主模型<br/>任一节点接受写入"]
RP["Paxos 共识保证一致性"]
RA["自动故障检测 + 自动选举"]
end
RM -.">"| RA
RF -.">"| RA
style RG fill:#00B6BC,color:#fff
style RP fill:#C44569,color:#fff
style RA fill:#00D866,color:#fff
```
## 两种工作模式
### 单主模式(Single-Primary)
```mermaid
flowchart TD
Primary["Primary\nRW + RO"] --> Paxos["Paxos 共识层"]
Paxos --> S1["Secondary 1\n只读"]
Paxos --> S2["Secondary 2\n只读"]
Paxos --> S3["Secondary N\n只读"]
style Primary fill:#00B6BC,color:#fff
style Paxos fill:#C44569,color:#fff
style S1 fill:#74b9ff,color:#000
style S2 fill:#74b9ff,color:#000
style S3 fill:#74b9ff,color:#000
```
- **特点**:类似主从,但故障切换全自动
- **适用**:大多数读写分离场景
- **冲突解决**:单主天然无冲突
### 多主模式(Multi-Primary)
```mermaid
flowchart LR
N1["Node 1\nRW + RO"] <-- Paxos --> N2["Node 2\nRW + RO"]
N2 <-- Paxos --> N3["Node 3\nRW + RO"]
style N1 fill:#00B6BC,color:#fff
style N2 fill:#C44569,color:#fff
style N3 fill:#00D866,color:#fff
```
- **特点**:所有节点都可读写
- **适用**:多数据中心容灾、低延迟写入需求
- **冲突检测**:基于行级别的乐观锁,冲突时拒绝写入节点
> [!WARNING] 多主模式的限制
> - 有冲突检测但不自动解决——冲突的那条事务会被拒绝并返回错误码 3092
> - 不适合相同数据在多个节点上同时修改的场景
> - **生产环境推荐使用单主模式**
## GR 的工作原理
```mermaid
flowchart TD
Client["客户端连接"] --> Node["任意节点"]
Node --> TXN["事务执行"]
TXN --> Cert["Certification: 检查与其他节点的冲突"]
Cert -->|"无冲突"| Order["Ordering: 获得全局顺序号"]
Cert -->|"冲突"| REJECT["❌ 事务被拒绝<br/>error 3092"]
Order --> Broadcast["Broadcast: 向所有成员广播 committed order"]
Broadcast --> Apply["Apply: 按全局顺序提交"]
Apply --> Members{"所有成员 >= N/2+1?"}
Members -->|是| SUCCESS["✅ 事务提交成功"]
Members -->|否| PENDING["⏳ pending 状态<br/>等待多数派确认"]
style SUCCESS fill:#00D866,color:#fff
style REJECT fill:#EE5A24,color:#fff
style PENDING fill:#FF9F43,color:#000
```
### Certification(认证)
GR 在每个事务提交前进行冲突检测:
```go
// 伪代码:certification 算法
func certTransaction(txn Transaction) error {
for _, pk := range txn.writeSet.primaryKeys {
// 检查全局 write set 中是否存在相同主键的并发事务
if conflict, exists := globalWriteSet.Find(pk); exists {
if !areCommutative(txn, conflict) {
return ErrorConflict("row-level conflict detected")
}
}
}
return nil
}
```
**认证流程的核心思路:**
1. **Collect**: 当事务在本地执行完毕后,收集所有被修改行的主键集合(write set)
2. **Certify**: 将 write set 与组内其他成员已提交的事务进行比较
3. **Resolve**: 若发现冲突且不可交换(commutative),则拒绝该事务(返回错误码 3092);若无冲突或可交换,则通过认证
> [!TIP] 什么是可交换操作?
> 两个事务如果修改的是不同的行,即使它们同时发生也不会冲突,这在数学上称为「可交换」(commutative)。例如:`UPDATE users SET age=age+1 WHERE id=1` 和 `UPDATE users SET name='Alice' WHERE id=2` 是可交换的,GR 允许两者都提交。
### Ordering(全局排序)
通过认证后,事务需要获得一个全局顺序号(view change ID + local order),确保所有节点以**完全相同的顺序**应用事务——这是保证一致性的关键。
### Broadcast & Apply(广播与应用)
获得全局序号的事务会通过 group communication layer 广播给所有组成员。每个成员收到后按全局顺序依次 apply,最终达成状态机的一致性。这就是 Paxos/Raft 式的确定性状态机复制。
```mermaid
flowchart TD
TA["事务 A: UPDATE users SET x=1 WHERE id=1"]
TB["事务 B: UPDATE users SET y=2 WHERE id=1"]
TA --> CA{Certification}
TB --> CB{Certification}
CA -->|passed| O1["已获得全局顺序 #100"]
CB -->|conflict! both write id=1| CE["Conflict Error 3092"]
style CE fill:#EE5A24,color:#fff
```
## 部署配置
```ini
# 每个节点 my.cnf 中需要配置
# 基本复制配置
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
master_info_repository = TABLE
relay_log_info_repository = TABLE
binlog_checksum = NONE
# GR 专用配置
plugin_load_add = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_start_on_boot = OFF
group_replication_local_address = "10.0.1.10:33061"
group_replication_group_seeds = "10.0.1.10:33061,10.0.1.11:33061,10.0.1.12:33061"
# 单主模式
group_replication_single_primary_mode = ON
group_replication_enforce_update_everywhere_checks = OFF
# 多主模式(取消注释以上两行,注释掉以下两行)
# group_replication_single_primary_mode = OFF
# group_replication_enforce_update_everywhere_checks = ON
```
### 初始化步骤
```sql
-- 在每个节点上执行
-- 1. 创建复制用户
CREATE USER rpl_user@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
FLUSH PRIVILEGES;
-- 2. 配置集群地址
SET GLOBAL group_replication_bootstrap_group = OFF;
-- 3. 在第一个节点启动组(引导组)
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;
-- 4. 在其他节点加入组
START GROUP_REPLICATION;
-- 5. 检查组成员状态
SELECT * FROM performance_schema.replication_group_members;
```
## 性能与注意事项
### 性能特点
GR 的性能瓶颈主要来自 Paxos 共识层,以下为生产环境的经验总结:
| 维度 | 说明 |
|------|------|
| **写入延迟** | 单主模式下,提交需要等待多数派确认(majority commit),TPS 通常比异步主从低 20%~40% |
| **并发能力** | 行级认证 + 乐观锁策略让不同行的写操作可以并行通过,高并发小事务场景表现较好 |
| **网络要求** | 节点间通过端口 33061 通信,要求低延迟、高带宽局域网;跨 DC 部署延迟应控制在 5ms 以内 |
| **SSD 必要** | binlog 和 relay log 频繁刷盘,强烈建议使用 SSD/NVMe |
### 生产注意事项
> [!IMPORTANT] 上线前必须检查的清单
> 1. **表必须有主键**:没有主键的表无法被 GR 复制,启动时会报错并拒绝加入该表的数据
> 2. **外键约束**:建议关闭 `foreign_key_checks`,GR 不保证跨节点外键一致性
> 3. **存储引擎**:只支持 InnoDB
> 4. **事务隔离级别**:推荐使用 `READ COMMITTED`(GR 默认),RR 在部分场景下可能增加冲突概率
> 5. **自增主键冲突**:多主模式下多个节点同时产生自增值会冲突,需配置 `auto_increment_increment` 和 `auto_increment_offset` 错开编号区间
> 6. **DDL 操作**:大表的 DDL(如 ADD INDEX)会在整个组阻塞,务必在维护窗口执行
> 7. **GTID 不可变**:开启 GTID 后无法关闭,数据初始化时必须用 GTID 方式导出导入
## 故障处理
```mermaid
stateDiagram-v2
[*] --> ONLINE: 节点正常加入组
ONLINE --> REMOVED: 节点宕机 / 网络断开
ONLINE --> OFFLINE: 手动 STOP GROUP_REPLICATION
REMOVED --> SELF_JOIN: 节点恢复并重新加入
REMOVED --> [*]: 永久退出
SELF_JOIN --> ONLINE: 数据同步完成
state REMOVED {
state_config ["Config Change Pending"]
state_error ["Error Recovering"]
}
```
> [!NOTE] 组的最小生存条件
> - 单主模式:至少 2 个节点在线(1 primary + 1 secondary)
> - 多主模式:至少 N/2+1 个节点在线
> - 少于这个阈值,整个组进入 readonly 模式,拒绝所有写入
## 监控命令
```sql
-- 组成员状态
SELECT * FROM performance_schema.replication_group_members;
-- MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE (ONLINE/RECOVERING/ERROR/RO)
-- 组内事务统计
SELECT * FROM performance_schema.replication_group_member_stats;
-- 正在进行的冲突事务
SELECT * FROM performance_schema.replication_group_history;
-- 连接数
SELECT * FROM performance_schema.replication_group_connections;
```
## 关联笔记
- [[hhs/MySQL/07-高可用与分布式/30-主从复制详解]] — GR 的主干是改进后的主从复制
- [[hhs/MySQL/07-高可用与分布式/31-MHA与Orchestrator]] — 为什么 GR 不需要外部 HA 工具
- [[hhs/MySQL/08-工程实践/38-监控指标]] — GR 特有的监控指标
@@ -0,0 +1,330 @@
---
tags: [MySQL, InnoDB Cluster, MySQL Shell, MySQL Router, Group Replication, 高可用, 官方集群, HA]
create time: 2026-05-16 00:00
status: 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 实例中 | ✅ |
核心能力包括:**自动故障转移**、**多主/单写拓扑**、**数据一致性保证**以及**无缝连接**。
## 架构总览
```mermaid
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 通过两个端口天然实现了读写分离,应用层无需感知集群拓扑变化。
## 快速搭建
### 前置条件
```bash
# 准备 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 一键部署
```javascript
// 连接到第一个实例
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()
```
输出示例:
```json
{
"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
```bash
# 自动生成路由配置
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 不可达时:
```mermaid
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,值越高越优先成为 Primary
> - `group_repification_single_primary_mode`:开启单写模式后,同一时刻只有一个可写节点
> - **RPO = 0**:在同步模式下,已提交的事务不会丢失;异步模式下可能存在少量数据损失
### 容忍节点数
| 节点总数 | 可容忍故障数 | 需在线最低数 |
|----------|:-:|------------|
| 3 节点 | 1 | 2 |
| 5 节点 | 2 | 3 |
| 7 节点 | 3 | 4 |
> [!WARNING] 脑裂风险
> 当网络分区导致集群分裂成两组时,每组都认为自己是合法的。InnoDB Cluster 通过 **"多数派原则"(Quorum)** 解决——只有拥有多数节点的分区才能继续提供服务,其余进入只读模式。这就是为什么推荐部署奇数个节点:避免平分局面。
## 日常运维操作
以下操作均在 MySQL Shell 中执行(JavaScript 模式)。
### 查看集群状态
```javascript
// 连接到集群中的任意实例即可
dba.connect('root@localhost:33061')
var cluster = dba.getCluster()
cluster.status() // 输出完整的拓扑和每个节点的健康信息
```
### 添加 / 移除节点
```javascript
const cluster = dba.getCluster()
// 添加新节点
cluster.addInstance('root@localhost:33064')
// 安全移除节点(会先同步数据)
cluster.removeInstance('root@localhost:33062', {'safeClone': true})
// 注意:如果节点已宕机且无法恢复,使用 force 模式:
// cluster.removeInstance('root@localhost:33062', {'force': true})
```
### 切换主节点
```javascript
// 手动将某个 Secondary 提升为 Primary
cluster.switchToMultiPrimaryMode() // 切换到多主模式
cluster.switchToSinglePrimaryMode() // 切回单主模式
// 在单主模式下指定哪个节点作为 Primary
cluster.setPrimaryInstance('root@localhost:33062')
```
### 升级集群
```javascript
// 滚动升级(逐个节点升级,不停服)
cluster.checkInstanceUpgrade() // 检查各节点是否满足升级条件
cluster.checkStackUpgrade() // 整体栈升级评估
```
## 生产注意事项
### 必备前置步骤
```bash
# 每个 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] 单机多实例测试技巧
> 开发阶段可以用一台机器跑三个实例,只需:
> 1. 每个实例独立的数据目录、端口、socket 文件
> 2. 独立的 `.cnf` 配置文件
> 3. `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` 需要合理设置连接池参数,避免在故障切换瞬间全部连接失效。
```go
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 接入
```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 在集群环境中的使用注意事项
@@ -0,0 +1,278 @@
---
tags: [MySQL, mysqldump, XtraBackup, PITR, 备份恢复]
create time: 2026-05-16 00:00
---
# 备份与恢复
## 概述
备份是数据安全最后一道防线。本章覆盖逻辑备份(mysqldump)、物理备份(Percona XtraBackup)、以及基于 binlog 的时间点恢复(PITR)三大核心方案。
## 备份策略全景
```mermaid
flowchart LR
subgraph "逻辑备份"
LD["mysqldump<br/>导出 SQL"]
LF["全量 + 增量<br/>依赖 binlog"]
end
subgraph "物理备份"
PD["Percona XtraBackup<br/>热拷贝ibd文件"]
PF["压缩 + 加密<br/>支持部分备份"]
end
subgraph "选择指南"
LS["数据量 < 50GB<br/>停机窗口可接受"]
PS["数据量 > 50GB<br/>需要 0 停机"]
end
LD --> LS
PD --> PS
style LS fill:#00B6BC,color:#fff
style PS fill:#00D866,color:#fff
```
## mysqldump 逻辑备份
### 全量备份
```bash
# 最基础的全量备份
mysqldump -u root -p --all-databases > all_dbs_$(date +%F).sql
# 推荐的参数组合(保持一致性和完整性)
mysqldump \
-u root -p \
--single-transaction # MVCC 快照,不锁表
--flush-logs # 切换 binlog,便于后续恢复
--master-data=2 # 记录 binlog position(注释形式)
--routines # 包含存储过程和函数
--triggers # 包含触发器
--events # 包含事件调度器
--databases app_db report_db > backup_$(date +%F).sql
```
### 还原
```bash
# 全量还原
mysql -u root -p < backup_2026-05-16.sql
# 只还原某个表(先导出再导入)
mysqldump -u root -p app_db --table users > users.sql
mysql -u root -p app_db < users.sql
```
### mysqldump 的优缺点
| 优点 | 缺点 |
|------|------|
| 简单可靠,几乎不会出错 | 大库慢(导入/导出)|
| 文本格式,可读可编辑 | 导出的数据和原始可能有差异(如字符集转换)|
| 自带权限和表结构定义 | 不支持增量备份 |
| 跨版本迁移方便 | 备份期间虽然有 `--single-transaction`,但 `--flush-logs` 仍需短暂锁表 |
## Percona XtraBackup(物理备份)
### 全量备份
```bash
# 全量备份(热备,不停机)
xtrabackup --backup \
--target-dir=/data/backups/full/ \
--user=root --password=xxx \
--parallel=4 # 多线程并行拷贝
# 备份后的准备阶段(使备份可用于恢复)
xtrabackup --prepare --target-dir=/data/backups/full/
# 增量备份(基于上一次备份)
xtrabackup --backup \
--target-dir=/data/backups/inc1/ \
--incremental-basedir=/data/backups/full/ \
--user=root --password=xxx
# 合并增量到全量(减少恢复时间)
# 先对全量做 prepare,只应用已提交的事务,不回滚未提交的
xtrabackup --prepare --target-dir=/data/backups/full/ --apply-only
# 再将增量备份合并进来
xtrabackup --prepare --target-dir=/data/backups/full/ --incremental-dir=/data/backups/inc1/
# 如果有多个增量层,依次合并
xtrabackup --prepare --target-dir=/data/backups/full/ --incremental-dir=/data/backups/inc2/
```
### 恢复
```bash
# 1. 停止 MySQL
systemctl stop mysqld
# 2. 清空数据目录(或用新目录)
rm -rf /var/lib/mysql/*
# 3. 拷贝备份数据
cp -a /data/backups/full/. /var/lib/mysql/
# 4. 设置权限
chown -R mysql:mysql /var/lib/mysql
# 5. 启动 MySQL
systemctl start mysqld
```
### XtraBackup 的优缺点
| 优点 | 缺点 |
|------|------|
| **热备份**:备份期间业务不受影响 | 学习曲线较陡 |
| 速度快:直接拷贝 ibd 文件 | 需要安装额外软件 |
| 支持增量备份 | 不能跨 major version(8.0→8.0)|
| 恢复速度远快于 mysqldump | 占用较多磁盘空间(物理拷贝)|
## PITR — 时间点恢复
结合全量备份 + binlog,可以将数据库恢复到任意精确时刻。
> [!QUESTION] 思考:如果凌晨 3 点的备份完成时 binlog position 是 `mysql-bin.000005:1234`,而 10 点误操作发生在 `mysql-bin.000006` 中,该怎么办?
> 答案是:binlog 链不能断——从 `mysql-bin.000005:1234` 开始到当前为止的所有 binlog 文件都必须保留。任何缺失都会导致 PITR 失败。
```mermaid
timeline
title PITR 恢复流程
00:00 : 凌晨全量备份 (XtraBackup)
06:00 : 应用正常运行
10:00 : ⚠️ 误删表!DELETE FROM users;
10:01 : 检测到异常,开始恢复
恢复过程 : 1. 恢复全量备份到 00:00
: 2. 重放 binlog 到 09:59:59
: 3. 跳过误操作语句
: 4. 恢复到 10:00 之前的状态
```
```bash
# 第一步:恢复全量备份
# (前面已讲过 XtraBackup 恢复步骤)
# 第二步:找到误操作的准确位置
# 方法 A:用时间定位(推荐,最直观)
mysqlbinlog --stop-datetime='2026-05-16 09:59:59' /var/lib/mysql/mysql-bin.000006 > safe_events.sql
# 方法 B:搜索误操作语句,获取 position
mysqlbinlog /var/lib/mysql/mysql-bin.000006 | grep -B5 "DROP DATABASE\|DELETE FROM users"
# 找到对应的 DELETE 所在事务的 stop_position,在此之前停止
# 第三步:重放 binlog 到误操作之前(基于 position)
mysqlbinlog \
--start-position=1234 \
--stop-position=4567 \
/var/lib/mysql/mysql-bin.000005 > before_incident.sql
# 第四步:跳过误操作行,手动编辑 SQL 后重放其余部分
# 方法一:导出到文件后用 sed/grep 过滤掉误操作的 DDL/DML
mysqlbinlog /var/lib/mysql/mysql-bin.000005 > all_events.sql
# 用编辑器打开 all_events.sql,定位到误操作的 BEGIN/COMMIT 区间并删除
# 方法二:精确用 position 范围分段重放
mysqlbinlog \
--stop-position=4567 \
/var/lib/mysql/mysql-bin.000005 | mysql -u root -p # 重放到误操作前
mysqlbinlog \
--start-position=5000 \
/var/lib/mysql/mysql-bin.000005 | mysql -u root -p # 从安全点继续重放
# MySQL 8.0+ GTID 模式(推荐,更简洁)
# 包含 uuid 下 1~100 的 GTID,排除第 101 个事务(即误操作的那个)
mysqlbinlog \
--include-gtids='uuid:1-100' \
--exclude-gtids='uuid:101' \
mysql-bin.000005 | mysql -u root -p
```
> [!WARNING] PITR 的关键约束
> - **必须有完整的 binlog 链**:全量备份时的 binlog position 之后所有的 binlog 都不能丢
> - **`binlog_format` 必须是 ROW 或 MIXED**:STATEMENT 模式下 PITR 不可靠(相同的 SQL 在不同上下文中可能产生不同结果)
> - **GTID 需要 `gtid_executed` 一致**:使用 `--include-gtids` / `--exclude-gtids` 要求服务端启用了 `enforce_gtid_consistency=ON`
> - **恢复到某个时间点会覆盖该时间点之后的所有数据**:做好评估,建议先在测试库验证恢复流程
## 备份验证与自动化
> [!QUESTION] 思考:如果一周后发生灾难,你发现昨晚的备份根本打不开——那时候还来得及补救吗?
> **未经测试的备份等于没有备份**。定期验证是备份策略中最重要的环节。
### 验证方法
```bash
# mysqldump SQL 文件验证:在测试库导入一次
mysql -u root -p test_db < backup_latest.sql
# 检查关键表的数据量和行数是否符合预期
# XtraBackup 验证:prepare 阶段就是第一次验证
xtrabackup --prepare --target-dir=/data/backups/latest/
# prepare 成功 = 备份文件完整性合格
# 更进一步:恢复到从库或测试机做一次完整演练(至少每季度一次)
```
### 自动化方案
```bash
#!/bin/bash
# /opt/scripts/mysql-backup.sh — 每日备份脚本
set -euo pipefail
BACKUP_DIR="/data/backups"
DATE=$(date +%F)
RETENTION_DAYS=7
# 1. XtraBackup 全量备份
xtrabackup --backup \
--target-dir="${BACKUP_DIR}/${DATE}/" \
--user=backup_user --password=$(cat /etc/backup_pass) \
--parallel=4
# 2. 压缩备份目录(节省磁盘空间)
tar czf "${BACKUP_DIR}/${DATE}.tar.gz" -C "${BACKUP_DIR}" "${DATE}"
rm -rf "${BACKUP_DIR}/${DATE}"
# 3. 清理超过保留期的旧备份
find "${BACKUP_DIR}" -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete
# 4. 备份元数据记录(方便追踪和恢复时定位)
echo "$(date +%F %T) | full | ${DATE} | OK" >> "${BACKUP_DIR}/backup.log"
```
```cron
# crontab -e — 每天凌晨 2 点执行
0 2 * * * /opt/scripts/mysql-backup.sh >> /var/log/mysql-backup-cron.log 2>&1
```
### 快速恢复清单
> [!CHECKLIST] PITR 快速恢复步骤
> 1. [ ] 确认误操作的准确时间
> 2. [ ] 找到最新可用的全量备份及其 binlog position
> 3. [ ] 停止应用写操作(防止二次损害)
> 4. [ ] 恢复全量备份到干净目录
> 5. [ ] 用 `--stop-datetime` 重放 binlog 到误操作前
> 6. [ ] 启动 MySQL 并验证关键表数据
> 7. [ ] 切换流量到新实例
> 8. [ ] 通知相关人员,更新 incident 记录
## 备份频率建议
| 数据类型 | 全量备份频率 | 增量备份频率 | binlog 留存 |
|---------|-----------|-----------|-----------|
| 核心交易数据 | 每天一次 | 每小时 | ≥ 7 天 |
| 用户数据 | 每天一次 | 每天 | ≥ 7 天 |
| 日志/归档数据 | 每周一次 | 按需 | 按需 |
| 测试环境 | 按需 | — | 按需 |
## 关联笔记
- [[hhs/MySQL/07-高可用与分布式/29-Binary Log]] — Binlog 是 PITR 的基础
- [[hhs/MySQL/07-高可用与分布式/30-主从复制详解]] — 从库可以作为只读备份节点
- [[hhs/DEV/Go-Database]] — Go 应用中处理数据库恢复错误的最佳实践
@@ -0,0 +1,25 @@
---
tags: [MySQL, 主从复制, 高可用, Binlog, 集群]
create time: 2026-05-20 23:55
---
# 七、高可用与分布式
## 概述
单台 MySQL 扛不住了怎么办?本章从 Binlog 原理讲起,覆盖主从复制、自动故障切换(MHA / Orchestrator)、Group Replication、InnoDB Cluster 以及备份恢复策略——这些是保障生产环境数据库可靠性的必备知识。
## 本章文档
| # | 主题 | 说明 |
|---|------|------|
| 29 | [[hhs/MySQL/07-高可用与分布式/29-Binary Log]] | Binlog 记录了所有写操作,主从复制和恢复都靠它 |
| 30 | [[hhs/MySQL/07-高可用与分布式/30-主从复制详解]] | 数据怎么从主库同步到从库、半同步 vs 异步的区别 |
| 31 | [[hhs/MySQL/07-高可用与分布式/31-MHA与Orchestrator]] | 主库挂了怎么自动切换到从库 |
| 32 | [[hhs/MySQL/07-高可用与分布式/32-Group Replication]] | 多主同时写入、冲突怎么处理 |
| 33 | [[hhs/MySQL/07-高可用与分布式/33-InnoDB Cluster]] | MySQL 官方的高可用集群方案 |
| 34 | [[hhs/MySQL/07-高可用与分布式/34-备份与恢复]] | 全量备份和增量备份、恢复到任意时间点 |
## 关联笔记
- [[hhs/MySQL/README]] — MySQL 知识库总目录