前言

数据不一致是业务系统中最敏感的问题。订单支付成功但状态未更新、库存扣了但订单失败、优惠券核销了但订单取消,这些都不是简单的技术异常,而是会影响资金、库存和用户体验。

典型现象

  • 支付成功但订单仍是待支付。
  • 库存扣减成功但订单创建失败。
  • MQ 消息发送成功但消费失败。
  • 用户积分重复发放。
  • 报表数据和明细数据对不上。
  • 主库和搜索索引数据不一致。

排查流程

flowchart TD
    A[发现数据不一致] --> B[确定业务主记录]
    B --> C[查看状态流转日志]
    C --> D[查看本地事务]
    D --> E[查看MQ消息]
    E --> F[查看消费和补偿]
    F --> G[对账确认影响范围]
    G --> H[修复数据]
    H --> I[补齐防重和监控]

先找业务主记录

排查数据不一致不能先看代码,要先确定主记录:

  • 订单号。
  • 支付流水号。
  • 库存流水号。
  • 优惠券核销记录。
  • MQ messageId。
  • traceId。

没有唯一业务号,就很难串起完整链路。

常见原因

本地事务边界错误

比如订单表插入成功,订单明细插入失败,但异常被吞掉,导致主从表不一致。

处理方式:

  • 明确事务边界。
  • 异常不能随意 catch 后不抛。
  • 使用事务注解时注意同类方法调用失效。
  • 事务内不要做远程调用。

MQ 发送和数据库事务不一致

数据库提交成功,但消息发送失败,或者消息发送成功但数据库回滚。

解决方案:

  • 本地消息表。
  • 事务消息。
  • Outbox 模式。
  • 定时扫描补偿。

企业里常用本地消息表:业务数据和消息记录在同一个本地事务提交,然后后台任务投递 MQ,投递成功后更新消息状态。

消费失败或重复消费

消费失败会导致后续数据没更新,重复消费会导致重复扣减或重复发放。

解决方式:

  • 消费幂等。
  • 消费记录表。
  • 状态机前置判断。
  • 死信队列。
  • 补偿重放工具。

缓存和数据库不一致

写库成功后删缓存失败,或者缓存重建时读到旧数据。

处理方式:

  • 先更新数据库,再删除缓存。
  • 删除失败进入重试。
  • 重要数据使用延迟双删或订阅 Binlog。
  • 允许短时间最终一致时明确窗口。

对账机制

对账不是只有支付系统才需要。凡是跨系统、异步、最终一致的链路都需要对账。

对账内容:

  • 主业务表和流水表。
  • 本地库和 MQ 消费结果。
  • 支付渠道和本地支付单。
  • 库存流水和库存余额。
  • DB 和 ES 索引。

对账结果要能生成差异单,支持自动修复和人工确认。

修复原则

  • 先冻结影响范围,避免继续扩大。
  • 以权威数据源为准。
  • 修复前备份数据。
  • 修复脚本可重复执行。
  • 修复过程记录审计日志。
  • 修复后重新对账。

资金类数据不能直接手工改状态,必须通过补单、冲正、调账等业务动作修复。

生产止血

  • 暂停异常入口。
  • 关闭相关异步消费者。
  • 对问题用户或订单加白名单处理。
  • 回滚最近发布。
  • 补偿失败消息。
  • 启动对账任务确认影响范围。

高频面试题

数据不一致怎么排查?

先确定业务主键和权威数据源,再按状态流转、本地事务、MQ 投递、消费结果、缓存和对账记录逐层排查,最后通过补偿任务或修复脚本处理。

最终一致性怎么保证?

通过本地消息表、事务消息、幂等消费、重试、死信、补偿任务和对账机制保证。关键是允许短时间不一致,但必须可发现、可补偿。

修复数据要注意什么?

要备份、可回滚、可重复执行、有审计日志,并以权威数据源为准。资金和库存场景必须走业务修复流程。

总结

数据不一致排查不是单点技术问题,而是完整业务链路问题。面试回答要体现事务、消息、幂等、补偿和对账的闭环。