图解分布式事务实现原理(二)

news2025/1/22 9:14:27

参考

本文参考https://zhuanlan.zhihu.com/p/648556608,在小徐的基础上做了个人的笔记。


TCC 实现方案

TCC 概念简述

TCC(Try-Confirm-Cancel)是一种分布式事务处理模式,旨在保证分布式系统中的事务一致性。它的核心思想是将一个分布式事务分解为多个子事务,每个子事务都有三个关键操作

  • Try:在Try阶段,子事务尝试执行业务检查、资源预留和其他必要的前置操作,以准备好执行实际的业务。如果Try操作成功,表示可以继续执行后续的Confirm操作。

  • Confirm:在Confirm阶段,子事务执行实际的业务确认操作,将之前预留的资源实际分配,完成业务处理。只有当所有分支事务的Try操作都成功,并且TM发起Confirm操作时,才会真正确认事务的执行。

  • Cancel:在Cancel阶段,子事务执行与Try操作相反的操作,即回滚操作,用于撤销之前的资源预留和业务处理。如果Try操作失败,TM将发起所有分支事务的Cancel操作。

**TCC的分布式事务管理器(TM)**首先发起所有分支事务的Try操作,如果有任何一个Try操作失败,TM将会发起所有分支事务的Cancel操作,以确保分布式事务的一致性。只有当所有分支事务的Try操作都成功时,TM才会发起所有分支事务的Confirm操作,最终完成整个事务的确认。

TCC模式通过将分布式事务拆解成多个子事务并明确Try、Confirm和Cancel操作,提供了更细粒度的事务控制,能够适应不同业务场景的需求。然而,实现TCC模式需要开发者谨慎处理异常、重试机制和分布式事务管理,因此具有一定的复杂性

分支事务成功的场景

在这里插入图片描述

分支事务失败的场景

在这里插入图片描述

TCC 宏观架构

下面是 TCC 分布式事务实现方案的整体架构,大家可以先整体浏览一下存个印象,下面我们会逐一展开介绍:

在这里插入图片描述

在 TCC 分布式事务架构中,包含三类角色:

  • 应用方 Application:指的是需要使用到分布式事务能力的应用方,即这套 TCC 框架服务的甲方
  • TCC 组件 TCC Component:指的是需要完成分布式事务中某个特定步骤的子模块. 这个模块通常负责一些状态数据的维护和更新操作,需要对外暴露出 Try、Confirm 和 Cancel 三个 API:
    • Try:锁定资源,通常以类似【冻结】的语义对资源的状态进行描述,保留后续变化的可能性
    • Confirm:对 Try 操作进行二次确认,将记录中的【冻结】态改为【成功】态
    • Cancel:对 Try 操作进行回滚,将记录中的【冻结】状消除或者改为【失败】态. 其底层对应的状态数据会进行回滚
  • 事务协调器 TX Manager:负责统筹分布式事务的执行:
  • 实现 TCC Component 的注册管理功能
  • 负责和 Application 交互,提供分布式事务的创建入口,给予 Application 事务执行结果的响应
  • 串联 Try -> Confirm/Cancel 的两阶段流程. 在第一阶段中批量调用 TCC Component 的 Try 接口,根据其结果,决定第二阶段是批量调用 TCC Component 的 Confirm 接口还是 Cancel 接口

TCC 案例分析

下面我们引入一个具体的分布式事务场景问题,并通过 TCC 架构加以实现,帮助大家进一步提高对 TCC 分布式事务方案的感性认识.

现在假设我们需要维护一个电商后台系统,需要处理来自用户的支付请求. 每当有一笔支付请求到达,我们需要执行下述三步操作,并要求其前后状态保持一致性:

  • 在订单模块中,创建出这笔订单流水记录
  • 在账户模块中,对用户的账户进行相应金额的扣减
  • 在库存模块中,对商品的库存数量进行扣减

上面这三步操作分别需要对接订单、账户、库存三个不同的子模块,底层的状态数据是基于不同的数据库和存储组件实现的,并且我们这套后台系统是基于当前流行的微服务架构实现的,这三子个模块本身对应的就是三个相互独立的微服务,因此如何实现在一笔支付请求处理流程中,使得这三笔操作对应的状态数据始终保持高度一致性,就成了一个非常具有技术挑战性的问题.

在这里插入图片描述

首先,我们基于 TCC 的设计理念,将订单模块、账户模块、库存模块分别改造成三个 TCC Component,每个 Component 对应需要暴露出 Try、Confirm、Cancel 三个 API,对应于冻结资源、确认更新资源、回滚解冻资源三个行为.

