前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微服务:事务管理

微服务:事务管理

作者头像
oec2003
发布2022-12-01 11:40:41
3980
发布2022-12-01 11:40:41
举报
文章被收录于专栏:不止dotNET

几乎所有的信息管理系统都会涉及到事务,事务的目的是为了保证数据的一致性,这里说的一致性是数据库状态的一致性。

说到数据库状态的一致性,相信大家都会想到 ACID :

  • 原子性(Atomic):在一个事件的多个数据库操作中,要么同时成功,要么同时失败,例如:转账业务;
  • 隔离性(Isolation):不同的业务之间处理数据相互独立,互不影响;
  • 持久性(Durability):正常提交的数据能够被持久化,不丢失数据,比如 mysql 天然就能持久化,redis 、 rabbitmq 也能通过设置进行持久化;
  • 一致性(Consistency):最终的数据正确,所以说是通过 AID 这些手段来保证了 C 。

在单体架构中,通常是一套程序对应一个数据库,事务基于数据库本身的能力,如果你在 .NET Core 中使用 dapper 或 sqlsugar ,可以很容易进行事务的处理,可以参考下面文档:

https://dapper-tutorial.net/transaction

https://www.donet5.com/Home/Doc?typeId=1183

但是,在微服务架构,分布式的场景中,事务的处理就会变得复杂,会存在多个节点,多个节点的同步、可用性等都是需要考虑的问题,在分布式中有一个著名的 CAP 理论:

  • C:数据一致性(Consisitency):分布式中存在多个节点,对某个指定的客户端来说,从任一节点读取的数据保证获取到的是最新写入的数据;
  • A:可用性(Acailability),非故障节点在合理的时间内返回合理的响应(不是错误和超时的响应);
  • P:分区容错性(Partition Tolerance),节点之间的数据传递是基于网络的,由于网络本身不是 100% 可靠,极端情况下会出现网络不可用的情况,进而将网络两端的节点分隔开来,这就是所谓的「网络分区」现象。在出现网络分区时,两部分的数据是不一致的,如果要保证数据的一致性,就必须要让没有及时同步数据的节点变为不可用,这就牺牲了可用性,否则就会牺牲一致性,所以在 P 一定存在的情况下,需要在 C 和 A 中间做取舍。

我们在 CAP、ACID 中讨论的一致性称为「强一致性」(Strong Consistency),而把牺牲了 C 的 AP 系统,但又要保证最终的结果是一致的,称为「弱一致性」,也叫最终一致性。最终一致性的概念由 eBay 的系统架构师丹 · 普利切特(Dan Pritchett)在 2008 年发表于 ACM 的论文「Base: An Acid Alternative」中提出的。

本文主要说下保证一致性的几种方式:TCC、SAGA 和消息队列。

TCC

TCC 是 Try-Confirm-Cancel 的缩写,表示将整个过程分为了三个阶段:

  • Try:一个请求涉及到多个服务,多个服务会同时进行 Try,这个阶段为尝试执行阶段,在这个阶段中会进行数据的校验、检查,保障一致性,并准备资源,都成功会进入到 Confirm 阶段;
  • Confirm:确认执行阶段,不进行任何业务检查,多个服务的 Try 都执行成功了,多个服务都进入到 Confirm 阶段,在这个阶段直接使用 Try 阶段准备的资源来完成业务处理。注意,Confirm 阶段可能会重复执行,因此需要满足幂等性。
  • Cancel:如果在 Try 阶段有一个服务没有成功,那么所有的服务都进入到 Cancel 阶段,在该阶段,释放 Try 阶段预留的业务资源。注意,Cancel 阶段也可能会重复执行,因此也需要满足幂等性。

在 .NET Core 中可以参考:

https://github.com/simpleway2016/JMS

在 Java 中可以使用 seata:

https://github.com/seata/seata https://seata.io/zh-cn/

因为在 TCC 中的第一步 Try 需要预留资源,进行检查和校验,但在某些场景下,资源不是我们所能控制的,比如支付中,余额是银行管理的,我们通常没有权限。所以这时就不太适合 TCC ,可以考虑用 SAGA 来代替 TCC。

SAGA

SAGA 起源于 1987 年普林斯顿大学的赫克托 · 加西亚 · 莫利纳(Hector Garcia Molina)和肯尼斯 · 麦克米伦(Kenneth Salem)在 ACM 发表的一篇论文《SAGAS》。

SAGA 和 TCC 最大的区别是基于数据补偿机制来代替回滚。一个 SAGA 表示处理多个服务中数据的一系列操作,由一连串的本地事务组成,每个独立的本地事务中还是能够使用 ACID 。

SATA 由两部分组成:

  • 将一个大的事务拆分成的若干个小的事务,比如一个大的事务 T ,拆分成 T1、T2、T3;
  • 每一个子事务有对应的补偿动作,例如对应上面的 T1、T2、T3 有 C1、C2、C3 的补偿动作。

在 ACID 中如果出现异常,可以很容易进行回滚,但 SAGA 没办法自己回滚,必须依赖补偿动作来进行回滚。

如果 T1、T2、T3 都提交成功了,整个事务 T 就提交成功,如果执行 T2 时出现异常,这时有两种方式进行处理:

正向(不断重试):不断对 T2 进行重试操作,直到成功(不排除人工干预),等 T2 重试成功后,继续执行后面的 T3;

反向(补偿):T2 出现异常时,执行对应的补偿 C2,C2 必须执行成功(不排除人工),然后执行 T1 对应的补偿动作 C1 。

在上面提到的 seata 中也同样可以支持 SAGA 模式。

除了 seata ,还有一个用 go 语言写的 DTM 分布式事务框架也不错:

https://dtm.pub/ https://github.com/dtm-labs/dtm

重要的是,DTM 支持 C# 客户端:

https://github.com/dtm-labs/dtmcli-csharp

消息队列

消息队列相信大家都不陌生,我们零代码产品中调用外部接口的组件,会被用在一些复杂的业务逻辑编排中,对外部接口的调用就是使用消息队列,RabbitMQ 的延时队列加上死信队列可以来进行重试的操作,来保证数据的最终一致。

还有另一种方式就是使用事务消息表,比如有这样一个场景,在系统列表中删除一条流程数据,这时需要做:

1、列表服务中对数据进行删除;

2、文件服务对这条数据相关的附件进行删除;

3、流程服务对该业务数据的所有流程信息进行删除。

具体的步骤如下:

1、列表服务删除数据成功后,在数据库中创建一张事务消息表,该表中记录事务 ID、数据删除成功的状态、业务数据 ID、附件待删除的状态、流程信息待删除的状态等;

2、列表服务删除数据成功后,发送消息分别进行附件删除处理和流程信息删除处理;

3、消息被正确处理后,修改事务消息表的状态;

4、创建一个单独的消息服务程序,轮询扫描事务消息表,如果发现状态没有变成已完成,就重新发送一个新的消息,这样附件删除和流程信息删除就会进行多次执行,这也要求这些操作必须是幂等的。

RabbitMQ 本身不支持分布式事务,不过有一些消息中间件是支持的,例如:RocketMQ,原生就支持分布式事务操作,可以更方便进行事务处理。

本文是一些理论的梳理,要想更彻底地掌握,可以选择一个框架,找几个场景,写写代码演练一下。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 不止dotNET 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • TCC
  • SAGA
  • 消息队列
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档