数据一致性分布式事务保证

wen java案例 2

本文目录导读:

数据一致性分布式事务保证

  1. 追求强一致性(ACID,牺牲部分可用性)
  2. 追求最终一致性(BASE,牺牲强一致性,换取高性能)
  3. 如何选择?一张表格总结
  4. 核心建议

这是一个非常核心且复杂的分布式系统问题。没有任何一种“万能”的方案能完美解决所有场景下的数据一致性,需要根据业务对一致性、性能、可用性的要求来权衡选择。

我们先明确核心矛盾:分布式事务要保证多个独立数据库或服务之间的数据状态最终(或实时)保持一致。

以下是目前主流的保证数据一致性的方案,从强一致性最终一致性排列:

追求强一致性(ACID,牺牲部分可用性)

适用于金融、证券、库存扣减等对一致性要求极高的场景。

两阶段提交

这是最经典的强一致性协议,由一个协调者和多个参与者组成。

  • 阶段一(准备阶段): 协调者询问所有参与者“能提交吗?”,参与者执行事务,写Undo/Redo日志,但不提交,返回“是”或“否”。

  • 阶段二(提交阶段):

    • 如果所有人都说“是”,协调者发“全局提交”命令。
    • 如果有人说了“否”或超时,协调者发“全局回滚”命令。
  • 优点: 理论上保证了强一致性。

  • 缺点(致命):

    • 同步阻塞: 在准备阶段,所有参与者都锁着资源(如数据库行锁),性能极差。
    • 单点故障: 协调者挂了,所有参与者都会一直锁着资源。
    • 脑裂风险: 协调者在发“提交”命令时挂了,有些参与者收到了提交,有些没收到,导致数据不一致(这也是2PC最大的问题)。
  • 现状: 几乎不在高并发互联网场景使用,主要用于单机数据库的XA协议或特定内部系统。

追求最终一致性(BASE,牺牲强一致性,换取高性能)

适用于大多数互联网场景(如订单、社交、内容发布)。

TCC

TCC是一种补偿型事务,它对业务侵入性较强,但性能远高于XA。

它将一个分布式事务拆分为三个步骤:

  1. Try(预留资源): 检查业务资源可用性,并预留资源(如冻结库存、冻结账户余额)。
  2. Confirm(确认提交): 如果所有Try都成功,执行业务操作(实际扣减库存、实际扣款)。要求保证幂等性。
  3. Cancel(取消回滚): 如果任意Try失败,执行补偿操作,释放Try阶段预留的资源(解冻库存、解冻余额)。要求保证幂等性。
  • 优点: 性能高,无全局锁,避免了XA的同步阻塞问题,可以定制化的补偿逻辑。
  • 缺点:
    • 开发成本极高: 每个业务接口都需要拆成Try/Confirm/Cancel三个方法。
    • 空回滚: Try未执行就收到了Cancel请求。
    • 悬挂问题: Cancel先于Try到达。

基于消息的最终一致性

这是目前最常用、最推荐的方案,核心思想是:只要保证本地事务和消息发送的原子性,就能通过异步消息达成最终一致。

  • 本地消息表

    1. 在业务数据库里创建一张本地消息表
    2. 在一个本地事务中,同时执行“业务操作(插入订单)”和“向本地消息表插入一条待发送消息”。这两个操作要么全成功,要么全失败。
    3. 通过一个定时任务扫描本地消息表中的待发送消息,发送给MQ。
    4. 下游消费者(如库存服务)消费消息并执行业务(扣库存)。
    5. 下游执行成功后,回调上游服务,更新本地消息表状态为“已处理”。
    6. 失败处理: 如果下游执行失败,可以重试,如果多次重试失败,可以转为人工介入。
  • 优点: 简单可靠,无第三方依赖。

  • 缺点: 需要维护本地消息表,对业务有侵入,且消息表可能成为数据库瓶颈。

  • RocketMQ 事务消息(推荐) 这是对本地消息表的优化,由MQ中间件实现。

    1. 半消息: 生产者向RocketMQ发送一条“半消息”(暂不可见)。
    2. 执行本地事务: 生产者执行本地业务(如创建订单),如果本地事务成功,提交半消息;如果失败,回滚半消息。
    3. 回查: 如果生产者宕机,RocketMQ会定期回调生产者的接口,询问“刚才那个本地事务到底成功了没?”,生产者根据数据库状态回复“提交”或“回滚”。
    4. 消息被成功提交后,下游消费者正常消费。
  • 优点: 无业务侵入,无需建表,消息可靠。

  • 缺点: 依赖RocketMQ,且理论上有短暂的不一致窗口期(半消息 -> 提交之间)。

Saga 模式

一种长事务解决方案,将一个长事务拆分成一系列子事务,每个子事务都有对应的补偿操作。

  • 实现方式:

    • 编排(Choreography): 各个服务通过监听彼此的事件来完成协调(如:订单服务 -> 支付服务 -> 库存服务)。
    • 协调(Orchestrator): 一个中央协调器负责告诉各个服务“该做什么”,并处理失败后的补偿。
  • 优点: 适合长事务、跨多个系统。

  • 缺点: 补偿操作难度大,需要经验,协调器模式引入了单点。

如何选择?一张表格总结

方案 一致性级别 性能/吞吐 开发成本 典型场景 代表技术
2PC/3PC 强一致性 极低 金融核心、跨库严格一致 XA、Seata AT
TCC 最终一致性 极高 高并发、敏感的扣款/扣库存 Seata TCC
本地消息表 最终一致性 传统、简单的异步解耦 自研
RocketMQ 事务消息 最终一致性 极高 互联网主流选择(推荐) RocketMQ
Saga 最终一致性 长流程、跨组织、可回滚的复杂业务 Seata Saga、Temporal

核心建议

  1. 尽量不要使用分布式事务! 最好的解决方案是避免跨库/跨服务的强一致事务,通过数据冗余(如将用户信息复制到订单库)、去数据库依赖(从缓存/ES查询数据)等方式,让业务尽可能在一个数据库实例内完成。
  2. 能异步就别同步: 大部分业务可以接受“几秒后不一致”,用异步消息 + 重试机制解决99%的问题。
  3. 做好幂等性和最终补偿: 无论用哪种方案,所有操作(尤其消费消息和补偿)都必须是幂等的,设计好兜底方案,比如人工核对、定时对账、手动补偿脚本。
  4. 选成熟的框架: 如果必须用,使用 SeataRocketMQ 这类成熟的中间件,不要自己造轮子。

最后总结:

  • 性能第一,能接受短暂不一致?RocketMQ 事务消息(首选)。
  • 一致性要求极高,且性能不重要?Seata AT(基于2PC优化)
  • 业务极其复杂,需要灵活补偿?Seata TCCSaga
  • 最简单的兜底方案?本地消息表 + 定时任务 + 重试 + 监控告警 + 人工介入

抱歉,评论功能暂时关闭!