同时,为了能够简化后续 TX Manager 和 Application 之间的交互协议,每个 TCC Component 会以插件的形式提前注册到 TX Manager 维护的组件市场 Component Market 中,并提前声明好一个全局唯一键与之进行映射关联.

在这里插入图片描述
由于每个 TCC Component 需要支持 Try 接口的锁定操作,因此其中维护的数据需要在明细记录中拆出一个用于标识 “冻结” 状态的标签,或者在状态机中拆出一个 “冻结” 状态.

最终在第二阶段的 Confirm 或者 Cancel 请求到达时,再把 ”冻结“ 状态调整为 ”成功“ 或者 ”失败“ 的终态.

在这里插入图片描述
下面描述一下,基于 TCC 架构实现后,对应于一次支付请求的分布式事务处理流程:

  1. Application 调用 TX Manager 的接口,创建一轮分布式事务:
  2. Application 需要向 TX Manager 声明,这次操作涉及到的 TCC Component 范围,包括 订单组件、账户组件和库存组件
  3. Application 需要向 TX Manager 提前传递好,用于和每个 TCC Component 交互的请求参数( TX Manager 调用 Component Try 接口时需要传递)
  4. TX Manager 需要为这笔新开启的分布 式事务分配一个全局唯一的事务主键 Transaction ID
  5. TX Manager 将这笔分布式事务的明细记录添加到事务日志表中
  6. TX Manager 分别调用订单、账户、库存组件的 Try 接口,试探各个子模块的响应状况,比并尝试锁定对应的资源
  7. TX Manager 收集每个 TCC Component Try 接口的响应结果,根据结果决定下一轮的动作是 Confirm 还是 Cancel
  8. 倘若三笔 Try 请求中,有任意一笔未请求成功:
    • TX Manager 给予 Application 事务执行失败的 Response
    • TX Manager 批量调用订单、账户、库存 Component 的 Cancel 接口,回滚释放对应的资源
    • 在三笔 Cancel 请求都响应成功后,TX Manager 在事务日志表中将这笔事务记录置为【失败】状态
  9. 倘若三笔 Try 请求均响应成功了:
    • TX Manager 给予 Application 事务执行成功的 ACK
    • TX Manager 批量调用订单、账户、库存 Component 的 Confirm 接口,使得对应的变更记录实际生效
      在三笔 Confirm 请求都响应成功后,TX Manager 将这笔事务日志置为【成功】状态

在这里插入图片描述
在上述流程中,有一个很重要的环节需要补充说明,首先,TCC 本质上是一个两阶段提交(Two Phase Commitment Protocol,2PC)的实现方案,分为 Try 和 Confirm/Cancel 的两个阶段

  • Try 操作的容错率是比较高的,原因在于有人帮它兜底. Try 只是一个试探性的操作,不论成功或失败,后续可以通过第二轮的 Confirm 或 Cancel 操作对最终结果进行修正
  • Confirm/Cancel 操作是没有容错的,倘若在第二阶段出现问题,可能会导致 Component 中的状态数据被长时间”冻结“或者数据状态不一致的问题

针对于这个场景,TCC 架构中采用的解决方案是:在第二阶段中,TX Manager 轮询重试 + TCC Component 幂等去重. 通过这两套动作形成的组合拳,保证 Confirm/ Cancel 操作至少会被 TCC Component 执行一次.

首先,针对于 TX Manager 而言:

  • 需要启动一个定时轮询任务
  • 对于事务日志表中,所有未被更新为【成功/失败】对应终态的事务,需要摘出进行检查
  • 检查时查看其涉及的每个组件的 Try 接口的响应状态以及这笔事务的持续时长
  • 倘若事务应该被置为【失败】(存在某个 TCC Component Try 接口请求失败),但状态却并未更新,说明之前批量执行 Cancel 操作时可能发生了错误. 此时需要补偿性地批量调用事务所涉及的所有 Component 的 Cancel 操作,待所有 Cancel 操作都成功后,将事务置为【失败】状态
  • 倘若事务应该被置为【成功】(所有 TCC Component Try 接口均请求成功),但状态却并未更新,说明之前批量执行 Confirm 操作时可能发生了错误. 此时需要补偿性地批量调用事务所涉及的所有 Component 的 Confirm 操作,待所有 Confirm 操作都成功后,将事务置为【成功】状态
  • 倘若事务仍处于【进行中】状态(TCC Component Try 接口请求未出现失败,但并非所有 Component Try 接口都请求成功),则检查事务的创建时间,倘若其耗时过长,同样需要按照事务失败的方式进行处理

