
前置机数据同步最麻烦的地方在于链路监控全是绿的业务侧却时不时冒出一条旧数据。系统替代那段时间内外网两边的库并行跑每天早上对账脚本跑出来的差异少则几十条多的时候上千条追查下去无非两类原因同步链路瞬时断了没接上或者写入顺序乱了把新值覆盖成旧值。这篇文章把当时用过的两套方案拆开讲清楚一套是实时比对一套是定时补偿两套不是二选一的关系而是主备搭配各自的边界和落地的坑都会说到。一、先说结论实时比对做主力定时补偿做兜底结论先放前面。实时比对解决差异产生后尽快发现、尽快修复定时补偿解决漏网差异定期清零。单靠任何一套都有洞实时比对挡不住比对脚本重启几分钟的空窗也挡不住算法覆盖不到的场景定时补偿周期再短也是事后补救白天高峰发现的差异要等到半夜才修业务方不接受审计也过不去。两套搭配的节奏白天实时链路边同步边比对差异直接进修复队列自动处理夜里跑一轮全量对账补漏第二天早上看对账报表差异归零才算过。跑了一个季度白天实时比对能处理九成以上的差异夜里补漏的量级通常在个位数到几十条主要是结构变更和极端乱序。没有夜里那轮兜底这些差异会一直躺在库里等到年底审计对不上总账才炸出来。1. 为什么不能只信同步工具自带的对账同步工具自带的监控大多只看链路状态进程活着、延迟在阈值内就认为正常。但覆盖写、事务乱序、目标端字段截断这些数据层面的问题链路监控全看不见。有一回目标端字段从 varchar(64) 改成 varchar(32)同步全程不报错超长内容静默截断对账才发现两边对不上业务差点拿着残缺数据去用。从那以后对账脚本自己写、自己跑、自己告警链路健康和数据一致分成两条线一条绿不代表另一条也绿。2. 一张表说清分工实时比对盯着增量和当天的热数据追求发现速度快间隔按分钟算定时补偿盯全量和历史冷数据追求覆盖完整按天跑。一个管快一个管全谁也替代不了谁。表级别还可以分级核心业务表两套都上变更很少的字典表只靠夜里全量兜底就够了省下来的资源留给大表。二、实时比对怎么落地实时比对的思路同步链路照常跑旁边再起一条比对链路两边按主键加时间戳取数逐条比对不一致的记下来进队列。落地有三个关键点取数窗口、比对用的键、差异分类。1. 取数窗口别拉太长也别卡边界比对脚本按时间窗口取增量比如每5分钟取最近10分钟内变更的数据。窗口要和间隔留重叠防止边界变更被两边都漏掉。重叠带来的重复比对没关系比对是幂等的漏一次就是差异。早期窗口设成5分钟取5分钟整点边界的变更总是对不上拖了两天才定位是游标回退问题改成10分钟窗口加5分钟间隔后这类差异再没出现过。-- 比对取数两边各取一份按主键拼上再逐字段比SELECTid,updated_at,checksum_colFROMsrc_tableWHEREupdated_atDATEADD(MINUTE,-10,GETDATE());窗口长短看写入节奏白天高峰窗口短一点夜里稀疏适当放大减少空转。参数放配置表调整不用重新发版。2. 比对用校验和别逐字段搬原值两边每个字段的原值都拉到比对程序逐个比量大时网络和内存吃不消源库连接也长期被占。做法是在数据库端按固定顺序拼行算校验和只拉回主键、校验和、更新时间三列不一致的行再回表拉原值定位字段。这一改把单次比对从四十多分钟压到五分钟内白天每小时跑一次也没压力。注意拼接顺序、空值填充、大小写规则两边必须一致任何不同都会制造一批假差异。3. 差异分三类处理别急着覆盖差异先分类再动手源头新目标旧是同步延迟等一个周期多数自己追平源头旧目标新是乱序覆盖要修一边有一边没有是丢数或多数要修还要查原因。不分类直接修会把延迟数据当差异覆盖越修越乱。修复统一走队列每条记录谁触发、改哪行、改前改后值内审时这个日志派上了用场。三、定时补偿怎么设计定时补偿是每天夜里跑的全量对账加修复逻辑和实时比对类似规模完全不同增量每次以千行计全量面对几千万行存量得换分层定位思路。1. 分桶校验和先定位再细查全量对账按主键哈希分桶两边各算每桶校验和不一致的桶才逐行比。一千行一桶两千万行的表分两万桶正常不一致的就几个逐行量从两千万降到几千夜里任务从通宵缩到一小时出头。桶大小要试太大定位不准太小算校验和本身就慢。按数据分布各定各的没有统一答案。# 分桶比对的骨架forbucket_idinrange(BUCKET_COUNT):src_sumsrc_conn.scalar(fSELECT SUM(row_crc) FROM t WHERE id %{N}{bucket_id})dst_sumdst_conn.scalar(fSELECT SUM(row_crc) FROM t WHERE id %{N}{bucket_id})ifsrc_sum!dst_sum:diff_buckets.append(bucket_id)2. 修复要限速别把库打挂夜里全量修复最怕几千条 update 一口气怼上去凌晨虽闲锁和日志盘也经不起猛写。修复队列按每秒几十条放批量但控包大小修完再跑验证比对校验和归零才收工。有一回修复语句没走索引变全表扫描凌晨三点把目标库 CPU 打满。从那以后修复 SQL 上线前必须在测试库看执行计划确认走索引才进队列这条规矩立了之后再没重演。3. 对账报表每天早上发出来夜里任务跑完自动生成对账报表总行数、差异数、修复数、遗留数按表分组发群。遗留数连续三天不为零就要升级排查多半是结构变更、字符集或源头脏数据需要人定规则。报表把数据一致性从技术黑盒变成所有人能盯的公开指标。四、两套方案的成本对比两套方案成本结构差别大选型要算清账资源花在什么时段、什么人身上。1. 实时比对的成本在白天比对链路占两边查询资源高峰期每小时一次、每次五分钟压力可控但不能忽略。源库是老系统取数尽量走只读副本或备库别直接怼主库。开发量不大取数、校验和、比对、修复队列四个模块两三个人周能落地维护主要是调窗口参数和优化取数SQL。2. 定时补偿的成本在夜里和存储全量对账扫全表凌晨的IO压力、校验和耗时、日志膨胀都是实打实的成本。好处是逻辑简单任务挂了重跑就行。开发量比实时小运维要留意夜间任务时长数据涨了桶数和超时时间要跟着调不然任务被杀第二天报表是空的。3. 预算上的考虑工具链开源商用都有开源省license但调度告警自己补商用按用户数报价。当时调度和告警用的搭贝的任务调度模块失败重试和超时告警都是现成的。取数和比对逻辑坚持自己写控制权在自己手里工具只负责把任务按时拉起来。五、踩过的三个坑方案本身不难难的是细节三个坑每个都花过真金白银的时间。1. 时间戳不可靠用updated_at当增量游标遇到过事务提交顺序和时间戳顺序不一致游标跳过了实际提交更晚的行造成漏比。补救是游标往前多回一段宁可重复不可漏。时间戳统一用数据库端时间生成别让应用服务器自己打时间时钟偏差制造的假差异排查起来特别有迷惑性。2. 字符集和空值两边库字符集不同同一中文字符串算出的校验和不一样对账天天报差异实际数据没问题排查一整天才定位。空值处理也要统一源头NULL到目标变空字符串校验和对不上但业务算一致进白名单不算差异不然假阳性淹没真差异。规则定期review字段下线规则跟着删。3. 表结构变更没有知会DBA在目标端改字段长度没走变更通知同步静默截断两周才被对账发现中间隔着月末结账补救花了一整个周末。后来定了规矩两端结构变更先过同步负责人评审比对脚本加元数据比对每天核一遍表结构定义类型、长度、可空性不一致就告警发现成本从两周降到一天。六、FAQQ实时比对的间隔设多短合适A5到15分钟比较实际。再短对库的压力明显上升而多数业务对几分钟的数据不一致有容忍度没必要追求秒级。真有秒级要求的表单独走双写加冲突检测不混在这套方案里混进来反而把整体复杂度抬高了。Q定时补偿每天跑一次够吗A对多数场景够。全量对账的意义是兜底不是追实时。如果白天实时比对的差异量一直很低一天一次没问题差异量突然变大说明链路本身有问题要查根因而不是加密对账频率频率解决不了链路的质量问题。Q两套方案能只用现成组件搭吗A可以。比对和修复本身就是脚本加调度调度可以用现成的任务调度工具当时用的搭贝的调度模块换别的也行。核心的比对逻辑建议自己写量不大可控性最高出了问题改起来也快不用等厂商排期。Q数据量到什么程度这套方案扛不住A单表过亿之后全量校验和的耗时会长到难以接受要改成按分区对账或者先做时间归档、只对活跃分区。实时比对的窗口也要相应拉长或者按业务重要性给表分级核心表高频比归档表一周比一次资源花在刀刃上。