新闻详情

新闻详情

首页 / 资讯中心 / 详情

数据库实时同步与异步同步:机制、原理与工程实践

发布时间:2026/9/15 5:32:38来源:尧图网络
数据库实时同步与异步同步:机制、原理与工程实践
数据库的实时同步说穿了就是一个“追上另一个库的每一次变化”的活儿。我刚入行那会儿最头疼的就是主库一挂、从库数据和主库差了十万八千里恢复业务全靠手工补数补到天亮是常有的事。后来接触的同步方案多了才慢慢搞明白实时同步和异步同步根本不是“快和慢”的关系而是两种完全不同的工程取舍。这篇东西我按自己的理解把这两套机制、底层原理、实操方案和踩过的坑一次性讲透。不管是刚做数据库课程设计的学生还是在维护Oracle、达梦、MySQL、ClickHouse的生产老手只要你被“数据怎么搬过去、怎么保持一致”这个问题折磨过这篇文章应该都能给你一些参考。我会先把同步机制拆开看再给出一套从MySQL同步到ClickHouse的可落地配置最后把我们上线时遇见的典型故障和排查思路一并整理出来。1. 同步机制全景实时同步和异步同步到底在同步什么1.1 实时同步不是“快一点的异步”很多人对实时同步有个误解以为它就是把同步频率调高、定时任务加快本质上还是隔一会儿跑一次。这里必须先纠偏实时同步和异步同步的区别不在速度而在“触发方式”。实时同步的核心是事件驱动Event-Driven。源库的每一条增删改操作都会产生一个事件比如MySQL里的binlog、PostgreSQL里的WAL、Oracle里的Redo Log同步组件监听并解析这些事件一旦有新的变更就立刻将其投递到目标端执行。源库提交事务的瞬间目标库基本上也在毫秒级之内完成了同样的变更整个过程不需要人工干预也不需要定时轮询。异步同步的核心则是批处理驱动Batch-Driven。它的流程通常是定一个间隔比如每5分钟、每小时跑一次任务把源库里旧的数据批量读取出来经过清洗转换后写入目标库。这里有个关键点异步同步拿到的往往是“某个时间点的快照”而不是“连续变化的日志流”。快照与快照之间新产生的数据只能在下一个周期再次被扫描带走这就天然导致目标库的数据滞后于源库。我自己的理解是这样的实时同步解决的是“丝滑一致”的问题异步同步解决的是“批量搬运”的问题。前者像一根水管直接接通两个水池水流几乎同时涌动后者像用桶一桶一桶地拎水虽然每次量很大但中间总有时间差。1.2 异步同步的价值不在“慢”而在“稳”既然异步同步有延迟为什么那么多系统还在用因为它在某些场景里反而比实时同步更合适。第一异步同步对源库的侵入性极低。大多数异步方案只是定期执行SELECT查询只要SQL写得合理走的是索引基本不会对源库造成持续的额外压力。实时同步如果配置不当比如binlog解析线程过多、目标端消费过慢反而可能把源库的IO拖垮。第二异步同步天然适合数据仓库和报表类场景。数仓里的分析任务本来就不需要看到秒级的数据它是按天、按小时做数据切片和指标汇总的。如果为了这种场景强行上实时同步不仅成本高还会产生大量无意义的中间数据。第三异步同步可以很方便地做数据校验和历史归档。因为它是批量读取所以可以顺带在同步过程中做去重、格式转换、分区切换甚至可以把历史数据从一个存储迁移到另一个存储而实时同步一般来说只会关注增量变更做这种历史数据的批量处理反而不擅长。所以一个成熟的数据库同步体系往往是实时和异步共存的。实时链路保证业务核心数据的低延迟可见异步链路用来做全量初始化、离线分析、日终对账。两者不是替代关系而是分工关系。2. 核心原理拆解binlog、CDC、消息队列和最终一致性2.1 binlog是MySQL同步的“第一桶金”做MySQL实时同步不管用什么工具底层几乎都绕不开binlog。你要理解实时同步先得理解binlog里到底装了什么。MySQL有几种binlog格式最常用的是ROW、STATEMENT、MIXED三兄弟。实时同步场景下我强烈建议使用ROW格式因为ROW格式记录的是每一行数据变更前后的完整镜像。比如执行一条UPDATE user SET age 30 WHERE id 100STATEMENT格式可能只记了一条SQL语句比较精简但同步到目标端重放时如果目标端的数据和源库有微小差异结果就不一样了。ROW格式则直接把id100这一行更新前后的值写清楚目标端只需按图索骥地改行准确率最高。可以这样类比STATEMENT格式相当于给你一份“菜谱”告诉你“把这道菜再加热一下”但每个厨房的锅和火候不同做出来的味道可能不一样ROW格式相当于直接给你“做好的菜”你只需要把它端上桌。在配置MySQL时至少要开启这几项[mysqld] server-id 1001 log-bin mysql-bin binlog_format ROW expire_logs_days 7 max_binlog_size 256Mserver-id必须设置为唯一值这个在搭建主从和配置同步工具时是硬性要求同一套复制链路里不允许出现两个相同的server-id否则MySQL会认为发生了复制环路。binlog_format ROW是我们做实时同步的基础expire_logs_days则控制binlog保留天数太短会导致同步组件断连后日志被清掉没法重新拉取太长又占磁盘空间我们生产环境一般根据同步延迟情况留3到7天。2.2 实时同步的三种主流姿势触发器、CDC、业务双写实时同步不是只有一种实现方式我梳理下来大致有三类姿势一触发器Trigger方案。在源库的表上建立触发器每有INSERT、UPDATE、DELETE就写入一张中间日志表再由同步程序把中间表的变化读出来投递到目标端。这种方案实现简单开发成本低但本身对源库的事务性能有较大冲击因为触发器是在原事务内部同步执行的属于额外负担。而且一条SQL语句即使只影响一行也要触发多个触发器的动作在高并发写入场景下很容易拖慢业务。个人建议中小系统、数据量可控、表结构不常动的场景可以试试高并发生产环境尽量别碰。姿势二CDCChange Data Capture方案。CDC的核心思想是“捕获数据变更”它不依赖触发器也不需要在业务代码里额外开发而是直接读取数据库的事务日志。MySQL的binlog、PostgreSQL的逻辑复制槽、Oracle的LogMiner和GoldenGate都属于这一类。以开源工具Canal为例它会伪装成一个MySQL从库向主库请求binlog然后解析成结构化的事件数据再投递给下游消费者。这个方案对源库影响相对小实时性也高是我们生产环境首选。姿势三业务双写方案。也就是在应用代码层面写完主库后再同步写一份到目标库或者消息队列。这个方案的优点是可控性强可以在业务代码里做事务补偿缺点是对代码侵入性极大而且一旦漏写某条数据很难靠事后机制补齐。双写方案一般只在系统规模较小、同步逻辑完全由自己掌握时才推荐否则维护成本会越来越高。2.3 异步同步的消息中间件与批量落库异步同步不止是定时跑个任务它还可以和消息队列深度绑定。举个例子业务系统每次写入主库后顺手往Kafka或RocketMQ里发一条消息异步消费端从消息队列里拿到这批变更再批量写入目标库。这里的“异步”是指业务请求和同步结果的解耦——业务方不需要等待目标库写入完成就可以返回。不过消息队列方案里有个很容易忽略的坑业务方发消息的动作和写数据库的动作不是一个本地事务。如果先写库后发消息消息发出后目标库写入失败就会产生“源库有、目标库无”的不一致如果先发消息后写库业务方一旦回滚消息却已经发出去了产生“目标库多了一条数据”的脏数据。我们现在的处理方式是业务写入主库后把消息以“本地消息表”的方式先存在同一个数据库事务里再由一个异步线程扫描本地消息表确认消息发送成功后更新状态。目标端消费消息时必须保证消费逻辑的幂等性否则重复投递一样会产生脏数据。纯异步批处理则是更朴素的方案每天凌晨跑一个定时任务用SELECT ... WHERE update_time ?的方式把增量数据捞出来然后批量写入目标库。这种方案对实时性要求不高的场景完全够用而且排查问题最方便因为每一批数据都是确定性的。3. 实操连线一套MySQL到ClickHouse的实时同步方案3.1 为什么选ClickHouse作为同步目标环境怎么准备这两年ClickHouse在分析场景里特别火很多团队的业务库还是MySQL但分析报表用的是ClickHouseMySQL到ClickHouse的数据同步就成了刚需。ClickHouse的MergeTree引擎本质上是为批量写入和列式分析设计的它在单条实时写入上百亿行这种场景下并不擅长所以如果把ClickHouse当作OLTP库高强度写很容易触碰到它的短板。我的建议是实时同步过来的数据先写到Kafka再由ClickHouse的Kafka引擎表消费并批量落盘让ClickHouse用自己最舒服的批量合并方式去处理数据。这也是为什么我在第一节强调“实时”不代表“每条都立刻写入”你完全可以做一个“秒级采集、分钟级落库”的准实时方案。环境准备分三块一台MySQL比如5.7或8.0保证开启binlog一套Kafka或直接用Canal自带的适配器直接写ClickHouse一套ClickHouse21.8以上版本。如果你的ClickHouse开启了集群模式需要注意副本表和分布式表的配置同步目标最好指向分布式表避免数据分片不均匀。3.2 Canal的配置与事件订阅监听binlog的正确姿势Canal是阿里巴巴开源的一套MySQL binlog解析工具也是目前Java生态里用得最广的CDC方案之一。我用它做过很多次同步稳定性和文档成熟度都有保证。Canal的使用分三步第一步在MySQL里创建专用的同步账号并授予复制权限CREATE USER canal% IDENTIFIED BY canal_pass; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO canal%; FLUSH PRIVILEGES;这里必须提醒只给SELECT和复制权限就够了绝对不要给这个账号分配删除或修改业务数据的权限。同步账号越权一旦被误操作就是灾难。第二步下载Canal并修改conf/example/instance.propertiescanal.instance.master.address 127.0.0.1:3306 canal.instance.dbUsername canal canal.instance.dbPassword canal_pass canal.instance.connectionCharset UTF-8 canal.instance.filter.regex db_name\\..*filter.regex是重点你可以用它精确控制同步哪些表。比如只同步order_db下的t_order和t_order_item可以写成db_name\\.t_order.*,db_name\\.t_order_item。如果在这块偷懒把全部表都同步过去后面过滤规则会更难维护。第三步在业务代码里对接Canal客户端监听数据变更事件。简单写法是使用canal.client库CanalConnector connector CanalConnectors.newSingleConnector( new InetSocketAddress(127.0.0.1, 11111), example, , ); connector.connect(); connector.subscribe(db_name\\..*); while (running) { Message message connector.getWithoutAck(100); long batchId message.getId(); if (batchId -1 || message.getEntries().isEmpty()) { // 没有数据变更稍等再取 Thread.sleep(200); continue; } for (CanalEntry.Entry entry : message.getEntries()) { // 解析entry区分INSERT、UPDATE、DELETE // 投递到Kafka或直接写入目标端 } connector.ack(batchId); // 确认这条批次处理完毕 }这个ack机制很关键它就像快递签收单。消费端处理完这一批事件后必须调用ack(batchId)通知Canal可以继续发送下一条如果处理过程中异常就不要ackCanal端会重新投递。但这里又引出一个问题重新投递可能导致目标端重复写入所以你的目标端写入逻辑必须设计成幂等。我们在生产里用的做法是目标表里带一个sync_version字段每次写入都带上源库binlog的坐标日志文件名加偏移量如果目标端发现同样的坐标已经处理过就跳过。这样即使消费端重启、重复拉取也不会污染数据。3.3 异步兜底链路定时批量任务和幂等设计实时链路做得再漂亮也难免有意外比如Canal挂了、Kafka分区繁忙、目标库锁表。这时候如果只有实时链路数据就会一直落后恢复之后还要想办法补齐缺口。所以我强烈建议给实时同步配一条“异步兜底”链路。异步兜底的做法不复杂每小时跑一次定时任务扫描源库中最近两小时有变更的记录SELECT id, order_no, status, update_time FROM order_db.t_order WHERE update_time DATE_SUB(NOW(), INTERVAL 2 HOUR) AND update_time DATE_SUB(NOW(), INTERVAL 10 MINUTE);这里特意把最近10分钟的数据排除掉避免和实时链路已经完整处理过的最新数据产生竞争。每次扫描完成之后把数据写入一个临时表再做去重合并最后Upsert到目标库。异步兜底的写入操作我们用的是ClickHouse的ReplacingMergeTree引擎。这个引擎允许你写入重复数据但会在后台合并时根据ORDER BY字段去重。比如你用ORDER BY (id)指定去重键那么同一id的多条记录在Merge后只会保留最新的一条按版本字段或写入顺序。这样的设计可以允许定时任务和实时任务偶尔写入同一条数据最终结果也不会乱。这个兜底链路的价值我举一个实际场景某次我们把Canal的内存设置调小了结果消息堆积把Canal搞挂了实时链路断了差不多40分钟。如果没有定时兜底任务这40分钟的业务数据就只能靠人肉补脚本处理而我们当时的兜底任务在Canal恢复后自动把这个缺口补上了最终对账差异为零。4. 故障排查实录同步延迟、数据不一致和循环同步4.1 同步延迟堆到几十万怎么定位瓶颈生产环境里最常遇到的同步问题就是“延迟”。表现是源库一条数据已经更新了但目标库怎么查都查不到或者目标库的监控面板显示同步位点落后源库一大截。我的排查路径一般是这样的第一步确认延迟发生在哪个环节。如果走的是MySQL到Kafka到ClickHouse的链路你就分别看MySQL的binlog位点、Kafka的消费位点、ClickHouse的写入速率。哪个环节消费位点增长最慢瓶颈就在哪里。第二步如果瓶颈在Canal先看Canal是否频繁GC。默认JVM堆内存可能不够用binlog解析如果赶上大事务会产生大量对象GC一停顿消费位点就原地不动。我们当时把Canal的-Xms和-Xmx都调整到4G延迟立刻从小时级降回秒级。第三步如果瓶颈在ClickHouse写入多半是批量过小导致的。ClickHouse单次insert条数越大整体吞吐越高所以实时消费端不要一条一条地insert而是攒够一定条数或间隔一定时间再批量写入。我们通常设置攒够5000条或每1秒刷一次实测吞吐提升非常明显。4.2 数据对不上账校验和修复的办法同步系统做得再好对账仍然是必不可少的。我们的对账方案分两层第一层是数量对账。每天跑一个任务统计源库和目标库每个表的总行数对比差值。这个方法看似笨拙但成本低能发现大部分漏同步的问题。注意这里要选合适的统计字段最好用主键ID的最大值和最小值辅助判断否则在数据量很大的表上COUNT(*)本身就很慢。第二层是抽样内容对账。对核心表按主键Hash抽样抽取一定比例的数据把源库和目标库的关键字段逐一比对。这里有个实用技巧不要按行一条一条地拼接字符串比对效率太低。可以按主键分组把多个字段拼成MD5值然后对比MD5一旦MD5不一致再展开具体字段排查效率能提升不少。如果发现确实有漏同步的数据小批量的直接跑补数SQL大批量的就把主键范围捞出来重新写入Kafka让实时链路重放一遍。修复完成后一定要重新执行一次对账脚本确认差异清零才能收工。4.3 循环同步和死锁是怎么把数据库拖垮的这个坑我在早期做双向同步时踩过。所谓双向同步就是A库和B库互相同步这样任意一边的写入都能在另一端生效。听起来很美但如果控制不好A库的一条更新同步到B库之后B库的变更日志里会再次记录这条更新然后又被反向同步回A库。A库收到之后又产生一条新日志再同步到B库……如此循环数据被无限放大最终把两个库都拖垮。规避方法有几种一是用数据源标记在同步过来的数据里写入一个特殊的源标识消费端看到这个标识就跳过业务逻辑处理只做数据落地二是从日志里过滤掉同步进程自己产生的变更比如给同步账号打标记binlog里带有特定server-id的记录不再转发三是不要做双向同步改为单向同步加业务层写路由这是最保险的方案。另外同步写入目标库时也容易引起死锁。比如两个事务分别同步两条互相引用的记录加锁顺序不一致就死锁了。解决思路有两个一是让同步线程串行化二是在批量写入时按主键排序保证加锁顺序一致。我们最终选的是后者因为吞吐更高也不影响实时性。5. 工具选型和场景建议从开源到国产数据库的同步方案速查5.1 MySQL生态的主流同步工具对比很多人问我同步工具选哪个好我通常会把它们分成三类来看。第一类是数据库原生主从复制比如MySQL的Replication、PostgreSQL的流复制。它最适合做高可用场景主库宕机后从库可以快速提升为新的主库。但原生复制的目标库类型必须和源库一致它解决不了异构数据库同步的问题。第二类是CDC与消息管道工具比如Canal、Debezium、Maxwell、Flink CDC。这类工具把数据库变更事件化再投递到消息队列或目标端。它们能解决数据库到数仓、到搜索引擎、到缓存的异构同步问题也是目前实时同步的主流选择。Debezium胜在开源生态好原生支持Kafka ConnectCanal在MySQL领域更接地气国内资料多Flink CDC则在流计算场景下能把数据和计算引擎天然融合。第三类是商业ETL和迁移工具比如DataX、CloudCanal、NineData等。DataX是阿里巴巴开源的离线数据同步工具适合大批量全量迁移CloudCanal等商业工具则在UI可视化和全增量一体化上做得更完善不过在选型前要评估好授权成本和平台绑定风险。我个人的选型经验是如果只是同构数据库之间的高可用优先用原生复制如果是异构实时同步优先用Canal或Debezium加Kafka如果是一次性全量迁移优先用DataX如果团队业务比较杂、希望少写代码可以评估商业同步平台。5.2 达梦、人大金仓等国产数据库同步怎么处理现在不少项目都在用达梦、人大金仓等国产数据库。这类数据库同步方案没有MySQL那么统一但也不用慌核心思路是一样的先看它是否提供兼容MySQL或PostgreSQL的复制协议。达梦数据库提供DTS数据集成工具可以完成同构和异构场景下的数据迁移与同步。如果是把MySQL增量同步到达梦目前比较稳妥的做法是用支持异构同步的商业工具或者达梦官方工具。人大金仓KingbaseES则兼容PostgreSQL协议很多基于PostgreSQL逻辑复制的同步工具可以直接或稍作适配后使用。针对这类国产库我的建议是选型前先确认同步目标和源库的兼容性。比如源库是MySQL目标库是达梦你就需要确认是否有达梦的CDC插件或驱动能被Flink CDC、Canal等工具识别。如果官方没有提供适配组件就不要强行用开源工具硬怼否则排查问题会非常痛苦。另一个思路是用业务中间层解耦源库的binlog事件先落到Kafka再开发一个小型消费程序用达梦的JDBC批量写入目标表。只要你的同步逻辑足够通用换目标库时只需要换一个writer实现。5.3 数据同步架构设计的三条铁律做过多套同步架构之后我总结出三条自己一直遵守的规则分享给你。第一条实时同步必须可监控。你能随时看到当前同步位点、链路延迟、消费速率和异常事件数量。没有监控的同步链路就像没有仪表盘的飞机出了状况根本不知道从哪开始排查。我们是在Prometheus里暴露了Canal消费者位点和Kafka消费组Lag指标再配上告警规则延迟超过1分钟就通知值班人员超过5分钟直接打电话。第二条同步链路必须可回放。也就是说每次数据变更最好落到一个持久化的中间介质上比如Kafka。这样即使目标端挂了你也能在恢复后从Kafka重新消费把缺漏的数据补回来。如果方案里没有这个中间介质一旦下游故障源库的旧binlog又过期了数据就永久丢失了。第三条写入目标库必须幂等。不管同步工具宣称自己多么可靠你都要假设它可能重复投递。目标端的写入逻辑只有做到“重复执行结果相同”才能在上游重试、消费者重启这些意外面前保持最终一致。实现幂等的手段有主键去重、版本号比较、幂等键表等根据目标库类型灵活选用。6. 结语与个人经验数据库的实时同步和异步同步本质上不是一道“二选一”的题目而是一道“如何搭配”的题目。实时同步负责低延迟和高时效异步同步负责稳定性和批量处理能力两者结合才是一个健壮的同步体系。我见过不少团队一上来就想全链路实时恨不得每张表都能秒级同步结果是把同步组件越叠越重运维成本居高不下还不如踏踏实实先做一个准实时的大宽表同步。根据我个人经验一开始做数据库同步建议不要直接上最复杂的架构。可以先做异步批处理把数据打通、验证清楚业务对延迟的忍耐度然后再判断哪些核心链路值得升级成实时同步。实时同步不是万能的但当你真正需要秒级一致的时候它的价值会让你觉得前期投入非常值得。最后再分享一个我们内部常用的技巧不要只盯着一张表的同步状态要建立一张“同步体检表”记录每次同步任务开始时间、结束时间、增量行数、耗时、异常数。这张表本身不复杂但它是你后续做延迟分析和数据对账最重要的底账。数据同步这行先让自己心里有数才有资格谈自动化。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

AI前端面试黄金准备期:SSE流式处理与TypeScript类型守门实战 2026/9/15 6:41:42

AI前端面试黄金准备期:SSE流式处理与TypeScript类型守门实战

1. 为什么9月8号是今年AI前端面试准备的黄金启动日?如果你正盯着日历,犹豫“现在开始准备AI方向的前端面试,到底来不来得及”,那我得先告诉你一个反直觉但被上百份真实offer验证过的结论:9月8号不是太晚,而…

阅读更多 →
联合储能系统在配电网优化调度中的应用与Matlab实现 2026/9/15 6:41:42

联合储能系统在配电网优化调度中的应用与Matlab实现

1. 项目概述:联合储能在配电网中的关键作用电力系统正经历着从传统化石能源向可再生能源转型的关键时期。在这个转型过程中,配电网作为连接发电侧和用户侧的"最后一公里",面临着前所未有的挑战与机遇。我最近完成的一个研究项目&am…

阅读更多 →
专业图片去水印技术解析与高效工具实操指南 2026/9/15 6:41:42

专业图片去水印技术解析与高效工具实操指南

1. 图片去水印工具的核心价值与应用场景作为一名经常处理图片素材的视觉设计师,我深知水印对作品完整性的破坏有多严重。无论是从网络获取的参考图、客户提供的带版权标记的素材,还是自己早期添加水印后需要重新编辑的旧作品,水印的存在往往成…

阅读更多 →
别再滥用Redis!后端缓存设计的三个致命误区 2026/9/15 6:41:42

别再滥用Redis!后端缓存设计的三个致命误区

去年,我们一个商品详情服务接入了Redis,QPS从两千涨到了两万,团队欢呼雀跃。三个月后,一次缓存雪崩,数据库被打穿,服务瘫痪了四十分钟。复盘时才发现,我们把Redis当成了万能药,却踩了…

阅读更多 →
社区空巢老人照管系统毕业设计:Spring Boot+Vue完整实现与部署指南 2026/9/15 6:41:42

社区空巢老人照管系统毕业设计:Spring Boot+Vue完整实现与部署指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
AI原生应用开发中的伦理挑战与实践框架 2026/9/15 6:38:42

AI原生应用开发中的伦理挑战与实践框架

1. 为什么AI伦理成为AI原生应用的必修课去年我在参与一个智能客服系统开发时,遇到过一个典型案例。当用户询问"我想自杀"时,系统竟然回复了附近药店的位置和营业时间。这个令人后怕的失误让我们团队意识到:没有伦理框架约束的AI系统…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