你是小阿巴,刚入职一家电商公司。
第一天上班,老板就交给你一个艰巨的任务:定期把公司的订单数据同步到数据分析仓库。
一听到数据同步这 4 个字,你立刻汗流浃背了。
你的哥哥程序员鱼皮,曾经就是在大公司负责数据同步。结果双十一当天,近 2 小时的订单数据没有同步过去。数据分析团队看到的数据是 2 小时前的,以为销量没达到预期,就没有及时给热销商品补货。最终错失了 1 个多亿的销售额!鱼皮也因此被老板优化掉了。
作为一名程序员,怎能轻易退缩?你握紧拳头,一定要完成好这个任务!
建议观看本文对应视频版:https://bilibili.com/video/BV1Xvnoz7EKJ
全量同步
什么是数据同步呢?就像你有两个手机,要把其中一个手机的照片复制到另一个手机里。数据同步就是把一个数据库的数据,定期复制到另一个数据库里。
你心想:这还不简单吗?我写个定时任务,每天 把整个订单表的数据 全部查出来,然后 一股脑插入 到数据仓库!
这种方法叫 全量同步,不管数据有没有变化,每次都把所有数据重新复制一遍,简单粗暴。
# 查出全部订单
orders = db.query("SELECT * FROM orders")
# 清空已有老数据
warehouse.execute("DELETE FROM orders")
# 插入新值
warehouse.insert(order)
第一天,公司有 1 万条订单数据。你的程序跑了 3 个小时,同步成功!
老板看着数据夸到:不错啊小阿巴,开发神速啊!
你内心暗爽:高端的功能往往只需要最简单的代码。
基础告警
第二天一早,你还没到公司,就收到了运营小姐姐的夺命连环 call:“小阿巴!出大事了!昨天晚上的数据同步失败了,现在老板还没看到昨天的数据,他正在会议室发火呢!”
你意识到:现在的程序是不可靠的,如果因为网络等原因同步失败了,可能要到第二天才会发现。
于是你给程序加了一段逻辑:如果同步失败,会记录错误日志,同时发邮件通知管理员,并且把数据库回滚到同步前的状态。
这样如果出了问题,你会比老板先发现,手动重新执行一遍同步任务就好。
增量同步
过了几天,老板黑着脸找你了:“小阿巴,为什么今天数据还没同步完?”
你看了下订单数据,立刻发现了问题。现在有 10 万条数据,程序跑了 30 个小时还没结束;这样下去,如果有 100 万条数据,估计要 300 个小时!
老板:少废话,快点解决,不然送你到隔壁餐饮部沉淀沉淀。
你开始思考:既然全量同步太慢,那我只同步每天新增的订单不就行了?
这就是 增量同步,约定每天 0 点执行同步任务,只同步昨天 0 点之后创建的订单。
这样大大减少了每次同步的数据量,任务的执行时间不再线性增加了。
但很快,你发了一个问题,订单的状态是会发生变化的,比如用户付款后又退款。
如果使用订单创建时间作为增量同步的分界标志,只能同步新增的订单,但是订单状态的更新不会同步!
于是,聪明的你使用 updated_time 字段进行增量同步,这个字段会在每次数据变化时自动更新。
这样新增和修改的数据都能同步了!
你内心窃喜:聪明如我小阿巴,老板夸我好开发。
批处理
过了几天,公司搞了一波大促活动,当天订单量比平时多了几倍,老板请大家通宵狂欢开 party。
结果正在你欢唱 “只因你太美” 的时候,突然公司的运维大叫:不好了不好了,我们的项目服务器卡死了,用户无法下单!
你看了看时间,0 点多,不正是数据同步任务的执行时间么?你瞬间汗流浃背,放下麦克风,赶回公司修 Bug。
原来是因为一次查询出来的订单数据太多了,都加载到内存,导致服务器 OOM 内存溢出了。
你悔不当初:唉,早该想到这个问题,既然单次处理数据量太大有风险,那我就 分批处理。
每 100 条数据为一批,每次只从数据库中分页查询出这一批数据,同步完这一批,再执行下一批。
这样每次只处理少量数据,内存压力小;而且如果某一批处理失败,只需要回滚和重新同步这一批,而不是全部重来。
问题算是解决了,但是今夜你彻夜难眠,你似乎成了公司业务增长后,唯一不开心的那个人。
希望今后不会再遇到这种闹心事了吧。
游标机制
但生活总是这样,屋漏偏逢连夜雨。两天后,你又收到了老板的咆哮:“狗阿巴,这就是你做的数据同步?你自己看看丢了多少数据!”
你大惊,丢失数据?不应该啊。。。
经过仔细排查,你发现了问题:如果在分页查询的过程中,有新的数据被插入或更新,就会导致数据偏移和丢失!
比如查询第 1 页时,符合条件的订单有 4 条:
订单 A:updateTime=08:00
订单 B:updateTime=09:00
订单 C:updateTime=09:30
订单 D:updateTime=09:50
执行第 1 页查询,每页 2 条,偏移量为 0:
SELECT * FROM orders
WHERE updated_time >= '2025-09-08' and updated_time < '2025-09-09'
ORDER BY updated_time
LIMIT 2 OFFSET 0; -- 每页 2 条,第 1 页
查询结果返回订单 A(08:00)和订单 B(09:00),同步完成后将偏移量调整为下一页 OFFSET=2
。
可就在查询下一页前,订单 B 因为 “修改订单状态” 被更新,这时订单 B 的更新时间就不在查询范围内了。
这就导致查询下一页 OFFSET=2
时,直接跳过了前两条订单 A 和 C,从 D 开始同步,导致订单 C 丢失。
怎么解决这个问题呢?
这可难不倒你小阿巴,既然 动态数据集 中使用 SQL 自带的 OFFSET 偏移作为分页起点会出问题,那不妨 自定义一个标志来记录下一批要同步的起点。
这就是游标机制。
比如我约定自增的主键 id 作为游标,每查询一批数据之后,把这批数据的最后一条记录作为新的游标值;查询下一批数据时,只查询 id > 游标的数据。
这样不仅防止数据丢失,还避免了 OFFSET 深度分页带来的性能问题。
此外,如果把游标想象成进度条的断点,可以更清晰地记录同步进度,失败后可以从断点继续。
做完这一通优化后,你长吁了一口气,工作看来是保住了。
性能优化
你以为终于可以安稳一段时间了。但没想到,公司的好日子才刚刚开始。
随着老板大力扩张业务,公司每天的订单量可以达到百万条,你的同步任务每次要跑几个小时才能完成。
更要命的是,老板现在对数据的依赖越来越强,要求每隔 2 个小时就要同步一次数据!
怎么能够让任务执行更快呢?
这时,你想起了曾经在
首先,修改插入数据库的操作方式。之前是一条一条地插入订单数据,改为执行一条批处理语句来同时插入同一批内多个订单数据,减少了跟数据库通信的次数,性能大幅提升。
但是你觉得还不够快,于是优化了批处理的流程。之前是等一批同步完再同步下一批,串行执行;现在你启动了多个线程,每个线程负责处理一批订单的同步,多个线程可以同时搬砖干活,效率大幅提高。
这一通操作下来,老板都激动了:“小阿巴,你这技术可以啊!好好干,我给你升职加薪!”
你笑了,有了老板这句话,你想继续努力给公司卖命了。
实时同步
两年后,随着你头发的消逝,公司总部决定要上市了!
老板找到了你:“阿巴阿巴,咱们的数据同步能不能做到实时?我想给投资人展示实时的业务监控。最好是用户刚下单,投资人立刻就能在大屏幕上看到!"
你早料到会有这个需求,帅气地甩出一句话:“交给我吧,我让你见识一下企业级方案!”
这两年你也没闲着,像实时数据分析这种典型需求的实现方案,你早已烂熟于心。
普通的定时任务已经无法满足实时性的要求,只能搬出 CDC + 消息队列 这两大杀器了。
CDC(Change Data Capture)就像给数据库安装了一个 24 小时实时监控的摄像头,数据有任何变化,摄像头都能立刻发现。
消息队列就像一个快递中转站。数据库变化的消息先发送到这个中转站,然后同步数据的程序从中转站取出数据并写入到数据分析仓库中。
有了中转站后,如果消息特别多,程序来不及处理,也不会丢失数据。
实现了这套方案之后,用户只要一下单,100 毫秒内老板就能在仪表盘上看到。而且保险起见,你还给消息队列加了个监控,如果消息堆积太多,就会自动告警。
他开心得像个孩子:“小阿巴,我给你升职加薪,还给你带新人!”
你又笑了,开始幻想着你和新人坐在高高的办公桌上,你给他讲述自己光辉事迹,他向你投来羡慕的目光。
方案完善
半个月后,公司迎来了双十一大促活动。
你正在和新来的实习生阿坤吹牛皮,没想到瞬间产生了大量订单,像洪水一般,让你的实时同步系统出现了各种异常:
-
有些订单重复同步了
-
有些订单的状态变化顺序错乱了
-
还有大量消息堆积在消息队列里处理不过来,导致数据没有及时同步
监控大屏上各种告警此起彼伏,老板的脸色越来越难看:“狗阿巴,这就是你说的企业级方案?”
这一刻,你有点恍惚:“我只是知道可以这么实现,但没人告诉我出了这些问题怎么解决。。。”
这时,你身旁的实习生阿坤说话了:“我来!”
只见他对着电脑一通操作,嘴里振振有词:
1)消息重复问题,可以通过幂等机制解决。给每条消息一个唯一编号,处理过的消息编号记录下来,就不会重复处理了。
2)消息乱序问题,可以通过分区解决。把相关的消息放到同一个分区,保证同一个订单的消息按顺序处理。
3)消息堆积问题,可以通过搭建集群和动态扩容,增加对消息的处理能力。
“这些点在使用消息队列来实现数据同步的时候就要考虑到才对呀!”
你听着阿坤说的话,默默低下了头,内心五味杂陈,这就是应届生的水平么?
阿坤看着你的代码接着说:“不是哥,做个数据同步要这么麻烦么?DataX、Canal、Debezium,这么多现成的企业级数据同步工具你不用?非要自己写代码?”
你无言以对,内心感受到了来自实习生的一吨暴击。
阿坤甚至一脸嫌弃地看着你:“不是哥们,连数据对账都不做么?你自己都不定期检查同步前后的数据是否一致么?我这有份数据同步的企业级方案,你好好看看吧。”
老板拍了拍阿坤的肩膀:“小伙子优秀,导师的导师,今后公司的未来就靠你了,我给你升职加薪。至于老阿巴,你好自为之吧。”
这个城市,又多了一个伤心的人,给个点赞收藏三连,帮忙回回血吧。