在这里插入图片描述
需要注意,在 TX Manager 轮询重试的流程中,针对下游 TCC Component 的 Confirm 和 Cancel 请求只能保证 at least once 的语义,换句话说,这部分请求是可能出现重复的.

因此,在下游 TCC Component 中,需要在接收到 Confirm/Cancel 请求时,执行幂等去重操作. 幂等去重操作需要有一个唯一键作为去重的标识,这个标识键就是 TX Manager 在开启事务时为其分配的全局唯一的 Transaction ID,它既要作为这项事务在事务日志表中的唯一键,同时在 TX Manager 每次向 TCC Component 发起请求时,都要携带上这笔 Transaction ID.

TX Manager 职责

首先针对于事务协调器 TX Manager,其核心要点包括:

  • 注册TCC Component接口:该接口用于向分布式事务框架注册TCC组件(Try、Confirm、Cancel组件),以便管理和执行事务。不同的应用程序可能有不同的TCC组件需求,因此需要提供接口来注册和管理这些组件。

  • 启动分布式事务接口:作为与应用程序交互的唯一入口,该接口用于启动分布式事务。应用程序通过调用此接口来触发分布式事务的执行,并根据事务的执行结果进行反馈。

  • 全局唯一的Transaction ID:每个分布式事务需要一个全局唯一的事务标识,通常称为Transaction ID,用于跟踪和管理事务。同时,需要一个事务日志表来记录每项分布式事务的执行进展明细,以便进行事务状态的管理和恢复。

  • 串联Try-Confirm/Cancel两阶段流程:TCC模式的核心是将事务分为Try、Confirm和Cancel三个阶段,需要实现这三个阶段的串联执行流程。根据Try的结果,推进执行Confirm或Cancel流程,以确保事务的一致性。

  • 持续运行轮询检查任务:为了确保分布式事务的最终一致性,需要持续运行轮询检查任务,监测每个处于中间态的分布式事务,并将其推进到终态(已确认或已取消)。这有助于处理各种异常情况和故障恢复。

在这里插入图片描述

TCC Component 职责

在这里插入图片描述

对于 TCC Component 而言,其需要关心和处理的工作包括:

  • 暴露出 Try、Confirm、Cancel 三个入口,对应于 TCC 的语义
  • 针对数据记录,新增出一个对应于 Try 操作的中间状态枚举值
  • 针对于同一笔事务的重复请求,需要执行幂等性校验
  • 需要支持空回滚操作. 即针对于一笔新的 Transaction ID,在没收到 Try 的前提下,若提前收到了 Cancel 操作,也需要将这个信息记录下来,但不需要对真实的状态数据发生变更

下面针对最后一点提到的空回滚操作,进一步加以说明:

这个空回滚机制本质上是为了解决 TCC 流程中出现的悬挂问题,下面我们举个具体例子加以说明:

具体来说,如果TX Manager在向Component A发起Try请求时发生了超时,并且TX Manager已经批量执行了Cancel操作,然后之前由于网络问题而阻塞的Try请求到达了Component A,这可能导致请求到达的次序颠倒。在这种情况下,Component A需要确保只要接收到了对应的Cancel请求,之后到来的Try请求需要被忽略,以维护事务的一致性。

支持空回滚操作是TCC组件的一项关键功能。通过空回滚操作,即使Try请求到达的次序发生颠倒,Component A仍然可以正确处理事务。当Component A收到Cancel请求时,它可以标记相应的事务为已取消状态,并且在之后收到的Try请求到达时,可以忽略它们,因为事务已经被取消。

这种机制确保了TCC事务模式的鲁棒性,即使在不稳定的网络环境下,也能够维护事务的一致性。空回滚操作是一种有效的方式来处理请求到达顺序的问题,确保分布式事务的可靠性。

在这里插入图片描述

TCC 优劣势分析

最后我们针对 TCC 分布式事务实现方案的优劣势进行分析:

优势:

  • TCC 可以称得上是真正意义上的分布式事务:任意一个 Component 的 Try 操作发生问题,都能支持事务的整体回滚操作
  • TCC 流程中,分布式事务中数据的状态一致性能够趋近于 100%,这是因为第二阶段 Confirm/Cancel 的成功率是很高的,原因在于如下三个方面:
    • TX Manager 在此前刚和 Component 经历过一轮 Try 请求的交互并获得了成功的 ACK,因此短时间内,Component 出现网络问题或者自身节点状态问题的概率是比较小的
    • TX Manager 已经通过 Try 操作,让 Component 提前锁定了对应的资源,因此确保了资源是充分的,且由于执行了状态锁定,出现并发问题的概率也会比较小
    • TX Manager 中通过轮询重试机制,保证了在 Confirm 和 Cancel 操作执行失败时,也能够通过重试机制得到补偿

