聊聊Seata分布式解决方案AT模式的实现原理

什么是Seata分布式事务解决方案

Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。

AT模式

AT模式目前来看是Seata框架独有的一种模式,其它的分布式框架上并没有此种模式的实现。其是由二阶段提交演变来的,解决了二阶段提交的同步阻塞等问题。
演变后的两阶段提交协议:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:
    • 提交异步化,非常快速地完成。
    • 回滚通过一阶段的回滚日志进行反向补偿。

Seata框架中有三个概念要阐述下。

  • TC:事务协调器,它是独立的组件,需要独立部署运行,Seata提供了这个独立运行的程序,它负责维护全局事务的运行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信,协调各个分支事务的提交或回滚。
  • TM:事务管理器,TM需要嵌入应用程序,它负责开启一个全局事务,并定义全局事务的范围,它的目的是最终向TC发起全局提交或回滚指令。
  • RM:与TC通信,控制分支事务,负责分支注册、报告分支事务状态,并接收事务协调器TC的指令,命令分支事务完成本地事务的提交或回滚。

流程示意图如下:

image

这个图还是比较清晰地,建议是先好好的理解下全流程。

Seata AT模式的具体流程如下。
(1)订单服务收到请求,订单服务中的TM向TC申请开启一个全局事务。(2)TC收到请求,创建一个全局事务,并将全局事务ID(称为XID)返回给订单服务的TM。
(3)订单服务的RM向TC注册分支事务。
(4)订单服务执行本地分支事务的业务逻辑并提交,释放锁定的数据库资源。
(5)订单服务向TC上报本地分支事务的提交结果。
(6)订单服务调用远程的积分服务,此时将XID通过参数传给积分服务。(7)积分服务向TC注册分支事务。
(8)积分服务执行本地分支事务的业务逻辑并提交,释放锁定的数据库资源,并返回订单服务。
(9)积分服务向TC上报本地分支事务的提交结果。
(10)订单服务的TM向TC发起全局事务的提交或回滚。
(11)TC向XID管辖下的全部分支事务发出提交或回滚的指令。

实现原理

我个人觉得框架其实也是一种需求的兑现,只是不像平常开发时那样有产品经理给你输出需求文档(应该也会有,但是少),业务流程不是传统的那种XXX业务,框架的需求一般是偏向技术一些,我把它认为是技术需求;而日常我们做的开发一般是业务需求的兑现。

上面的流程可以看作是需求,那么现在需求出来了,程序猿要怎么实现?

设计思路

TM的设计

流程的开始与结束是由TM决定的,这个TM就是订单服务Controller入口进去后的某个Service方法(当然也可能不是,看你的代码架构,我这里只按照我自己的平常的开发模式来。)在这个Service方法中,处理了订单服务以及积分服务的业务。当Service方法完成后,那么就是整个业务做完了,事务即完成。因此,要我来实现,这个TM对应的Service方法,我会选择用一个注解包裹,通过动态代理的方式,在这个方法的前后分别负责全局事务的开始与结束流程。

RM的设计

RM负责执行具体的业务,将数据入库同时上报给TC。由于二阶段回滚时需要根据回滚日志replay,那么就一定需要记录业务数据执行的日志。那怎么记录?回想了下Mybatis中的数据源代理,这里也是一样的思路。必须拦截或是代理原先的数据源,解析原执行的sql,注入Seata的逻辑,增强其执行逻辑。我们看下RM要做的事情,第一阶段:

image

第二阶段提交:

image

第二阶段回滚:

image

在执行sql的过程中,各个代理对象起到的作用如下

image

所以在RM端最关键的就是数据源代理以及远程通信。这两块尤其是前者,就是AT模式的技术实现。

TC的设计

