一、什么是数据同步
数据同步是指把一份数据从一个存储位置复制到另一个存储位置,并保证两端数据在内容上保持一致的过程。在真实业务中,数据同步几乎无处不在:主从复制保证高可用、读写分离分摊压力、异地机房做容灾、数据仓库做离线分析、缓存与数据库之间保持一致,背后都离不开数据同步。
从同步方式上看,常见的数据同步可以分为几类:
- 全量同步:一次性把源端的全部数据复制到目标端,适合初始化场景,但数据量大时耗时长、对源库压力大。我们在做全量同步的时候,尽量的选择到一个用户使用量较低的时段进行一个同步,或者对于一些大型的项目,比如我们市面上常见的一些大型游戏,他们会选择一个时间进行停服处理,在处理完毕后会重新开放给用户。
- 增量同步:只同步发生变化的数据,通常依赖日志或时间戳,是生产环境的主流方式。这是我们常见的一个方案,通常在那些集群中会经常用到,用于同步各个集群之间的数据,以及主节点从节点之间的数据。
- 实时同步:变化发生后毫秒到秒级即可到达目标端,依赖 binlog 等变更日志。
- 准实时/定时同步:按固定周期批量拉取,实现简单,但存在延迟。
从一致性上看,又可以分为强一致与最终一致。绝大多数互联网业务选择最终一致,用可接受的延迟换取吞吐和可用性。
二、MySQL 实现数据同步的核心基础:binlog
MySQL 实现数据同步的根基是 binlog(二进制日志)。它记录了数据库中所有引起数据变更的语句或行变更,是主从复制、数据订阅、CDC(Change Data Capture)的统一数据源。
2.1 binlog 的三种格式
| 格式 | 记录内容 | 优点 | 缺点 |
|---|---|---|---|
| STATEMENT | 记录原始 SQL 语句 | 日志量小 | 含 NOW()、UUID()、LIMIT 等不确定函数时主从不一致 |
| ROW | 记录每一行变更前后的值 | 一致性最好,可精确还原 | 日志量大,批量更新时膨胀明显 |
| MIXED | 由 MySQL 自动在两者间选择 | 兼顾体积与一致性 | 行为不够直观,排查稍复杂 |
生产环境一般推荐 ROW 格式,尤其是需要做数据订阅、审计或跨异构系统同步时,ROW 格式能提供最完整的变更信息。
2.2 开启 binlog
在 my.cnf 中配置并重启 MySQL:
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL
expire_logs_days = 7
其中 binlog_row_image = FULL 表示记录变更行的全部列,便于下游还原完整数据;expire_logs_days 控制日志保留天数,避免磁盘被写满。
配置完成后可以通过以下命令确认:
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_format';
SHOW BINARY LOGS;
三、方案一:MySQL 原生主从复制
主从复制是 MySQL 最经典、最常用的数据同步方式,本质上就是基于 binlog 的异步复制。
3.1 复制流程
flowchart LR
A[主库 Master] -- 写入变更 --> B[binlog]
B -- dump 线程推送 --> C[从库 IO 线程]
C -- 写入 --> D[relay log 中继日志]
D -- SQL 线程回放 --> E[从库数据]
F[从库] -- 上报位点 --> A
整个过程分为三步:
- 主库把数据变更写入 binlog。
- 从库的 IO 线程连接主库,由主库的 dump 线程把 binlog 事件推送给从库,从库写入本地 relay log。
- 从库的 SQL 线程读取 relay log 并回放,最终数据与主库保持一致。
3.2 搭建步骤
第一步:主库创建复制账号
CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl@123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
第二步:主库获取当前 binlog 位点
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
-- 记录 File 和 Position,例如 mysql-bin.000003 / 154
UNLOCK TABLES;
第三步:导出主库数据并导入从库
mysqldump -uroot -p --single-transaction --master-data=2 \
--databases demo > demo.sql
mysql -uroot -p < demo.sql
--single-transaction 保证 InnoDB 表在一致性快照下导出,不锁表;--master-data=2 会把 binlog 位点以注释形式写入备份文件。
第四步:从库配置并启动复制
CHANGE MASTER TO
MASTER_HOST = '192.168.1.10',
MASTER_PORT = 3306,
MASTER_USER = 'repl',
MASTER_PASSWORD = 'Repl@123456',
MASTER_LOG_FILE = 'mysql-bin.000003',
MASTER_LOG_POS = 154;
START SLAVE;
SHOW SLAVE STATUS\G
当 Slave_IO_Running 和 Slave_SQL_Running 都为 Yes,且 Seconds_Behind_Master 逐步归零时,说明复制已经正常工作。
3.3 复制模式的选择
- 异步复制:主库提交后立即返回,不等待从库确认,性能最好,但主库宕机可能丢数据。
- 半同步复制:主库至少等待一个从库确认收到 binlog 后才返回,兼顾性能与数据安全。
- 组复制(MGR):基于 Paxos 协议的多节点一致性复制,支持自动选主,适合对一致性要求高的场景。
开启半同步复制示例:
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;
四、方案二:基于 binlog 的异构数据同步(CDC)
当目标端不是 MySQL,而是 Elasticsearch、Kafka、ClickHouse、Redis 等系统时,主从复制就不适用了。这时通常采用 CDC 方案,直接订阅 binlog 并转换为目标端可消费的消息。
4.1 常见工具
| 工具 | 特点 | 适用场景 |
|---|---|---|
| Canal | 阿里开源,伪装成从库拉取 binlog | MySQL 到 ES/Redis 的实时同步 |
| Maxwell | 轻量,输出 JSON 格式 | 中小规模数据订阅 |
| Debezium | 基于 Kafka Connect,生态完善 | 企业级 CDC 平台 |
| Flink CDC | 流式计算 + CDC,支持全增量一体 | 实时数仓、复杂加工 |
4.2 Canal 工作原理
flowchart LR
A[MySQL Master] --> B[binlog]
B --> C[Canal Server 伪装从库]
C --> D[Canal Client]
D --> E[Elasticsearch]
D --> F[Kafka]
D --> G[Redis]
Canal 把自己伪装成一个 MySQL 从库,向主库发送 dump 协议请求,主库把 binlog 推送给它。Canal 解析 binlog 后转换成结构化数据,客户端再根据业务逻辑写入目标端。
4.3 一个简单的 Canal 客户端示例
public class CanalClientDemo {
public static void main(String[] args) throws Exception {
CanalConnector connector = CanalConnectors.newSingleConnector(
new InetSocketAddress("127.0.0.1", 11111),
"example", "", "");
connector.connect();
connector.subscribe("demo\\..*");
connector.rollback();
while (true) {
Message message = connector.getWithoutAck(100);
long batchId = message.getId();
if (batchId == -1 || message.getEntries().isEmpty()) {
Thread.sleep(1000);
continue;
}
for (CanalEntry.Entry entry : message.getEntries()) {
if (entry.getEntryType() != CanalEntry.EntryType.ROWDATA) {
continue;
}
CanalEntry.RowChange rowChange =
CanalEntry.RowChange.parseFrom(entry.getStoreValue());
for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
System.out.println("表:" + entry.getHeader().getTableName()
+ ",事件:" + rowChange.getEventType()
+ ",变更后:" + rowData.getAfterColumnsList());
}
}
connector.ack(batchId);
}
}
}
注意 connector.ack(batchId) 必须在业务处理成功之后调用,否则消息会重复消费;处理失败时调用 rollback 让 Canal 重新投递。
五、方案三:定时任务批量同步
对于实时性要求不高的场景,比如每天凌晨把业务库数据同步到报表库,用定时任务做增量拉取是最简单直接的方式。
5.1 基于时间戳的增量同步
-- 每次只拉取上次同步之后更新的数据
SELECT id, name, amount, update_time
FROM orders
WHERE update_time > #{lastSyncTime}
AND update_time <= #{currentTime}
ORDER BY update_time
LIMIT 1000;
这种方式实现简单,但有两个坑需要注意:
- 如果业务表没有可靠的
update_time,或者更新时没有刷新该字段,就会漏数据。 - 物理删除的数据无法通过时间戳感知,需要配合逻辑删除或定期全量校对。
5.2 基于自增主键的分页同步
SELECT id, name, amount
FROM orders
WHERE id > #{lastMaxId}
ORDER BY id
LIMIT 1000;
用自增主键做游标可以避免深分页问题,但同样无法感知更新和删除,通常只适合只增不改的日志类数据。
六、方案四:双写与业务层同步
有些场景下,同步逻辑直接写在业务代码里,比如写 MySQL 的同时写一份到 Redis 或 ES。这种方式实现直观,但问题也很明显:
- 两个写操作不在同一个事务里,任何一个失败都会造成数据不一致。
- 业务代码与同步逻辑耦合,后续新增目标端需要改代码。
- 并发写同一行时容易出现覆盖顺序错乱。
如果确实需要双写,建议采用「先写数据库,再发消息,由消费者异步写目标端」的模式,并配合本地消息表或事务消息保证最终一致。
七、数据同步中的常见问题与应对
7.1 主从延迟
主从延迟是最常见的问题,表现为从库读到的数据比主库旧。常见原因包括大事务、从库单线程回放、从库硬件较弱等。应对手段有:
- 开启并行复制:
slave_parallel_workers = 8,并设置slave_parallel_type = LOGICAL_CLOCK。 - 拆分大事务,避免一次更新几十万行。
- 关键读请求走主库,或使用半同步复制降低延迟风险。
7.2 数据不一致
可以通过 pt-table-checksum 定期校验主从数据一致性,发现差异后用 pt-table-sync 修复。对于 CDC 链路,则需要在消费端做幂等处理,避免重复消费导致数据错乱。
7.3 同步中断
常见的中断原因包括主库 binlog 被清理、从库执行出错、网络抖动等。建议:
- 合理设置 binlog 保留时间,确保从库或 CDC 消费端不会因为位点过期而无法恢复。
- 监控
Seconds_Behind_Master和复制线程状态,异常时及时告警。 - 对 CDC 消费端做位点持久化,重启后能从上次位置继续。
7.4 大表全量初始化
首次同步大表时,直接 mysqldump 可能锁表或耗时过长。可以采用「全量快照 + 增量追平」的方式:先记录 binlog 位点,再用一致性快照导出全量数据,导入完成后从记录的位点开始消费增量,最终追平。
八、方案选型建议
| 场景 | 推荐方案 | 说明 |
|---|---|---|
| 高可用、读写分离 | 原生主从复制 / MGR | 成熟稳定,运维成本低 |
| 同步到 ES、Redis、Kafka | Canal / Debezium / Flink CDC | 基于 binlog,实时性好 |
| 离线报表、T+1 分析 | 定时任务批量同步 | 实现简单,对源库压力可控 |
| 跨机房容灾 | 主从复制 + 半同步 | 兼顾数据安全与延迟 |
| 实时数仓 | Flink CDC | 全增量一体,支持复杂加工 |
九、总结
MySQL 实现数据同步的核心是 binlog。围绕它衍生出三条主流路径:原生主从复制解决高可用与读写分离,CDC 工具解决异构系统实时同步,定时任务解决离线批量同步。选型时先明确实时性要求和目标端类型,再考虑一致性与运维成本。无论采用哪种方案,都要重点关注主从延迟、数据校验、位点管理和异常监控,才能让同步链路长期稳定运行。
本文系统讲解MySQL数据同步的四大主流方案:基于binlog的原生主从复制、异构系统CDC(如Canal)、定时批量同步及业务层双写。重点剖析binlog机制、ROW格式优势、半同步与MGR复制模式、Canal工作原理、增量同步策略(时间戳/主键)、主从延迟与数据一致性保障措施,并给出选型建议,强调位点管理、监控告警与异常恢复等运维关键点。

1310

被折叠的 条评论
为什么被折叠?