劣势:

  • TCC 分布式事务中,涉及的状态数据变更只能趋近于最终一致性,无法做到即时一致性
  • 事务的原子性只能做到趋近于 100%,而无法做到真正意义上的 100%,原因就在于第二阶段的 Confirm 和 Cancel 仍然存在极小概率发生失败,即便通过重试机制也无法挽救. 这部分小概率事件,就需要通过人为介入进行兜底处理
  • TCC 架构的实现成本是很高的,需要所有子模块改造成 TCC 组件的格式,且整个事务的处理流程是相对繁重且复杂的. 因此在针对数据一致性要求不那么高的场景中,通常不会使用到这套架构.

事实上,上面提到的第二点劣势也并非是 TCC 方案的缺陷,而是所有分布式事务都存在的问题,由于网络请求以及第三方系统的不稳定性,分布式事务永远无法达到 100% 的原子性.


总结

Transaction Message:能够支持狭义的分布式事务. 基于消息队列组件中半事务消息以及轮询检查机制,保证了本地事务和消息生产两个动作的原子性,但不具备事务的逆向回滚能力
TCC Transaction:能够支持广义的分布式事务. 架构中每个模块需要改造成实现 Try/Confirm/Cancel 能力的 TCC 组件,通过事务协调器进行全局 Try——Confirm/Cancel 两阶段流程的串联,保证数据的最终一致性趋近于 100%

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1214919.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.5 Windows驱动开发:应用层与内核层内存映射

在上一篇博文《内核通过PEB得到进程参数》中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这次我们将实现一个更加有趣的功能,在某些情况下应用层与内核层需要共享一片内存…

这家提供数据闭环完整链路的企业,已拿下多家头部主机厂定点

“BEV感知数据闭环”已经成为新一代自动驾驶系统的核心架构。 进入2023年,小鹏、理想、阿维塔、智己、华为问界等汽车品牌正在全力推动从高速NOA到城区NOA的升级。在这一过程当中,如何利用高效的算力支撑、完善的算法模型、大量有效的数据形成闭环&…

【人工智能实验】A*算法求解8数码问题 golang

人工智能经典问题八数码求解 实际上是将求解转为寻找最优节点的问题,算法流程如下: 求非0元素的逆序数的和,判断是否有解将开始状态放到节点集,并设置访问标识位为true从节点集中取出h(x)g(x)最小的节点判断取出的节点的状态是不…

基于springboot实现医患档案管理系统项目【项目源码】计算机毕业设计

基于springboot实现医患档案管理系统演示 Java语言简介 Java是由SUN公司推出,该公司于2010年被oracle公司收购。Java本是印度尼西亚的一个叫做爪洼岛的英文名称,也因此得来java是一杯正冒着热气咖啡的标识。Java语言在移动互联网的大背景下具备了显著的…

03.智慧商城——封装请求模块、登录静态页面、图形验证码

01. 登录页静态布局 (1) 准备工作 新建 styles/common.less 重置默认样式 // 重置默认样式 * {margin: 0;padding: 0;box-sizing: border-box; }// 文字溢出省略号 .text-ellipsis-2 {overflow: hidden;-webkit-line-clamp: 2;text-overflow: ellipsis;display: -webkit-box…

【Spring】超详细讲解AOP(面向切面编程)

文章目录 1. 前言2. 什么是AOP3. AOP快速入门4. AOP的核心概念5. 切点表达式6. 切点函数7. 通知8. 总结 1. 前言 本文围绕AOP进行讲解,AOP可以做什么,涉及到了哪些注解,以及各个注解运行的时机,以及Around相较于其它注解有什么不同,并且如果要执行目标方法需要怎么做 2. 什么…

Java实现简单的俄罗斯方块游戏

一、创建新项目 1.首先新建一个项目,并命名为俄罗斯方块。 2.其次新建一个类,命名为Main,或其他的。 二、运行代码 代码如下: package 俄罗斯方块;import java.awt.BorderLayout; import java.awt.Color; import java.awt.Gr…

C# 字节数组按照指定大小拆分保存至TXT文件