TC端需要管理Seata全局事务会话信息,一般是由全局事务、分支事务、全局锁构成,对应表globaltable、branchtable、lock_table。因此在安装部署的时候(file模式除外)都会创建这三个表。从上面来看,目前我们还没有实现隔离性,所谓的隔离性是指多个用户并发访问数据库时,数据库为每个用户开启的事务不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。这也是这里有一个全局锁表的原因。每次本地事务提交前,都会向TC端申请注册分支,同时还会申请全局锁,RM端通过拿到的全局锁保证了读写的隔离性,因此一旦当前事务持有全局锁,那么其他的事务不能提交。

写隔离

两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。

  1. tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的全局锁 ,本地提交释放本地锁。
  2. tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的全局锁,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待全局锁。
  3. tx1 二阶段全局提交,释放全局锁。tx2 拿到全局锁提交本地事务。

image

如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。

此时,如果 tx2 仍在等待该数据的全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的全局锁等锁超时,放弃全局锁并回滚本地事务释放本地锁,tx1 的分支回滚最终成功。

因为整个过程全局锁在 tx1 结束前一直是被 tx1 持有的,所以不会发生脏写的问题。

image

读隔离

image

seata at 模式默认的隔离级别为读未提交(因为已经提交的sql有可能会回滚)。如果要实现读已提交,select语句需要更改为 SELECT FOR UPDATE 语句。

SELECT FOR UPDATE 语句的执行会申请全局锁,如果全局锁被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是已提交的,才返回。

总结

Seata AT可以给你带来一种“无侵入”式的编程体验,你不需要改动任何业务代码,只需要一个注解和少量的配置信息,就可以实现分布式事务。

总结来看,AT模式主要是是基于 DataSource 代理实现的,通过代理 DataSource、Connection、PreparedStatement,拦截 SQL 执行,增强其执行逻辑,由代理侧加入额外的能力以提供分布式事务服务类似自动挡驾驶模式,分布式事务这个强大且复杂的服务能力由Seata框架托管,对业务实现无侵入式,用户仍然只专注于业务 SQL。

本文转载于网络 如有侵权请联系删除

