This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/MySQL/30-主从复制详解.md
T
2026-05-17 00:06:11 +08:00

11 KiB
Raw Blame History

tags, create time
tags create time
MySQL
主从复制
Replication
Semi-Sync
GTID
2026-05-16 10:30

主从复制

概述

MySQL 主从复制是通过 Binlog 将主库的数据变更传播到从库的过程。它是高可用架构的基础组件,也是读写分离的前提。

复制架构

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 重放应用到本地数据库

同步模式

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。

-- 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 才返回写成功。

# 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  # 刷盘后再等待
# 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 复制流程

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 验证
# 启用 GTID
gtid_mode = ON
enforce_gtid_consistency = ON
master_info_repository = TABLE     # 复制信息存在表中而非文件
relay_log_info_repository = TABLE

并行复制(Multithreaded Slave, MTS)

MySQL 5.6+ 引入从库并行回放,大幅提升复制延迟处理能力。

# 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 并行模式对比

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。 区别仅在于如何把连续事件流切分成可并行的逻辑组。

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 时才能并行(避免表锁冲突)

监控复制状态

-- 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 线程
-- 实时延迟监控(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)

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

常见排查步骤:

-- 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 可能导致数据静默丢失。必须先确认事务内容。

-- ⚠️ 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;

延迟过大如何诊断

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

关联笔记