1.按照4个字节拆分为一行显示示例代码 byte[] result new byte[] {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 };using (StreamWriter writer new StreamWriter("output.txt")){for (int i 0; i < result.Length; i 4) //按照四个字节拆分{byte[] tempArray n…

Android10 手势导航

种类 Android10 默认的系统导航有三种&#xff1a; 1.两个按钮的 2.三个按钮的 3.手势 它们分别对应三个包名 frameworks/base/packages/overlays/NavigationBarMode2ButtonOverlay frameworks/base/packages/overlays/NavigationBarMode3ButtonOverlay frameworks/base/packa…

【Python3】【力扣题】290. 单词规律

【力扣题】题目描述&#xff1a; 【Python3】代码&#xff1a; 1、解题思路&#xff1a;哈希。两个字典。分别记录字母对应的单词和单词对应的字母&#xff0c;若不是对应的&#xff0c;则返回False。 知识点&#xff1a;字符串.split()&#xff1a;将字符串按指定字符拆分成…

代码随想录算法训练营|五十三天

判断子序列 392. 判断子序列 - 力扣&#xff08;LeetCode&#xff09; 过程&#xff1a; public class Solution {public bool IsSubsequence(string s, string t) {int[,] dp new int[s.Length 1, t.Length 1];for (int i 1; i < s.Length; i) {for (int j 1; j <…

内置升压的单声道D类音频功率放大器:HT81293

HT81293是一款内置升压的单声道D类音频功率放大器&#xff0c;由锂电池供电时&#xff0c; THDN10%&#xff0c; 能连续输出18W功率&#xff08;4Ω负载&#xff09;。 HT81293A内置可动态调节的升压&#xff0c;可以提供一个适应不同输出功率的电压给D类功放&#xff0c;其可大…

1.mysql安装及基础

目录 概述安装上传jar包解压添加用户组和用户更改权限修改配置文件 my.cnf初始化登录mysql修改密码远程登录生效配置 sql语句分类数据定义语言 结束 概述 mysql安装及基础&#xff0c;后续涉及基础会继续补充。 安装 上传jar包 解压 tar -zxvf mysql-5.7.44-linux-glibc2.1…

六、Nacos快速入门

目录 一、服务注册到Nacos 二、nacos服务分级存储模型 1、作用 2、划分集群 3、根据权重负载均衡 三、环境隔离 1、在未设置namespace时&#xff0c;所有服务都默认在public 2、新建namespace 3、将order-service的namespace更改为dev 4、总结 一、服务注册到Nacos …

2019年五一杯数学建模C题科创板拟上市企业估值解题全过程文档及程序

2019年五一杯数学建模 C题 科创板拟上市企业估值 原题再现 科创板在首届中国国际进口博览会开幕式上宣布设立&#xff0c;是独立于现有主板市场的新设板块。设立科创板并试点注册制是提升服务科技创新企业能力、增强市场包容性、强化市场功能的一项资本市场重大改革举措&…

玩转样本量计算,全靠这个在线免费小工具

相信很多小伙伴都有过这样的经历&#xff1a;做科研设计、撰写论文&#xff0c;设计好主题后摆在眼前的是你最头痛的问题——样本量计算。事实上&#xff0c;样本量计算往往是临床医生做临床研究设计的一大障碍&#xff0c;是临床研究设计、临床知识经验以及统计学知识的结合。…

【机器学习】线性回归算法:原理、公式推导、损失函数、似然函数、梯度下降

1. 概念简述 线性回归是通过一个或多个自变量与因变量之间进行建模的回归分析&#xff0c;其特点为一个或多个称为回归系数的模型参数的线性组合。如下图所示&#xff0c;样本点为历史数据&#xff0c;回归曲线要能最贴切的模拟样本点的趋势&#xff0c;将误差降到最小。 2. 线…

第二证券:产业资本真金白银传递市场信心

本年以来&#xff0c;A股商场继续颤抖&#xff0c;但工业本钱纷繁行为&#xff0c;拿出大笔真金白银掀起增持回购潮。Wind数据闪现&#xff0c;到11月15日记者发稿&#xff0c;本年以来已有逾千家公司发布了股票回购预案&#xff0c;拟回购金额上限估计超1200亿元。同期&#x…

《洛谷深入浅出基础篇》P1551亲戚——集合——并查集P1551亲戚

上链接&#xff1a;P1551 亲戚 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1551 上题干&#xff1a; 题目背景 若某个家族人员过于庞大&#xff0c;要判断两个是否是亲戚&#xff0c;确实还很不容易&#xff0c;现在给出某个亲戚关系图…

LINMP搭建wordpress-数据库不分离

目录 一、nginx部署 1.安装nginx前的系统依赖环境检查 2.下载nginx源代码包 3.解压缩源码包 4.创建普通的nginx用户 5.开始编译安装nginx服务 6.创建一个软连接以供集中管理 7.配置nginx环境变量 二、mysql 1.创建普通mysql用户 2.下载mysql二进制代码包 3.创建mys…