当前位置:首页 > 问答 > 正文

说说怎么搭个通用的分布式事务框架,别太复杂但得靠谱

要搭一个“不太复杂但得靠谱”的通用分布式事务框架,核心思想是不强求所有操作在同一时刻完成真正的原子提交,而是通过一系列的后置操作,保证所有参与方的数据最终会达到一致状态,这条路子通常被称为“最终一致性”,下面我们一步步拆解怎么实现。

明确核心目标:可靠通知与状态追踪

别一上来就想搞两阶段提交那种复杂的东西,我们的框架首要目标是:当一个跨服务的业务操作发起后,框架要能可靠地确保每个参与的服务都知道自己该做什么,并且如果中途失败了,框架有能力知道失败在哪里,并能重新驱动流程直到成功。

这就像你是个项目经理,接到一个大任务(分布式事务),你把它拆成几个小任务(子事务),分给不同部门(微服务),你的工作不是逼着所有部门在同一秒签字画押,而是确保每个部门最终都完成了自己的任务,并且有办法知道谁做完了、谁没做完、没做完的要去催。

设计核心组件

一个最简单的框架需要三个核心部件:

  1. 事务协调器: 这是框架的大脑,它负责发起一个全局事务,记录这个事务的总体状态(进行中、已成功、已失败),并驱动整个流程,它可以是一个独立服务,也可以嵌入在某个核心服务中,为了简单,我们先把它做成一个内嵌的库。
  2. 事务日志表: 这是框架的记忆,极其重要,必须在数据库中创建一张表,用来记录每一个分布式事务的踪迹,这张表至少要包含这些字段:
    • 事务ID:全局唯一标识,代表这一整笔业务。
    • 事务状态0-进行中1-已成功2-已失败
    • :存放发起事务时的主要参数,订单ID:123,扣款金额:100元”,这个是为了后续重试时能用上。
    • 重试次数:记录这个事务已经被尝试处理了多少次。
    • 创建时间 / 更新时间:用于监控和清理超时事务。
  3. 消息队列: 这是框架的神经系统,用它来异步地通知各个参与服务执行任务,订单服务创建订单后,不是直接调用库存服务,而是向消息队列发一条“请为订单123预留库存”的消息,这样做的好处是解耦,即使库存服务暂时宕机,消息也会在队列里等着,不会丢失。

定义工作流程(以“下单扣库存”为例)

现在我们把组件串起来,看一个典型流程:

  1. 开启事务: 用户下单,订单服务开始处理,它首先在自己的数据库里,在一个本地数据库事务中,做两件事:

    • 插入订单记录(状态为“待付款”之类的中间状态)。
    • 事务日志表插入一条新记录,生成全局事务ID,状态为“进行中”,并保存下单信息。
    • 这个“插订单+插事务日志”的操作必须在同一个数据库事务里,保证要么都成功,要么都失败,这是整个框架可靠性的基石。
  2. 发送异步消息: 订单服务向消息队列发送一条消息,主题是“库存锁定”,内容是“事务ID=XXX,订单ID=123,商品SKU=ABC,数量=1”,发完消息后,订单服务就可以给用户返回“下单成功”了。注意: 这里可能因为网络问题发送失败,所以通常需要有一个“本地消息表”的机制来保证消息一定能发出去(为了简化,我们可以先使用支持事务消息的MQ,比如RocketMQ,它能在本地事务提交后保证消息必达)。

  3. 消费消息执行子事务: 库存服务一直监听着“库存锁定”队列,它收到消息后,开始处理:

    • 解析消息,得到事务ID和业务参数。
    • 执行本地事务:检查库存,然后扣减对应的可用库存(或生成一个预占记录)。
    • 如果扣减成功,库存服务可以更新一下事务协调器的状态(比如调用协调器的一个接口,告知事务ID对应的库存环节已成功),或者更简单的,直接发一条“库存锁定成功”的消息到下一个队列(如果需要后续步骤的话),如果扣减失败(比如库存不足),则标记事务失败。
  4. 处理失败与重试: 这是体现“靠谱”的关键。

    • 场景1:库存服务宕机,没收到消息。 没关系,消息会一直在队列里,等库存服务重启后,它会继续消费。
    • 场景2:库存服务处理时出了bug,处理失败。 框架应捕获异常,不要确认消费这条消息(让消息重回队列),过一会儿重试,为了避免无限重试和“毒药消息”,需要配合重试次数字段,达到一定次数后,就将该事务标记为“人工干预”状态,并发出告警。
    • 场景3:需要反向操作(回滚)。 比如下单后支付失败,需要释放库存,我们的框架可以这么做:支付服务支付失败后,向消息队列发送一条“订单取消”的消息,库存服务消费后,执行释放库存的操作,这其实就是通过补偿动作达到最终一致性。
  5. 最终扫描与兜底: 光靠消息重试还不够万无一失,我们需要一个“兜底”的扫描任务(一个定时Job),这个Job定期去扫描事务日志表,找出那些长时间处于“进行中”状态的事务,对于这些“悬挂”的事务,扫描任务可以尝试重新发布消息,或者根据日志内容直接查询下游服务确认状态,从而推动事务向前完成。

需要特别注意的要点

  • 幂等性: 这是生命线!因为网络问题和重试机制,任何一个服务都可能收到多次相同的请求,库存服务在扣减库存时,必须判断“这个事务ID是否已经处理过”?可以通过在库存服务也建一张事务记录表,在处理前先查询一下,如果已经处理过就直接返回成功,避免重复扣减。
  • 数据一致性级别: 要明确告诉业务方,这个框架提供的是“最终一致性”,在极端情况下,用户可能会看到订单成功了但库存还没扣,或者短暂超卖,如果业务无法接受,那就不适合用这个简单框架。
  • 监控与告警:事务日志表中长时间未完成的事务、重试次数过多的事务,一定要有监控和告警,方便人工及时介入处理。

这个框架的精髓是:通过本地事务+异步消息+重试机制+幂等保证,将一个大事务分解成多个小步骤,通过不断重试和补偿,让系统数据最终保持一致。 它牺牲了强一致的实时性,换来了系统的可用性和简单性,对于大多数电商、互联网业务场景来说,是足够靠谱且不复杂的选择。

说说怎么搭个通用的分布式事务框架,别太复杂但得靠谱