重构期间新旧库同时写,最常见的问题是两边账对不上:流水条数不一致、余额差几分钱、单据号跳号或重复、日终报表两边数值不同。这类问题往往不是某一处代码写错,而是双写链路的取舍没有定清楚。下文按现象定位原因,给出逐项排查顺序,以及上线前后可以落地的预防措施。
先判断双写是怎么实现的,这决定了排查方向
同样是并行写入,链路差别很大。先确认属于下面哪一种,再往具体环节查,能省不少时间。
- 业务层双写:新旧系统各自调用接口写自己的库,中间靠程序顺序控制。灵活但一致性最脆,任一边失败都要单独处理。
- 数据同步中间件:从旧库变更日志或触发器投递到新库。链路短,但延迟、重复投递、字段映射问题是常态。
- 共享前置应用:统一入口先写主库,再由补偿任务写另一侧。可控性较好,对补偿任务的可靠性要求高。
判断完方式,就知道该把注意力放在应用代码、同步通道还是补偿任务上。
排查方向一:双写不是原子操作,中途失败无人接
两个库不在同一个事务里,第一边成功、第二边失败就是不一致的起点。常见表现是旧库有单、新库无单。
- 确认写入顺序:是先旧后新还是先新后旧,失败时是否有回滚或反向补偿。
- 检查异常是否被吞掉:接口调用超时、连接池耗尽时,程序是否只打了日志就继续往下走。
- 确认补偿任务是否存在、是否被正常调度、失败任务有没有进入重试队列。
- 抽样比对超时时段的数据,定位是否集中在某几个时间窗口。
工程上的通行做法是把补偿做成独立的对账任务,按业务主键拉两端数据比对,不依赖写入时的成功反馈。具体实现方式可参考技术实现上的一些常见做法,重点是对账任务要和写离线、可重跑。
排查方向二:主键与单据号在两边各生成一套
老系统常用数据库自增或序列生成单据号,新库换成分布式编号或雪花号。并行期内两套号共存,最容易出现跳号、重号和关联断链。
- 先核对编号规则:同一业务在两边是否共用前缀、是否有序、能否从号段推出业务归属。
- 检查新系统是否直接生成自己的号、又反写回旧系统,造成一处流水两套号。
- 核对关联关系:旧系统按单据号关联明细,重建后若号变了,关联要么保留原号做映射,要么整单重排。
建议在并行期统一由一个编号服务发号,两边写入时都取同一号;旧序号作为兼容字段保留,供回溯和打印使用。批量导入历史数据时尤其要守住这条,否则对账时会有一批数据两套编号对不上。
排查方向三:字段映射与精度处理丢了钱
金额、数量、税率这类字段,两边库类型和精度往往不同,迁移和双写过程中都可能被截断或四舍五入,余额差几十块的问题多出在这里。
- 逐字段核对数值类型:是否有的用浮点、有的用定点,小数位是否一致。
- 核对币种与单位:新旧系统是否统一了单位,是否存在元与分并存的字段。
- 检查汇总口径:旧系统按明细汇总、新系统按存储汇总值取数,中间是否丢了未同步的明细。
处理原则是金额统一走定点数,规定保留小数位,舍入只在一处做,其余环节只搬运不入舍。核对时用原币金额与换算金额分别比对,两种口径都平了才算通过。系统重构与数据迁移这类事,建议在POC阶段就把字段映射表定下来,并请业务方一起确认口径。
排查方向四:并发与重复投递造成重复入账
同步中间件重投、用户重复点击、定时任务重跑,都可能让同一笔业务在某一侧写入两次。典型现象是笔数偏多而金额对不上。
- 检查是否有业务幂等键,如业务单号加来源标识,并且数据库层面有唯一约束兜底。
- 确认同步组件是否支持断点续传和至少一次投递,同样要求消费端做幂等处理。
- 下游对账若按时间窗口全量重跑,要考虑已入账数据是否会被重复统计。
幂等与非原子写入之间还有配合关系:双写各环节都应先查幂等记录再写,唯一索引保证最终不会出现两条。接口重试机制的细节可参考站内关于系统集成与接口开发的相关内容。
排查方向五:外键、触发器与读写分离带来的隐性差异
旧库里常见的触发器和外键,在新架构里通常不再保留,职责被移到了应用层。如果旧库有触发器在悄悄写日志或汇总表,而新系统没有等价逻辑,对账必然对不上。
- 梳理旧库中的数据自动化逻辑:触发器、存储过程、定时作业分别做了什么。
- 确认新系统在应用侧补上了对应处理,且两边的执行先后不产生覆盖。
- 若使用了读写分离,核对从库延迟是否会让读取端看到旧值,而不是数据真的没写成功。
这一项的验收方式是对业务流程做端到端复核,不能只看表里有没有数据。政务和国企类项目往往涉及公开数据和台账,政府与国企国产化改造案例里的梳理经验可以作为参考。
推荐的排查顺序
按下面顺序推进,先定性质再定位,避免一上来就埋头查代码。
- 确认双写实现形态和写入链路,画出从入口到两个库的完整路径图。
- 锁定不一致的时间段、业务类型和来源,判断是个案还是批量。
- 核对笔数差额与金额差额,区分漏写、重复写和心理不平的口径问题。
- 检查单据号规则与主键映射,排除编号造成的关联错位。
- 逐字段核对金额、数量和边界值,确认是否有截断与舍入。
- 检查补偿任务、同步组件的运行日志与重试记录。
- 核对库内原表与汇总表是否同口径、是否都由应用侧维护。
- 最后才回到业务代码,定位具体的分支和分支时序。
这个顺序背后是投入回报比:链路和编号这类结构性问题一旦排除,后面代码层面的排查会快很多。
预防措施:上线前的核对清单
老系统重构一般要走较长的并行期,把下面这些方法提前落实到交付流程里,账目平衡问题会明显减少。如果牵涉多系统协同,可以参考我们的系统集成与接口开发服务。
- 编号集中发放,给出规则和范围,记录每次分发,避免手工打断。
- 关键单据使用业务幂等键加数据库唯一约束双重兜底。
- 落账并记录到清晰的流水表,而不是只依赖比对差异。
- 按业务主键的双向对账任务做成可重跑、可导出差异清单的工具。
- 金额字段统一采用定点数类型和固定小数位,舍入规则写成接口文档。
- 每天固定时点输出差异报表,由业务与技术一起过。
- 并行期切换主库和单写时点明确,并检查旧入口是否在切换时被关闭。
常见问题
两边余额差几分钱,一定要全量查到根源吗
先确认精度处理规则是否一致,再抽样核实际主体。如果差异只是由统一舍入导致的,调整统计口径比对即可;如果差额在个股随笔数变化,就必须追到写入链路。
并行期可以先只改读不改写吗
可以,先让新系统读、写入仍走旧系统,比对两边的查询结果,把口径和精度问题先解掉,再切为双写。这样能把问题范围缩小到一边。
对账发现的历史差异需要全部修坟吗
按业务规则区分数据的重要性。对外和对账有影响的必须补差,内部统计类可合并归正并在报告中留痕,关键是每处差异都记录处理方式。
并购这类并行重构项目,各行业的验收口径差异较大,我们做过的同类项目案例覆盖制造、物流与金融等行业,可以按业务上下文对照参考。