419 lines
15 KiB
Markdown
419 lines
15 KiB
Markdown
|
|
---
|
|||
|
|
tags: [MySQL, 主从复制, Replication, Semi-Sync, GTID]
|
|||
|
|
create time: 2026-05-16 10:30
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 主从复制
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
MySQL 主从复制是通过 Binlog 将主库的数据变更传播到从库的过程。它是高可用架构的基础组件,也是读写分离的前提。
|
|||
|
|
|
|||
|
|
> [!tip] 生活中的类比
|
|||
|
|
> 想象主库是一位「主播」,从库是「观众」。主播做了一个操作(比如画画),观众通过直播画面(Binlog)看到这个操作,然后在自己的画布上**照着做一遍**。
|
|||
|
|
>
|
|||
|
|
> 这个"看到 → 照做"的过程,就是主从复制的本质。
|
|||
|
|
|
|||
|
|
> [!question] 为什么需要主从复制?
|
|||
|
|
> 一台 MySQL 能撑多久?当读请求暴增时,单机性能到顶了怎么办?
|
|||
|
|
> 主从复制给出了两个关键答案:**读写分离**(主库写、从库读)和**高可用**(主库挂了,从库顶上)。
|
|||
|
|
|
|||
|
|
## 复制架构
|
|||
|
|
|
|||
|
|
复制的过程可以简化为三个步骤:**主库记录变更** → **网络传输** → **从库重放变更**。
|
|||
|
|
|
|||
|
|
```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 中继日志"]
|
|||
|
|
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 重放应用到本地数据库 |
|
|||
|
|
|
|||
|
|
> [!question] 为什么要多一个 Relay Log,而不是直接在从库上执行?
|
|||
|
|
> 其实这和消息队列的「削峰填谷」是一个道理。Relay Log 起到了**缓冲区**的作用:即使从库暂时卡住了(比如正在做慢查询),主库发来的 Binlog 也不会丢失,先暂存在 Relay Log 里,等从库空闲了再慢慢回放。
|
|||
|
|
|
|||
|
|
## 同步模式
|
|||
|
|
|
|||
|
|
复制的「同步」和「异步」,核心问题只有一个:**主库写完后,要不要等从库确认?**
|
|||
|
|
|
|||
|
|
| 模式 | 等不等从库? | 数据安全 | 性能 |
|
|||
|
|
|------|------------|---------|------|
|
|||
|
|
| **异步** | 不等 | 低(可能丢数据) | 最高 |
|
|||
|
|
| **半同步** | 至少等 1 个 | 中(大幅降低丢数据概率) | 中 |
|
|||
|
|
| **组复制** | 等多数派共识 | 高 | 较低 |
|
|||
|
|
|
|||
|
|
下面的流程图展示了三种模式的完整过程:
|
|||
|
|
|
|||
|
|
```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', -- 主库 IP
|
|||
|
|
SOURCE_USER='repl_user', -- 专用的复制账号
|
|||
|
|
SOURCE_PASSWORD='password',
|
|||
|
|
SOURCE_PORT=3306,
|
|||
|
|
SOURCE_AUTO_POSITION = 1; -- GTID 模式:自动找到正确的起始位置
|
|||
|
|
|
|||
|
|
START REPLICA; -- 启动复制!
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!tip] 关键参数 `SOURCE_AUTO_POSITION`
|
|||
|
|
> 设为 `1` 表示开启 GTID 自动定位——从库会自动告诉主库"我已经有哪些事务了",主库从缺失的部分开始发送。再也不用手动查 Binlog 文件和偏移量了。
|
|||
|
|
|
|||
|
|
> [!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 秒收不到 ACK 就降级为异步(保护性能)
|
|||
|
|
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 复制。
|
|||
|
|
|
|||
|
|
> [!question] 为什么传统 Position 复制让人抓狂?
|
|||
|
|
> 传统模式下,从库靠 `mysql-bin.000003:154`(文件名 + 偏移量)来记录复制进度。
|
|||
|
|
> 想象一下:主库挂了,你要把从库切到新主库上,但新旧主库的 Binlog 文件名和偏移量完全不同——你得**手动计算**该从哪个位置继续复制。
|
|||
|
|
>
|
|||
|
|
> GTID 的思路就简单多了:给每个事务贴一个「身份证号」。无论主库怎么切换,从库只要说"我已经执行到 23 号事务了",新主库就知道该从 24 号开始发。
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
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"]
|
|||
|
|
B --> C["写入 Binlog 事件, 带 GTID 标记"]
|
|||
|
|
C --> D["Binlog Dump 线程发送 GTID 和事件到 Slave"]
|
|||
|
|
D --> E["Slave IO Thread 写入 Relay Log"]
|
|||
|
|
E --> F{"GTID Set 已包含此 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 靠「页码」定位,GTID 靠「身份证号」定位。**
|
|||
|
|
|
|||
|
|
| 特性 | Position-based | GTID-based |
|
|||
|
|
|------|---------------|------------|
|
|||
|
|
| **定位方式** | 文件名 + position 号(像书签) | GTID set(像身份证号集合) |
|
|||
|
|
| **跨库切换** | 需要手动查 position | 自动定位 |
|
|||
|
|
| **跳过事务** | 困难 | `SET gtid_next = '...'; BEGIN; COMMIT; SET gtid_next = AUTOMATIC;` |
|
|||
|
|
| **一致性检查** | 手动比对 | `pt-table-checksum` + GTID 验证 |
|
|||
|
|
|
|||
|
|
> [!tip] 实际建议
|
|||
|
|
> MySQL 8.0 中 GTID 已经非常成熟,**新项目强烈建议默认开启**。传统 Position 模式仅在兼容老版本时使用。
|
|||
|
|
|
|||
|
|
在主库和从库的 `my.cnf` 中添加以下配置即可启用 GTID:
|
|||
|
|
|
|||
|
|
```ini
|
|||
|
|
# 启用 GTID
|
|||
|
|
gtid_mode = ON # 开启 GTID
|
|||
|
|
enforce_gtid_consistency = ON # 强制一致性:禁止不安全的 SQL 写法
|
|||
|
|
master_info_repository = TABLE # 复制元数据存到系统表,比文件更可靠
|
|||
|
|
relay_log_info_repository = TABLE
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!tip] 热知识
|
|||
|
|
> MySQL 8.0 中 `master_info_repository` 和 `relay_log_info_repository` 默认就是 TABLE,无需手动配置。
|
|||
|
|
|
|||
|
|
## 并行复制(Multithreaded Slave, MTS)
|
|||
|
|
|
|||
|
|
前面提到 SQL Thread 负责重放 Relay Log。问题来了——默认只有一个 SQL Thread,所有事务都**排队串行**执行。如果主库很忙,从库就很容易「越落越远」。
|
|||
|
|
|
|||
|
|
> [!tip] 用快递仓库来理解并行复制
|
|||
|
|
> 串行回放 = 一个分拣员按顺序处理所有包裹。
|
|||
|
|
> 并行回放 = 多个分拣员**同时**处理,只要包裹之间没有依赖关系(比如不会同时操作同一行),就可以并行。
|
|||
|
|
|
|||
|
|
MySQL 5.6+ 引入从库并行回放,大幅提升复制延迟处理能力。
|
|||
|
|
|
|||
|
|
在从库配置中开启并行复制:
|
|||
|
|
|
|||
|
|
```ini
|
|||
|
|
# MySQL 5.x(已弃用)
|
|||
|
|
# slave_parallel_type = LOGICAL_CLOCK
|
|||
|
|
# slave_parallel_workers = 8
|
|||
|
|
|
|||
|
|
# ✅ MySQL 8.0+ 标准命名
|
|||
|
|
replica_parallel_type = LOGICAL_CLOCK # 按事务依赖关系决定哪些可以并行
|
|||
|
|
replica_parallel_workers = 8 # 并发 worker 数量(建议 CPU 核数的一半)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!TIP] worker 数量设多少合适?
|
|||
|
|
> 不是越多越好。每个 worker 都占内存和 CPU,一般建议 **CPU 核数的一半**,上限不超过 16。设太多反而会因为锁竞争导致更慢。
|
|||
|
|
|
|||
|
|
### MTS 并行模式对比
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
subgraph "DATABASE 默认"
|
|||
|
|
D1["Relay Log 事件"] --> D2{"按 database 分桶"}
|
|||
|
|
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{"判断事务依赖关系"}
|
|||
|
|
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 时才能并行(避免表锁冲突)
|
|||
|
|
|
|||
|
|
## 监控复制状态
|
|||
|
|
|
|||
|
|
复制搭好了,日常巡检同样重要。最常见的关注点:**IO 线程和 SQL 线程是否在正常运行?从库落后主库多少秒?**
|
|||
|
|
|
|||
|
|
```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 线程断连**、**SQL 线程卡住**、**延迟越来越大**。下面逐一分析。
|
|||
|
|
|
|||
|
|
### IO 线程断开(Slave_IO_Running: No)
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
A["IO Thread 断连"] --> B{"原因定位"}
|
|||
|
|
B -->|"网络不通"| C["检查防火墙和安全组端口"]
|
|||
|
|
B -->|"Binlog 已过期"| D["purge binary logs"]
|
|||
|
|
B -->|"认证失败"| E["核对 user 权限和 password"]
|
|||
|
|
B -->|"Server UUID 冲突"| F["重置 slave server_uuid"]
|
|||
|
|
C --> G["CHANGE REPLICATION SOURCE TO"]
|
|||
|
|
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'; -- 指定要跳过的 GTID
|
|||
|
|
BEGIN; COMMIT; -- 提交一个空事务, 标记该 GTID 为已执行
|
|||
|
|
SET GTID_NEXT='AUTOMATIC'; -- 恢复自动分配 GTID
|
|||
|
|
|
|||
|
|
-- 如果需要从头重建从库的 GTID 记录(慎用!)
|
|||
|
|
STOP REPLICA;
|
|||
|
|
SET GLOBAL gtid_purged = '3E11FA47-71CA-11E1-9E33-C80AA9429562:1-100'; -- 声明从库已拥有这些 GTID
|
|||
|
|
START REPLICA;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 延迟过大如何诊断
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
A["Seconds_Behind_Master 大"] --> B{"IO 延迟还是 SQL 延迟"}
|
|||
|
|
B -->|"Relay_Log_Space 持续增长"| C["SQL Worker 回放太慢, 增加 replica_parallel_workers"]
|
|||
|
|
B -->|"Read_Master_Log_Pos 跟丢"| D["网络带宽不足或 Master 写入量太大"]
|
|||
|
|
B -->|"DDL 执行中"| E["大表 ALTER TABLE 阻塞, 使用 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 多主复制进阶方案
|