Golang微服务如何处理分布式事务 讲解TCC和本地消息表方案

Golang微服务如何处理分布式事务 讲解TCC和本地消息表方案
最新回答
悲伤∩侵蚀的笑

2021-09-07 06:11:41

Golang微服务如何处理分布式事务:TCC和本地消息表方案详解

在微服务架构中,Golang编写的微服务同样面临跨服务数据一致性问题,TCC和本地消息表是两种常见且落地性强的分布式事务解决方案。

TCC方案

定义与原理

TCC(Try-Confirm-Cancel)是一种补偿型事务机制,将一次分布式操作拆分为三个阶段:

  • Try(资源检查与锁定):预留业务资源,检查相关资源的可用性并进行锁定。
  • Confirm(执行业务操作):在Try阶段成功后,执行实际的业务操作,将预留资源转为正式使用。
  • Cancel(回滚):当Try阶段或Confirm阶段出现失败时,释放Try阶段预留的资源,恢复到事务执行前的状态。

TCC不依赖全局事务协调器,而是通过业务代码自行控制事务流程。

使用场景

适用于对一致性要求较高、涉及多个服务同时变更状态的场景,如支付、订单、库存等业务。例如在下单减库存加订单的场景中:

  • Try阶段:订单服务创建订单但状态设为“待确认”,库存服务预扣库存。
  • Confirm阶段:若两个服务都执行成功,则正式提交,订单状态变为“已确认”,库存真正减少。
  • Cancel阶段:若任一服务执行失败,调用Cancel接口释放资源,订单取消,库存回退。
Golang中的实现

在Golang中,可以使用一些框架如DTM,或者自建流程引擎来管理TCC的三个阶段。关键在于每个服务都要提供Try/Confirm/Cancel三个接口,并且保证这些接口的幂等性。

注意事项
  • 幂等性设计:Confirm和Cancel操作要设计成幂等的,防止网络重传导致重复执行。
  • 事务协调器:需要一个事务协调器记录状态,用于在失败后自动重试或人工介入。

本地消息表方案

定义与原理

本地消息表是一种轻量级的最终一致性方案,其核心思想是在本地数据库中记录一个事务消息,再异步发送给其他服务。借助数据库的事务特性,保证本地操作和消息写入的一致性。

使用场景

适合对实时性要求不高、但又需要可靠消息传递的场景,如日志记录、通知类操作。例如在用户注册送积分的服务中:

  • 用户服务在一个事务里完成插入用户数据的同时,往一张message_log表里写一条“发积分”的消息。
  • 后台有个定时任务不断扫描这张表,把消息发到MQ或者直接调用积分服务API。
  • 积分服务收到后执行加分操作,并返回结果,用户服务再标记这条消息为已处理。
实现要点
  • 事务一致性:消息写入和主业务操作必须在同一个数据库事务中,以确保两者要么都成功,要么都失败。
  • 重试机制:消息处理要有重试机制,避免因处理失败导致消息丢失。
  • 幂等性:建议加上唯一标识做幂等,防止重复处理同一条消息。

TCC与本地消息表的对比与选型

对比
  • 一致性要求

    TCC适合强一致性场景,能够保证多个服务同时变更状态时数据的一致性。

    本地消息表适合异步解耦场景,允许短暂的不一致,最终达到一致性。

  • 实现复杂度

    TCC实现复杂,需要维护三段式逻辑,开发成本较高。

    本地消息表实现简单,不依赖第三方事务中间件,成本较低。

  • 实时性

    TCC实时性强,能够及时处理事务。

    本地消息表存在延迟,因为是通过异步方式处理消息。

选型建议
  • 如果服务间交互频繁、对一致性要求高,如涉及支付、订单、库存等核心业务的组合操作,可以考虑引入TCC方案。
  • 如果只是需要异步保障消息送达,如日志记录、通知类操作等对实时性要求不高的场景,本地消息表会更轻便。

总之,TCC和本地消息表各有优劣,在实际选型时,需要综合考虑业务需求和技术能力,选择最适合的方案来解决Golang微服务中的分布式事务问题。