相关文章

  • PCB封装又画错了?一张纸搞定封装检查!

    相信很多同学在画PCB时都有过封装画错的精力,不是画大了就是画小了,甚至是器件有遮挡,导致PCB制板回来后器件焊接不上,只能手动飞线,严重时导致整个板子报废,比如下面图中的U8,封装就画小了,导致芯片焊接不上去。有没有简单方法来提前检查PCB封装,避免采坑呢?当然有啦,先说结果,后说教程。结果部分我们可以把PCB先按照1:1的比例打印到纸上,然后把器件在纸上面摆一摆,这样就能大体判断封装画的对不对了,不对的画再修改,改正确后再投板。下图是打印在纸上的PCB,芯片放在上面对比是非常合适的。板子回来后尺寸也是刚好合适,成功避免采坑。教程部分我们以AltiumDesigner为例,介绍打印过程。文件-页面设置-缩放比例-1-高级-删除底层(先打印顶层,再打印底层,打印底层时要选择镜像:高级-bot要镜像)文件-页面设置-预览-打印导出为PDF就可以直接打印啦。下面是制板回来的结果,是不是非常简单呢?

  • Android物联网应用程序开发(智慧园区)—— 图片预览界面

    大家好,又见面了,我是你们的朋友全栈君。效果图:实现步骤:1、首先在build.gradle文件中引入RecycleViewimplementation'com.android.support:recyclerview-v7:28.0.0'复制添加完成后,在右上角有一个同步SyncNow的提示,点击进行同步构建,接下来修改activity_main.xml的代码2、在activity_main.xml布局文件中加入RecyclerView<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="ve

  • H5-Dooring可视化搭建文档.vuepress

    周末的两天48小时,画完了H5-Dooring文档的原型图和文档内容,另一个小伙伴完成了文档网站的开发,在设计文档的过程中刚好梳理了一下最近的迭代计划和方向,特意分享给大家,希望Dooring能面向未来.Dooring文档搭建文档系统我们采用的是vuepress,它提供了很便捷的文档编写部署方案,且支持移动端适配,我们来看看H5-Dooring的文档界面:文档组织结构:H5-Dooring架构图:移动端访问效果:文档的具体细节大家可以访问github体验,目前文档源码已提交到github.Dooring用户墙(密恐者慎?)还有很多就不一一截图了,谢谢各位这么忙,还来关注H5-Dooring,我和小伙伴们会持续迭代,与时俱进.Dooring2.0规划目前我们网站上看到的是Dooring1.0的完结版,其中还有很多需要优化和丰富的内容,其实在1.0开发之前我就已经规划到3.0版本了,由于时间和整体架构升级的原因,目前只能一步步推动.这里我们来梳理一下Dooring2.0的规划:丰富商品组件(如倒计时组件,转盘,抽奖)支持第三方oss上传上线接口文档,支持不同后端语言开发支持PSD解析添加组

  • R语言GARCH-DCC模型和DCC(MVT)建模估计

    原文链接:http://tecdat.cn/?p=7194这个简短的演示说明了使用rmgarch软件包的DCC模型及其方法的使用,尤其是在存在MVT分布形状参数的情况下进行2级DCC估计的另一种方法。第一阶段并将其传递给dccfit cl=makePSOCKcluster(10) multf=multifit(uspec,Dat,cluster=cl) 复制接下来,估计DCC模型。fit1=dccfit(spec1,data=Dat,fit.control=list(eval.se=TRUE),fit=multf,cluster=cl) 复制为了在实践中拟合DCC(MVT)模型,要么假定第一阶段的QML,要么必须在阶段中共同估算共同的形状参数。在下面的示例中,一种替代方法用于估计近似共同形状参数。似然度和形状参数变化的图表明,只需几次迭代即可收敛到稳定值。shape参数的值表示峰度为1.06。对非对称DCC(MVT)模型重复进行拟合。 xspec=ugarchspec(mean.model=list(armaOrder=c(1,1)),variance.model=list(gar

  • 增量学习不只有finetune,三星AI提增量式少样本目标检测算法 | CVPR 2020

    作者|VincentLee来源|晓飞的算法工程笔记该论文研究了非常有意义的增量式少样本目标检测场景iFSD(IncrementalFew-ShotDetection),场景设置如下:检测模型可以在包含充足样本的基础类别上进行训练训练好后,iFSD能够应用到真实世界中,任何新类别在任何时候都能通过少量标注样本进行注册对于无限的新类别的学习,在内存使用量、存储用量和计算量上都应该是可行的,理想情况下,模型可以发布在资源有限的设备上,如手机和机器人目前的常规目标检测算法大都难以适用于iFSD的场景设置,一般的做法是对新类别进行fine-tune,但这样既耗时效果又一般。为此,论文提出无限制CentreNet(OpeN-endedCentrenEt,ONCE),在原CentreNet(CenterNet?)的基础上,采用基于特征的知识迁移策略,将网络分成类可知和类不可知模块进行增量式少样本学习。首先使用基类训练一个通用的特征提取器,然后基于meta-learning学习classcode生成器,最后通过结合特征和classcode进行目标定位。论文的主要贡献如下:在增量式少样本目标检测问题上,

  • ZABBIX 监控基本报警故障

    ZABBIX监控基本报警故障浏览器右上角查看导航按钮>>>>CPU触发器:   1)Processorloadistoohighon{HOST.NAME}{HOST.NAME}上处理器负载太高     触发器表达式:{Zabbixserver:system.cpu.load[percpu,avg1].avg(5m)}>5       告警等级:警告   2)DiskI/Oisoverloadedon{HOST.NAME}磁盘I/O在{HOST.NAME}上重载     触发器表达式:{Zabbixserver:system.cpu.util[,iowait].avg(1h)}>30       告警等级:警告   3){HOST.NAME}[CPUIdle]-[<10%]CPU空闲小于百分之10     触发器表达式:{Zabbixserver:system.cpu.util[,idle].count(#5,10,"lt")}=5       告警等级:一般严重General触发器:   1)Hostnamewaschang

  • C语言实现base64编解码

    base64编解码工作中经常会用到base64编解码,有些开源库中也有实现,但是如果再去看他们的怎么用有时候也是有点费劲的,还有就是需要引用那个头文件啊,什么的,尤其是OpenSSL里边的,所以这里献上原理,及其使用. 至于用途还有详细的介绍我觉得某度某科里讲的挺好的,这里就只写上实现原理及代码了. 详细请看base64.h和base64.c,使用见main.c即可,可以使用任何编译器编译运行,下面依次是base64.h,base64.c,main.c如果需要源文件可以留言哦,对你有帮助的话赞一吧,之后也会把之前的干货,竟然分享给大家的.请多多支持. 这里我尽量使用了简单的代码结构实现的,容易理解一点,如果你理解之后可以加之优化的.大神请飘过... // //base64.h //base64 // //Createdbyguofuon2017/5/25. //Copyright©2017年guofu.Allrightsreserved. // #ifndefbase64_h #definebase64_h #include<stdio.h> #if__cpluspl

  • [Oracle]-[安装]-Cent OS安装Oracle Client

    http://www.oracle.com/technology/tech/oci/instantclient/index.html下载:oracle-instantclient-basic-10.2.0.4-1.i386.ziporacle-instantclient-sqlplus-10.2.0.4-1.i386.zip先创建客户端的安装目录,这两个目录可以自定义,但配置环境变量时,需要一致。mkdir -p /opt/oracle/libmkdir -p /opt/oracle/network/admin(有文章说还需要下载sdk,这里没下载试过也可以。)解压上面下载的文件。unzip oracle-instantclient-basic-10.2.0.4-1.i386.zipunzip oracle-instantclient-sqlplus-10.2.0.4-1.i386.zip其中,这二个文件都解压到当前目录下的同一个目录下面:instantclient_10_2cd instantclient_10_2把这个目录下的所有文件搬到/opt/oracle/lib若仅让当前用户

  • 两个imageView实现图片轮播

    前言在不少的项目中,都会用到图片轮播这个功能,现在网上关于图片轮播的轮子也层出不穷,千奇百怪,笔者根据自己的思路,用两个imageView也实现了图片轮播,这里给大家介绍笔者的主要思路以及大概步骤。轮播实现步骤层级结构最底层是一个UIView,上面有一个UIScrollView和UIPageControl,scrollView上有两个UIImageView,imageView的宽高=scrollView的宽高=view的宽高轮播原理假设轮播控件的宽为x,高为y,我们设置scrollView的contentSize的宽度为3x,并且让scrollView在x方向偏移量为x,即显示中间内容scrollView.contentSize=CGSizeMake(3x,y); scrollView.contentOffset=CGPointMake(x,0);复制接下来使用代理方法scrollViewDidScroll来监听scrollView的滚动,定义一个枚举来记录滚动的方向typedefNS_ENUM(NSInteger,Direction){ DirectionNone=1<<

  • 用栈实现队列

    描述用两个栈来实现一个队列,完成队列的Push和Pop操作。队列中的元素为int类型。思路代码实现publicclassMyQueue{ privateStack<Integer>stack1; privateStack<Integer>stack2; publicMyQueue(){ stack1=newStack<Integer>(); stack2=newStack<Integer>(); } publicvoidstack2ToStack1(){ while(!stack2.empty()){ stack1.push(stack2.pop()); } } publicvoidpush(intelement){ stack2.push(element); } publicintpop(){ if(stack1.empty()==true){ this.stack2ToStack1(); } returnstack1.pop(); } publicinttop(){ if(stack1.empty()==true){ thi

  • 创建管理通知渠道

    创建和管理通知渠道从Android8.0(API26)开始,所有的通知必须分配一个渠道。每一个渠道,你都可以设置渠道中所有通知的视觉和听觉行为。然后,用户能够随意修改这些设置来决定通知的行为。视频说明 https://youtu.be/zGIw4MIJn5o用户能够设置每个App的通知渠道行为在用户界面渠道显示为“类别”渠道创建后就不能更改通知行为了,用户能够完全控制通知的行为,但我们还是可以更改说明和渠道名称我们可以为每个不同类型的通知创建一个渠道;如果target>=26就必须使用渠道。如果target是在26以下,但是运行在了26及以上了,不要怕,还是会和往常一样,不会抛出异常。如果target>=26;在26及以上运行的时候没有指定一个渠道,通知是不会发出的,系统会记录此错误。可以在logcat看到AndroidO有一个新的开发者设置,当发送了没有指定渠道的通知时显示一个toast。Settings>DeveloperoptionsandenableShownotificationchannelwarnings创建通知渠道创建渠道有三个步骤使用一个唯一ID,一

  • 1002个GIS应用

    1002GISApplications&Users ----HowGISIsChangingtheWorld----通过这1002个地理信息系统应用程序和应用程序,增强您的数据能力。(1)为你或你的学生寻找地理信息系统项目而苦苦挣扎时?(2)当有人问起地理信息系统到底能做什么时?(3)想让你的业务和服务多样化?经过一年的发展,以下是您最喜欢的GIS应用程序:地理信息系统学生的项目想法,地理信息系统案例研究,地理信息系统项目,地理信息系统的使用-来自50多个行业,这1000个地理信息系统应用程序的拥挤指南将打开你的思维,我们的神奇的星球及其相互连接。1002GISApplications&Users1.PrecisionFarming–Harvestingmorebushelsperacrewhilespendinglessonfertilizerusingprecisionfarmingandsoftware.(HowtowinthefarmusingGIS)2.DiseaseControl–Combatingthespreadofpeststhroughbyident

  • sass中中文注释报错

    最近项目中用到了sass来编译css,但是scss代码中写了中文注释后编译报错,   经过查找文档和资料,终于找到了解决办法,即在scss文件顶部加上@charset"utf-8";这句代码就ok了。

  • 无穷的远方,无数的人们,都和我有关

    场数 T1 T2 T3 总分 Rank Day1 80 90 30 200 34 Day2(出题人锅场) 10 40 20 70 76 Day3 100 48 60 208 4 Day4 90 80 30 200 30 Day5(原题场) 60 70 50 180 38 Day6 100 100 45 245 12 Day7 100 100 0 200 9 Day8 40 30 70 140 40 Day9 ? ? ? ? ? Day1T3出了扫描线,我不会,导致我GG,区分度很小,真sb Day2出题人出锅了.sb Day3考的很舒服,区分度很大 Day4我睿智了,宕机了,T1没立刻想出来,然后做了1h.T2挂了,导致T3我没时间想+写了.(就是菜) Day5出题人有毒把,T2我写正解,不过建边用vector,然后被卡了,暴力A了???T3原题,我没写过,不会 Day6应该能AK的.不过SB了。 Day7神仙图论题我tm锤爆. Day8我

  • BizTalk连接SAP方法

    新建一个测试BizTalk项目在右键选择添加生成项   选择适配器元素   选择WCF-SAP   在Binding配置中点击   输入帐号和密码     配置主机地址       配置绑定属性,启用BizTalk兼容性 配置使用NCo作为连接方式     点击连接      

  • [BZOJ]2908: 又是nand

     题解: LCT死于卡常....迫不得已写了树剖+线段树 思路很简单  按位分解对于每一位 考虑这条路径上从右往左第一个0出现的位置  然后判断这一位经过这条路径后是0还是1 统计每一位贡献即可 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<stack> #include<queue> #include<cmath> #include<set> #include<map> #definempmake_pair #definepbpush_back #definepiipair<int,int> #definelink(x)for(edge*j=h[x];j;j=j->next) #defineinc(i,l,r)for

  • 【译文】四十二种谬误(一)

    胡乱翻译的,点击查看原文   1.人身攻击(AdHominem) 2.你也一样(AdHominemTuQuoque) 3.诉诸权威(AppealtoAuthority) 4.诉诸信念(AppealtoBelief) 5.诉诸习惯(AppealtoCommonPractice) 6.结果至上(AppealtoConsequencesofaBelief)   1.人身攻击(AdHominem) 依据与断言或论证无关的事实来否定他人。通常,这个谬误分为两步。首先,攻击断言提出者或者表述者的人格、背景和行为。然后,这些攻击被用以证明他们的观点错误。这种谬误具有以下形式: A断言X B攻击A 因此A的主张是错误的 为什么人身攻击是一种谬误,因为某人的断言正确与否与他的人格、背景或者行为无关。 例 Bill:“我认为堕胎是错的。”Dave:“你当然这么说,你是个牧师。”Bill:“那我提出的那些论证呢?”Dave:“那些不作数。就像我说的,你是个牧师,你当然说堕胎是错的。而且,你是教皇的狗腿子,所以我不会相信你说的。” 2.你也一样(AdHomi

  • Opengl es2.0 学习笔记(一)初始化

    文章目录一、vs使用opengles2.0二、初始化所用到的API三、撸代码四、创建错误 官网 http://khronos.org/bugzilla 一、vs使用opengles2.0 1.下载库区分x86x64 2.C++->常规->包含目录->include文件夹 3.链接器->附加依赖项:libEGL.lib,libGLESv2.lib 4.链接器->常规->附加库目录 5.opengles没有windows实现,angular提供了windows版windows平台调用direct3D需要copy这两个库扔到debug下d3dcompiler_43.dlld3dcompiler_46.dll 二、初始化所用到的API //创建display返回display eglGetDisplay(EGLNativeDisplayTypedisplay_id); //初始化egl eglInitialize //选择config eglChooseConfig //根据conig获取format eglGetConfigAtt

  • 小组展示

    1.项目名称:闲置物品交换系统(本项目继承自15级,但是删去了其中部分不必要的功能) 2.小组成员:刘方东,姚昕怡,梅敏敏,刘昊昕,马骁驰,李炬坪 3..闲置交换需求规格说明书:见博客文件 4.成品展示: 5.数据库设计:   6.小组分工:  

  • P1347 排序

    题目描述 一个不同的值的升序排序数列指的是一个从左到右元素依次增大的序列,例如,一个有序的数列A,B,C,D表示A<B,B<C,C<D。在这道题中,我们将给你一系列形如A<B的关系,并要求你判断是否能够根据这些关系确定这个数列的顺序。 输入输出格式 输入格式:   第一行有两个整数n,m,n表示需要排序的元素数量,2<=n<=26,第1到n个元素将用大写的A,B,C,D....表示。m表示将给出的形如A<B的关系的数量。 接下来有m行,每行有3个字符,分别为一个大写字母,一个<符号,一个大写字母,表示两个元素之间的关系。   输出格式:   若根据前x个关系即可确定这n个元素的顺序yyy..y(如ABC),输出 Sortedsequencedeterminedafterxxxrelations:yyy...y. 若根据前x个关系即发现存在矛盾(如A<B,B<C,C<A),输出 Inconsistencyfoundafter2relations. 若根据这m个关系无法确定这n个元素的顺序,输

  • 2016ACM/ICPC亚洲区沈阳站 E - Counting Cliques(爆搜+剪枝 时限1s)

    题目链接:https://nanti.jisuanke.com/t/A2062 题意:   给出n个点,m条边,求点集大小为S的完全图个数 思路:   爆搜+剪枝,hdu5952把这题时限扩大成4s了,但是这里是1s,网上很多代码都是超时的,剪枝不到位。   这里剪枝的思想是,将无向变成有向,编号小连向编号大的节点。并且记录它们的度,很显然,每搜索一个节点,它的度一定是>=s-1,这样才符合完全图的性质。    代码: #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> usingnamespacestd; typedeflonglongll; constintmaxn=110; constintmaxm=1100; structnode{ intu,v,nxt; }e[maxm]; inth[maxn],vis[maxn]; intn,m,s,t,cnt,ans,num; intout[ma

相关推荐

推荐阅读