1. 复制架构
┌──────────────┐
│ Client │
└──────┬───────┘
│
┌─────▼─────┐
│ Master │ ← 处理写请求,记录 binlog
│ (主库) │
└─────┬─────┘
│ binlog dump
┌─────────────┼─────────────┐
↓ ↓ ↓
┌────────┐ ┌────────┐ ┌────────┐
│ Slave 1│ │ Slave 2│ │ Slave 3│ ← 读请求
│(IO线程) │ │(IO线程) │ │(IO线程) │
│(SQL线程)│ │(SQL线程)│ │(SQL线程)│
└────────┘ └────────┘ └────────┘
2. 复制模式
2.1 异步复制(Asynchronous)
Client → Master (写入成功) → 立即返回
↓ 后台异步
Slave(可能有延迟)
特点:快,不阻塞客户端,但 Master 宕机时可能丢失未同步数据
MySQL 默认模式
2.2 半同步复制(Semi-Synchronous)
Client → Master (prepare) → 等待至少 1 个 Slave ACK
↓
Slave (收到 relay log)
→ ACK → Master commit → 返回客户端
特点:返回慢一些,但保证至少一个 Slave 已接收数据,降低丢失风险
2.3 组复制(Group Replication)
多主或单主模式,多数派确认后提交(Paxos 协议)。
自动故障检测和切换,数据强一致。
MySQL 5.7+ 内置。
| 模式 | 一致性 | 性能 | 故障场景数据丢失 |
|---|---|---|---|
| 异步 | 最终一致 | 最高 | 可能丢失所有未同步 |
| 半同步 | 至少一个副本 | 中 | 最多丢失一个事务 |
| 组复制 | 强一致 | 较低 | 不丢失(多数派确认) |
3. Binlog
3.1 格式
| 格式 | 内容 | 优点 | 缺点 |
|---|---|---|---|
| STATEMENT | 原始 SQL 语句 | 体积小 | 非确定性函数(UUID/ NOW())问题 |
| ROW | 行变更前后镜像 | 精确、安全 | 体积大(UPDATE 全表时膨胀) |
| MIXED | 混合策略 | 平衡 | 复杂 |
-- 推荐配置(ROW 格式)
SET GLOBAL binlog_format = 'ROW';
SET GLOBAL binlog_row_image = 'FULL'; -- FULL / MINIMAL / NOBLOB
-- ROW 格式示例
-- UPDATE users SET status = 'active' WHERE id = 1
-- binlog 记录:
-- before_image: id=1, status='inactive'
-- after_image: id=1, status='active'
3.2 GTID(全局事务标识)
GTID = Master_UUID:Transaction_Sequence
示例:
a1b2c3d4-e5f6-7890-abcd-ef1234567890:1-100
优势:
- 自动定位复制位点,避免 file:position 管理
- 故障切换更简单(CHANGE MASTER TO ... AUTO_POSITION=1)
- 避免跳过事务导致的脑裂
4. 复制延迟
4.1 延迟原因
| 原因 | 说明 |
|---|---|
| 单线程 SQL 线程 | 经典 MySQL 只有 1 个 SQL 线程重放 |
| 大事务 | 一个事务修改大量行,Slave 重放慢 |
| Slave 性能差 | Slave 硬件弱于 Master |
| 锁竞争 | Slave 上也有读请求,与 SQL 线程竞争 |
4.2 并行复制
-- MySQL 5.7+ 并行复制配置
SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK';
SET GLOBAL slave_parallel_workers = 8; -- SQL 线程数
-- LOGICAL_CLOCK:基于组提交(同一时刻提交的事务可并行)
-- DATABASE:基于库级别并行(不同库的事务并行)
4.3 监控延迟
-- 查看延迟
SHOW SLAVE STATUS\G
-- Seconds_Behind_Master: 0 ← 延迟秒数
-- 更精确的方法(pt-heartbeat)
-- Master 每秒写 heartbeat 表,Slave 计算差值
5. 复制延迟的根因分析与排查方法论
生产环境中复制延迟是最常见的故障之一。排查时应遵循「三步定位法」:
第一步:确认延迟程度
SHOW SLAVE STATUS\G
-- 关注 Seconds_Behind_Master(估算延迟秒数)
-- Slave_SQL_Running_State(SQL 线程当前状态)
第二步:定位瓶颈环节
- IO 线程慢:网络带宽不足、
sync_binlog=1导致频繁 fsync - SQL 线程慢:单线程重放、大事务、表锁冲突、Slave 硬件性能差
第三步:针对性优化
- 网络层:专线通信、压缩传输(
slave_compressed_protocol=1) - 硬件层:Slave 使用 SSD、增加 IO 容量
- 配置层:启用并行复制、调整
innodb_flush_log_at_trx_commit=2
更精确的延迟测量应使用 pt-heartbeat 工具:Master 每秒更新 heartbeat 表,Slave 计算时间差值,避免 Seconds_Behind_Master 在空闲时显示为 0 的假象。
6. 读写分离的实践方案
| 方案 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| ProxySQL | 中间件代理 | 透明、支持查询路由规则 | 增加一层网络延迟 |
| MySQL Router | 官方中间件 | 与 MGR 深度集成 | 功能相对简单 |
| 应用层拆分 | 代码中区分读写数据源 | 最灵活、零中间件依赖 | 代码侵入性强 |
7. 故障自动切换架构
| 方案 | 自动切换 | 数据一致性 | 适用场景 |
|---|---|---|---|
| MHA | ✅ | 可能丢少量数据 | 传统 MySQL 主从 |
| Orchestrator | ✅ | 可配置 | 大规模拓扑管理 |
| MGR | ✅ 内置 | 强一致(多数派) | MySQL 5.7+ 推荐 |
8. 复制链与拓扑监控
8.1 多层级联复制架构
┌─────────┐
│ Master │
└────┬────┘
│ binlog
┌────┴────┐
│ Relay │ ← 级联中继 Slave(log-slave-updates=ON)
│ Slave │
└────┬────┘
│ binlog
┌─────────┼─────────┐
↓ ↓ ↓
┌────────┐┌────────┐┌────────┐
│ Read 1 ││ Read 2 ││ Read 3 │
└────────┘└────────┘└────────┘
级联复制(Cascading Replication)适用于跨地域部署场景:主库仅需向一个中继节点发送 binlog,由中继节点分发给各区域的只读副本,显著降低主库的网络负载。
关键配置:
-- 中继节点必须开启 log-slave-updates,否则不会转发 binlog
SET GLOBAL log_slave_updates = ON;
-- 中继节点同样需要 binlog 格式为 ROW
SET GLOBAL binlog_format = 'ROW';
8.2 复制延迟监控指标体系
| 指标名 | 获取方式 | 正常范围 | 异常阈值 |
|---|---|---|---|
| Seconds_Behind_Master | SHOW SLAVE STATUS | 0-1s | > 5s |
| Slave_IO_State | SHOW SLAVE STATUS | “Waiting for master…” | “Connecting to master…” |
| Master_Log_File / Read_Master_Log_Pos | SHOW SLAVE STATUS | 接近主库 binlog 位置 | 差距 > 1GB |
| Exec_Master_Log_Pos | SHOW SLAVE STATUS | 接近 Read_Master_Log_Pos | 差距 > 100MB |
| Slave_SQL_Running_State | SHOW SLAVE STATUS | 显示正常状态 | “Waiting for dependent transaction…” |
更精准的监控应结合 performance_schema:
-- MySQL 8.0 复制性能监控
SELECT
WORKER_ID,
LAST_SEEN_TRANSACTION,
LAST_ERROR_NUMBER,
LAST_ERROR_MESSAGE
FROM performance_schema.replication_applier_status_by_worker;
-- 复制延迟精确测量(pt-heartbeat)
SELECT
round(MAX(age), 2) AS max_replication_lag_seconds
FROM percona.heartbeat;
8.3 binlog 刷盘策略与数据安全
-- sync_binlog 配置对比
SET GLOBAL sync_binlog = 1; -- 每次事务都 fsync,最安全,性能最低
SET GLOBAL sync_binlog = 100; -- 每 100 个事务 fsync,平衡方案
SET GLOBAL sync_binlog = 0; -- 由 OS 决定刷盘,性能最高,宕机可能丢失
-- innodb_flush_log_at_trx_commit 配合
SET GLOBAL innodb_flush_log_at_trx_commit = 1; -- 最安全:每次事务 Redo Log 刷盘
-- 非核心读库可设为 2(每秒刷盘)以提升 Slave 重放性能
生产建议:
- 金融级主库:
sync_binlog=1+innodb_flush_log_at_trx_commit=1(双一配置) - 一般业务主库:
sync_binlog=1+innodb_flush_log_at_trx_commit=2 - 只读从库:
sync_binlog=0+innodb_flush_log_at_trx_commit=2(只读库不需要 binlog 持久化)
9. 云原生时代的复制架构演进
AWS Aurora、阿里云 PolarDB 等通过「共享存储 + 日志即数据」架构,将复制简化为只读节点回放 Redo Log:
- 写节点只管写日志到共享存储
- 读节点从共享存储读取日志并应用
- 复制延迟降低到毫秒级,不受事务大小影响
这种架构下,传统的 binlog-based 复制不再是性能瓶颈,开发者可以更专注于业务逻辑而非复制调优。然而,云原生数据库也存在 vendor lock-in 风险,迁移成本需要考虑。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。