
一、什么是数据同步数据同步是指把一份数据从一个存储位置复制到另一个存储位置并保证两端数据在内容上保持一致的过程。在真实业务中数据同步几乎无处不在主从复制保证高可用、读写分离分摊压力、异地机房做容灾、数据仓库做离线分析、缓存与数据库之间保持一致背后都离不开数据同步。从同步方式上看常见的数据同步可以分为几类全量同步一次性把源端的全部数据复制到目标端适合初始化场景但数据量大时耗时长、对源库压力大。我们在做全量同步的时候尽量的选择到一个用户使用量较低的时段进行一个同步或者对于一些大型的项目比如我们市面上常见的一些大型游戏他们会选择一个时间进行停服处理在处理完毕后会重新开放给用户。增量同步只同步发生变化的数据通常依赖日志或时间戳是生产环境的主流方式。这是我们常见的一个方案通常在那些集群中会经常用到用于同步各个集群之间的数据以及主节点从节点之间的数据。实时同步变化发生后毫秒到秒级即可到达目标端依赖 binlog 等变更日志。准实时/定时同步按固定周期批量拉取实现简单但存在延迟。从一致性上看又可以分为强一致与最终一致。绝大多数互联网业务选择最终一致用可接受的延迟换取吞吐和可用性。二、MySQL 实现数据同步的核心基础binlogMySQL 实现数据同步的根基是binlog二进制日志。它记录了数据库中所有引起数据变更的语句或行变更是主从复制、数据订阅、CDCChange 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 Repl123456; 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-data2 \ --databases demo demo.sql mysql -uroot -p demo.sql--single-transaction保证 InnoDB 表在一致性快照下导出不锁表--master-data2会把 binlog 位点以注释形式写入备份文件。第四步从库配置并启动复制CHANGE MASTER TO MASTER_HOST 192.168.1.10, MASTER_PORT 3306, MASTER_USER repl, MASTER_PASSWORD Repl123456, 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阿里开源伪装成从库拉取 binlogMySQL 到 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、KafkaCanal / Debezium / Flink CDC基于 binlog实时性好离线报表、T1 分析定时任务批量同步实现简单对源库压力可控跨机房容灾主从复制 半同步兼顾数据安全与延迟实时数仓Flink CDC全增量一体支持复杂加工九、总结MySQL 实现数据同步的核心是 binlog。围绕它衍生出三条主流路径原生主从复制解决高可用与读写分离CDC 工具解决异构系统实时同步定时任务解决离线批量同步。选型时先明确实时性要求和目标端类型再考虑一致性与运维成本。无论采用哪种方案都要重点关注主从延迟、数据校验、位点管理和异常监控才能让同步链路长期稳定运行。