主从复制

返回 数据库基础

主从复制(Replication)是 MySQL 高可用与读写分离的基础:主库(Master)处理写入,变更同步到一个或多个从库(Replica),从库提供读服务。


复制原理

主库(Master)                          从库(Replica)
┌─────────────────────┐               ┌─────────────────────────────┐
│  Client 写入        │               │  IO Thread                  │
│    ↓                │               │    连接主库,拉取 binlog      │
│  执行 SQL → 提交事务 │               │    写入本地 Relay Log         │
│    ↓                │  binlog 流    │         ↓                    │
│  写入 binlog ──────►│──────────────►│  SQL Thread                  │
└─────────────────────┘               │    重放 Relay Log 中的 SQL   │
                                      │    更新从库数据               │
                                      └─────────────────────────────┘

三个线程:

线程所在节点职责
Dump Thread主库监听从库连接,推送 binlog 事件
IO Thread从库连接主库,接收 binlog → 写 Relay Log
SQL Thread从库读取 Relay Log,重放 SQL 更新本地数据

binlog 三种格式

格式记录内容优点缺点
STATEMENT原始 SQL 语句日志体积小NOW()UUID() 等非确定性函数在从库执行结果不同,数据不一致
ROW每行数据的前后值数据精确,CDC 工具必须用此模式大事务时日志体积极大(如 DELETE 100万行
MIXED默认 STATEMENT,不安全场景自动切 ROW兼顾体积与安全行为复杂,排查问题困难

生产推荐 ROW 模式,配合 binlog_row_image=FULL,并压缩 binlog。

# my.cnf
binlog_format = ROW
binlog_row_image = FULL
sync_binlog = 1          # 每次事务提交都刷盘,防止主库宕机丢失 binlog
innodb_flush_log_at_trx_commit = 1  # redo log 同步刷盘,双一配置

三种同步模式

模式流程数据安全性能
异步复制(默认)主库提交后立即返回,不等从库确认主库宕机可能丢失已提交事务
半同步复制主库等待至少一个从库写入 Relay Log 后才返回至少一份副本,安全性高中(主库有等待延迟)
增强半同步(After Sync)主库 binlog 刷盘 → 从库 ACK → 主库提交最安全,从库先确认较低
-- 安装半同步插件(主库)
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 1000;  -- ACK 超时 1s,退化为异步
 
-- 从库
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;

GTID 复制(推荐)

GTID(Global Transaction ID):每个事务在全局唯一标识,格式 server_uuid:transaction_id

优点:

  • 从库重新指向新主库时,无需手动指定 binlog 文件名和位点(CHANGE MASTER TO 自动处理)
  • 轻松实现主从切换和故障恢复
# my.cnf(主从都要开)
gtid_mode = ON
enforce_gtid_consistency = ON
-- 基于 GTID 的从库配置
CHANGE MASTER TO
  MASTER_HOST = '192.168.1.10',
  MASTER_USER = 'replica',
  MASTER_PASSWORD = 'password',
  MASTER_AUTO_POSITION = 1;  -- 自动基于 GTID,无需指定 binlog 文件和位点
 
START SLAVE;
SHOW SLAVE STATUS\G

主从延迟

产生原因

主库:多线程并发写入,binlog 产生快
从库:SQL Thread 默认单线程重放,无法并发

主库写入量 > 从库重放速度 → 延迟累积

常见触发场景

  • 主库大事务(批量 UPDATE/DELETE 百万行)
  • 主库 DDL(ALTER TABLE 长时间锁表后的大量积压)
  • 从库服务器性能低于主库
  • 网络抖动导致 IO Thread 重连

查看延迟

SHOW SLAVE STATUS\G
-- Seconds_Behind_Master: 30   ← 延迟 30s
-- Relay_Master_Log_File / Exec_Master_Log_Pos  ← 对比主库位点

解决方案

方案说明
并行复制(MTS)slave_parallel_workers = 8(MySQL 5.7+ 支持按事务/按 WRITESET 并行重放)
WRITESET 并行slave_parallel_type = WRITESET(MySQL 8.0+,最优,无依赖关系的事务并行执行)
避免大事务批量操作分批处理(每批 1000 行),减少单事务 binlog 体积
从库升配提升从库 CPU/磁盘 IO 性能,匹配主库写入速度
半同步超时降级半同步场景监控 rpl_semi_sync_master_no_tx,延迟高时及时告警
# my.cnf(从库)
slave_parallel_type = WRITESET
slave_parallel_workers = 8
slave_preserve_commit_order = 1  # 保证事务提交顺序一致

读写分离

主库写,从库读,分散压力:

应用
├── 写操作(INSERT/UPDATE/DELETE)→ 主库
└── 读操作(SELECT) → 从库1 / 从库2 / 从库3(负载均衡)

实现方案

方案说明
应用层路由代码中配置主从数据源,手动指定;灵活但侵入业务代码
中间件代理MyCat / ShardingSphere-Proxy / ProxySQL,应用无感知
Spring AbstractRoutingDataSource动态数据源,结合 @ReadOnly 注解切换

读写分离一致性问题

主从延迟期间,从库读到旧数据

场景处理策略
写后立即读(如下单后查订单)强制路由到主库(@Primary / 事务内读)
普通查询允许轻微延迟,走从库
关键一致性读读主库或等待 WAIT_FOR_EXECUTED_GTID_SET
// Spring 读写分离示例(AbstractRoutingDataSource)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {}
 
@Aspect
public class DataSourceAspect {
    @Before("@annotation(ReadOnly)")
    public void setReadDataSource() {
        DataSourceContextHolder.set("replica");
    }
    @After("@annotation(ReadOnly)")
    public void clearDataSource() {
        DataSourceContextHolder.clear();
    }
}

高可用架构

一主一从(最简单)

Client → Master(读写)
              └── Replica(读 + 备份)

一主多从

Client → Master(写)
              ├── Replica-1(读)
              ├── Replica-2(读)
              └── Replica-3(备份/异地容灾)

MHA(Master High Availability)

主库宕机时,MHA 自动选举延迟最小的从库提升为主库,并修复其他从库的复制链路:

MHA Manager(监控主库心跳)
       │ 主库宕机
       ▼
选举延迟最小的 Replica → 提升为新 Master
更新其他 Replica 的 CHANGE MASTER TO → 指向新 Master

MySQL InnoDB Cluster(官方方案)

基于 Group Replication(组复制),自动选主、自动故障恢复,支持多主模式。


常用命令

-- 主库:查看 binlog 状态
SHOW MASTER STATUS;
SHOW BINARY LOGS;
 
-- 从库:查看复制状态(重点关注 Seconds_Behind_Master)
SHOW SLAVE STATUS\G
 
-- 从库:暂停/启动复制
STOP SLAVE;
START SLAVE;
 
-- 跳过一个错误事件(临时处理复制错误,慎用)
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;
 
-- 基于 GTID 跳过指定事务(更精确)
STOP SLAVE;
SET GTID_NEXT = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:N';
BEGIN; COMMIT;
SET GTID_NEXT = 'AUTOMATIC';
START SLAVE;

相关

  • MySQL — binlog 详解、InnoDB redo/undo log
  • 数据库事务 — 主从一致性与事务隔离
  • 分库分表 — 主从读写分离之上的进一步扩展
  • CDC — binlog 解析,Canal/Debezium 基于主从复制机制