精华内容
下载资源
问答
  • Seata框架是一个业务层的XA(两阶段提交)解决方案。在理解Seata分布式事务机制前,我们先回顾一下数据库层面的XA方案。一. MySQL XA方案MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:RM(Resource ...

        Seata框架是一个业务层的XA(两阶段提交)解决方案。在理解Seata分布式事务机制前,我们先回顾一下数据库层面的XA方案。

    一. MySQL XA方案

    MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:

    • RM(Resource Manager):用于直接执行本地事务的提交和回滚。在分布式集群中,一台MySQL服务器就是一个RM。

    • TM(Transaction Manager):TM是分布式事务的核心管理者。事务管理器与每个RM进行通信,协调并完成分布式事务的处理。发起一个分布式事务的MySQL客户端就是一个TM。

    XA的两阶段提交分为Prepare阶段和Commit阶段,过程如下:

    1. 阶段一为准备(prepare)阶段。即所有的RM锁住需要的资源,在本地执行这个事务(执行sql,写redo/undo log等),但不提交,然后向Transaction Manager报告已准备就绪。

    2. 阶段二为提交阶段(commit)。当Transaction Manager确认所有参与者都ready后,向所有参与者发送commit命令。

    如下图所示:

    06819c39e7c09adacf5184f92d057e58.png

        MySQL XA拥有严重的性能问题。一个数据库的事务和多个数据库间的XA事务性能对比可发现,性能差10倍左右。另外,XA过程中会长时间的占用资源(加锁)直到两阶段提交完成才释放资源。

    二. Seata

        Seata的分布式事务解决方案是业务层面的解决方案,只依赖于单台数据库的事务能力。Seata框架中一个分布式事务包含3中角色:

    • Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。

    • Transaction Manager (TM):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。

    • Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

        其中,TM是一个分布式事务的发起者和终结者,TC负责维护分布式事务的运行状态,而RM则负责本地事务的运行。如下图所示:

    e2f871ac26c69afab783ba2b8633e790.png

        下面是一个分布式事务在Seata中的执行流程:

    1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。

    2. XID 在微服务调用链路的上下文中传播。

    3. RM 向 TC 注册分支事务,接着执行这个分支事务并提交(重点:RM在第一阶段就已经执行了本地事务的提交/回滚),最后将执行结果汇报给TC。

    4. TM 根据 TC 中所有的分支事务的执行情况,发起全局提交或回滚决议。

    5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

        (一)为什么Seata在第一阶段就直接提交了分支事务?

        Seata能够在第一阶段直接提交事务,是因为Seata框架为每一个RM维护了一张UNDO_LOG表(这张表需要客户端自行创建),其中保存了每一次本地事务的回滚数据。因此,二阶段的回滚并不依赖于本地数据库事务的回滚,而是RM直接读取这张UNDO_LOG表,并将数据库中的数据更新为UNDO_LOG中存储的历史数据。

        如果第二阶段是提交命令,那么RM事实上并不会对数据进行提交(因为一阶段已经提交了),而实发起一个异步请求删除UNDO_LOG中关于本事务的记录。

    由于Seata一阶段直接提交了本地事务,因此会造成隔离性问题,因此Seata的默认隔离级别为Read Uncommitted。然而Seata也支持Read Committed的隔离级别,我们会在下文中介绍如何实现。

    (二)Seata执行流程

    下面是一个Seata中一个分布式事务执行的详细过程:

    1. 首先TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。

    2. XID 在微服务调用链路的上下文中传播。

    3. RM 开始执行这个分支事务,RM首先解析这条SQL语句,生成对应的UNDO_LOG记录。下面是一条UNDO_LOG中的记录:

    {    "branchId": 641789253,    "undoItems": [{        "afterImage": {            "rows": [{                "fields": [{                    "name": "id",                    "type": 4,                    "value": 1                }, {                    "name": "name",                    "type": 12,                    "value": "GTS"                }, {                    "name": "since",                    "type": 12,                    "value": "2014"                }]            }],            "tableName": "product"        },        "beforeImage": {            "rows": [{                "fields": [{                    "name": "id",                    "type": 4,                    "value": 1                }, {                    "name": "name",                    "type": 12,                    "value": "TXC"                }, {                    "name": "since",                    "type": 12,                    "value": "2014"                }]            }],            "tableName": "product"        },        "sqlType": "UPDATE"    }],    "xid": "xid:xxx"}

        可以看到,UNDO_LOG表中记录了分支ID,全局事务ID,以及事务执行的redo和undo数据以供二阶段恢复。

    1. RM在同一个本地事务中执行业务SQL和UNDO_LOG数据的插入。在提交这个本地事务前,RM会向TC申请关于这条记录的全局锁。如果申请不到,则说明有其他事务也在对这条记录进行操作,因此它会在一段时间内重试,重试失败则回滚本地事务,并向TC汇报本地事务执行失败。如下图所示:

    43a5a0bfa3a41ea87f21d34fe16a34bf.png

    1. RM在事务提交前,申请到了相关记录的全局锁,因此直接提交本地事务,并向TC汇报本地事务执行成功。此时全局锁并没有释放,全局锁的释放取决于二阶段是提交命令还是回滚命令。

    2. TC根据所有的分支事务执行结果,向RM下发提交或回滚命令。

    3. RM如果收到TC的提交命令,首先立即释放相关记录的全局锁,然后把提交请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。异步队列中的提交请求真正执行时,只是删除相应 UNDO LOG 记录而已。

    6d0c65113fcc49557c9752d3ea40dda4.png

    1. RM如果收到TC的回滚命令,则会开启一个本地事务,通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。将 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理。否则,根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句并执行,然后提交本地事务达到回滚的目的,最后释放相关记录的全局锁。

    77bd4ede4463e96edd3856228017be13.png

    (三)Seata隔离级别

    Seata由于一阶段RM自动提交本地事务的原因,默认隔离级别为Read Uncommitted。如果希望隔离级别为Read Committed,那么可以使用SELECT...FOR UPDATE语句。Seata引擎重写了SELECT...FOR UPDATE语句执行逻辑,SELECT...FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT...FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是已提交的才返回。

    20eed2dd62c12c10dc0414031adaa286.png

    SELECT FOR UPDATE.PNG

    出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

    三. Seata支持的模式

    上文中我们提到的Seata流程只是Seata支持的一种分布式事务模式,称为AT模式。它依赖于RM拥有本地数据库事务的能力,对于客户业务无侵入性。如图所示:

    f30ff5275077ef0842525a2107dd36a5.png

    AT模式中业务逻辑不需要关注事务机制,分支与全局事务的交互过程自动进行。

    另外,Seata还支持MT模式。MT模式本质上是一种TCC方案,业务逻辑需要被拆分为 Prepare/Commit/Rollback 3 部分,形成一个 MT 分支,加入全局事务。如图所示:

    57695d19f20fdc6f4cb33dd9ddf1aeb2.png

    MT 模式一方面是 AT 模式的补充。另外,更重要的价值在于,通过 MT 模式可以把众多非事务性资源纳入全局事务的管理中。

    四. XA和Seata AT的对比

    7f933dba1503142add6e0eb868da2727.png

    注:Seata的曾用名为FESCAR。

    如图所示,XA 方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身(通过提供支持 XA 的驱动程序来供应用使用)。而 Seata 的 RM 是以二方包的形式作为中间件层部署在应用程序这一侧的,不依赖与数据库本身对协议的支持,当然也不需要数据库支持 XA 协议。这点对于微服务化的架构来说是非常重要的:应用层不需要为本地事务和分布式事务两类不同场景来适配两套不同的数据库驱动。

    另外,XA方案无论 Phase2 的决议是 commit 还是 rollback,事务性资源的锁都要保持到 Phase2 完成才释放。而对于Seata,将锁分为了本地锁和全局锁,本地锁由本地事务管理,在分支事务Phase1结束时就直接释放。而全局锁由TC管理,在决议 Phase2 全局提交时,全局锁马上可以释放。只有在决议全局回滚的情况下,全局锁 才被持有至分支的 Phase2 结束。因此,Seata对于资源的占用时间要少的多。对比如下图所示:

    12c6ebc5bf9a2e3a480b56003d58d809.png

    088f3c8340106a7bd88d08f98bd230b3.png

    展开全文
  • Seata框架是一个业务层的XA(两阶段提交)解决方案。在理解Seata分布式事务机制前,我们先回顾一下数据库层面的XA方案。一. MySQL XA方案MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:RM(Resource ...

    Seata框架是一个业务层的XA(两阶段提交)解决方案。在理解Seata分布式事务机制前,我们先回顾一下数据库层面的XA方案。

    一. MySQL XA方案

    MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:

    RM(Resource Manager):用于直接执行本地事务的提交和回滚。在分布式集群中,一台MySQL服务器就是一个RM。

    TM(Transaction Manager):TM是分布式事务的核心管理者。事务管理器与每个RM进行通信,协调并完成分布式事务的处理。发起一个分布式事务的MySQL客户端就是一个TM。

    XA的两阶段提交分为Prepare阶段和Commit阶段,过程如下:

    阶段一为准备(prepare)阶段。即所有的RM锁住需要的资源,在本地执行这个事务(执行sql,写redo/undo log等),但不提交,然后向Transaction Manager报告已准备就绪。

    阶段二为提交阶段(commit)。当Transaction Manager确认所有参与者都ready后,向所有参与者发送commit命令。

    如下图所示:

    MySQL XA拥有严重的性能问题。一个数据库的事务和多个数据库间的XA事务性能对比可发现,性能差10倍左右。另外,XA过程中会长时间的占用资源(加锁)直到两阶段提交完成才释放资源。

    二. Seata

    Seata的分布式事务解决方案是业务层面的解决方案,只依赖于单台数据库的事务能力。Seata框架中一个分布式事务包含3中角色:

    Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。

    Transaction Manager (TM):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。

    Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

    其中,TM是一个分布式事务的发起者和终结者,TC负责维护分布式事务的运行状态,而RM则负责本地事务的运行。如下图所示:

    下面是一个分布式事务在Seata中的执行流程:

    TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。

    XID 在微服务调用链路的上下文中传播。

    RM 向 TC 注册分支事务,接着执行这个分支事务并提交(重点:RM在第一阶段就已经执行了本地事务的提交/回滚),最后将执行结果汇报给TC。

    TM 根据 TC 中所有的分支事务的执行情况,发起全局提交或回滚决议。

    TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

    (一)为什么Seata在第一阶段就直接提交了分支事务?

    Seata能够在第一阶段直接提交事务,是因为Seata框架为每一个RM维护了一张UNDO_LOG表(这张表需要客户端自行创建),其中保存了每一次本地事务的回滚数据。因此,二阶段的回滚并不依赖于本地数据库事务的回滚,而是RM直接读取这张UNDO_LOG表,并将数据库中的数据更新为UNDO_LOG中存储的历史数据。

    如果第二阶段是提交命令,那么RM事实上并不会对数据进行提交(因为一阶段已经提交了),而实发起一个异步请求删除UNDO_LOG中关于本事务的记录。

    由于Seata一阶段直接提交了本地事务,因此会造成隔离性问题,因此Seata的默认隔离级别为Read Uncommitted。然而Seata也支持Read Committed的隔离级别,我们会在下文中介绍如何实现。

    (二)Seata执行流程

    下面是一个Seata中一个分布式事务执行的详细过程:

    首先TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。

    XID 在微服务调用链路的上下文中传播。

    RM 开始执行这个分支事务,RM首先解析这条SQL语句,生成对应的UNDO_LOG记录。下面是一条UNDO_LOG中的记录:

    {"branchId": 641789253,"undoItems": [{"afterImage": {"rows": [{"fields": [{"name": "id","type": 4,"value": 1}, {"name": "name","type": 12,"value": "GTS"}, {"name": "since","type": 12,"value": "2014"}]}],"tableName": "product"},"beforeImage": {"rows": [{"fields": [{"name": "id","type": 4,"value": 1}, {"name": "name","type": 12,"value": "TXC"}, {"name": "since","type": 12,"value": "2014"}]}],"tableName": "product"},"sqlType": "UPDATE"}],"xid": "xid:xxx"}

    可以看到,UNDO_LOG表中记录了分支ID,全局事务ID,以及事务执行的redo和undo数据以供二阶段恢复。

    RM在同一个本地事务中执行业务SQL和UNDO_LOG数据的插入。在提交这个本地事务前,RM会向TC申请关于这条记录的全局锁。如果申请不到,则说明有其他事务也在对这条记录进行操作,因此它会在一段时间内重试,重试失败则回滚本地事务,并向TC汇报本地事务执行失败。如下图所示:

    RM在事务提交前,申请到了相关记录的全局锁,因此直接提交本地事务,并向TC汇报本地事务执行成功。此时全局锁并没有释放,全局锁的释放取决于二阶段是提交命令还是回滚命令。

    TC根据所有的分支事务执行结果,向RM下发提交或回滚命令。

    RM如果收到TC的提交命令,首先立即释放相关记录的全局锁,然后把提交请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。异步队列中的提交请求真正执行时,只是删除相应 UNDO LOG 记录而已。

    RM如果收到TC的回滚命令,则会开启一个本地事务,通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。将 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理。否则,根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句并执行,然后提交本地事务达到回滚的目的,最后释放相关记录的全局锁。

    (三)Seata隔离级别

    Seata由于一阶段RM自动提交本地事务的原因,默认隔离级别为Read Uncommitted。如果希望隔离级别为Read Committed,那么可以使用SELECT...FOR UPDATE语句。Seata引擎重写了SELECT...FOR UPDATE语句执行逻辑,SELECT...FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT...FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是已提交的才返回。

    SELECT FOR UPDATE.PNG

    出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

    三. Seata支持的模式

    上文中我们提到的Seata流程只是Seata支持的一种分布式事务模式,称为AT模式。它依赖于RM拥有本地数据库事务的能力,对于客户业务无侵入性。如图所示:

    AT模式中业务逻辑不需要关注事务机制,分支与全局事务的交互过程自动进行。

    另外,Seata还支持MT模式。MT模式本质上是一种TCC方案,业务逻辑需要被拆分为 Prepare/Commit/Rollback 3 部分,形成一个 MT 分支,加入全局事务。如图所示:

    MT 模式一方面是 AT 模式的补充。另外,更重要的价值在于,通过 MT 模式可以把众多非事务性资源纳入全局事务的管理中。

    四. XA和Seata AT的对比

    注:Seata的曾用名为FESCAR。

    如图所示,XA 方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身(通过提供支持 XA 的驱动程序来供应用使用)。而 Seata 的 RM 是以二方包的形式作为中间件层部署在应用程序这一侧的,不依赖与数据库本身对协议的支持,当然也不需要数据库支持 XA 协议。这点对于微服务化的架构来说是非常重要的:应用层不需要为本地事务和分布式事务两类不同场景来适配两套不同的数据库驱动。

    另外,XA方案无论 Phase2 的决议是 commit 还是 rollback,事务性资源的锁都要保持到 Phase2 完成才释放。而对于Seata,将锁分为了本地锁和全局锁,本地锁由本地事务管理,在分支事务Phase1结束时就直接释放。而全局锁由TC管理,在决议 Phase2 全局提交时,全局锁马上可以释放。只有在决议全局回滚的情况下,全局锁 才被持有至分支的 Phase2 结束。因此,Seata对于资源的占用时间要少的多。对比如下图所示:

    展开全文
  • 现在用的比较多的分布式事务方案主要有TCC和可靠消息。TCC需要对业务进行改造,分别实现try/confirm/cancel方法,侵入性太强,工作量大。而可靠消息主要适合可以异步调用的业务,对于需要跨服务同步调用的业务,实现...

    缘起

    现在用的比较多的分布式事务方案主要有TCC和可靠消息。TCC需要对业务进行改造,分别实现try/confirm/cancel方法,侵入性太强,工作量大。而可靠消息主要适合可以异步调用的业务,对于需要跨服务同步调用的业务,实现困难。以订机票为例,从深圳->新疆乌鲁木齐,假设没有直达,第一步先要确定中转地,选择一家航空公司订票,假设这步的订票结果是成功调用南航的服务订到了深圳->西安,那第二步调用就要根据第一步的结果订第二张票,比如优先选择同一家航空公司,间隔时间要大于两小时,出发地要限制在西安。如果第二步订票失败,那么第一步订的票也需要回滚。像这种存在上下文逻辑关系的跨服务调用,用消息同步方案很难处理。

    所以需要一个好的支持跨服务调用的分布式方案。

    理想实现

    1) 简洁明了,最好和普通的无事务或一阶段事务的远程调用相接近

    2) 侵入性小,尽量不要因为引进分布式事务引起业务代码结构的明显变化

    3) 危险期短

    4) 性能损失少

    现实情况

    TCC方案满足条件3,4,条件1勉强,不满足条件2。

    Atomikos方案貌似也比较流行,但有很多限制。比较奇怪的是网上的例子基本上是用Atomikos实现基于XA的单例多数据源的事务控制,看不到跨服务调用的例子。官方倒是提供了各种场景的使用例子,看了后,我猜到了原因:官方的例子很不清爽,而且高性能方案要收费。我在测试的时候发现免费版限制了50个事务/每秒,就直接放弃了对它的进一步研究。

    我最满意的方案是阿里的GTS,它是基于XA的两阶段提交,使用时只要一个注解和极少量的代码侵入,不破坏原有代码结构,而且性能损失只有10%左右。看它的文档,我估计它是直接本地一阶段提交,同时记录操作前像,后像undo日志,当需要回滚时,使用undo日志定位记录,对比当前数据和后像,如果一致,直接使用前像覆盖回滚,如果不一致,则报警让人工介入。这个方案很对我胃口,然而不是免费开源,且要在云端使用。

    自己造轮子

    TPCTransaction: https://github.com/johnhuang-cn/TPCTransaction

    TPC == Two Phase Commit

    先看成品效果(基于Spring Cloud)

    以经典的银行转帐为例,从alpha银行帐户转出到bravo银行同名帐户。

    调用方代码

    这里写图片描述

    服务端Service实现

    使用Mybatis mapper
    这里写图片描述
    调用方是不是和Spring的手工事务控制代码差不多?服务端只是将Spring的@Transactional换成了@TPCTransactional注解。

    再看看性能

    为了对比,同时做了一个一阶段提交的实现,也就是说调用即提交,没有后期同步commit/rollback的功能。如果出现转出成功,转入失败,那就会出现数据不一致。测试进行了1000次的转帐业务,数据库两个银行各有50个帐户,两边各账户初始存款100万,为了测试事务的有效性,引入3%的随机失败。

    测试结果(Transaction/Second):

    Non transaction serial TPS: 35.57 // 单线程

    Non transaction paralle TPS: 337.60 // 并发

    TPC transaction serial TPS: 42.34 // 单线程

    TPC transaction paralle TPS: 282.16 // 并发

    基于TPC的转帐,经过1000次转帐并等待10秒后(需待事务完成或超时回滚),两边帐户数据一致(加起来200万),证明事务控制是有效的。而一阶段直接提交在有随机异常的情况下,毫无疑问肯定是不一致了。

    TPC事务在并发情况下,大约损失15%,这个是可以接受的。线程情况下TPC事务还更快一点。这是因为单线程下两个方案的锁的粒度是一样的,而@TPCTransactional目前实现还比较简单,@TPCTransactional的代码比@Transactional少很多很多,所以性能还快一点。但在并发下,锁的粒度成了影响性能的关键,这时两阶段方案就相对慢了。

    以上测试,所有的实例和DB都在一台机子上,Mysql运行在虚拟机的Docker里,所以绝对性能不高,主要看相对性能。

    实现原理

    先上原理图

    这里写图片描述

    发起方的实现

    事务的开始

    TPCTransactionManager.begin(),创建了全局事务UID,并保存ThreadLocal里

    这里写图片描述

    远程调用的背后

    当通过feign client调用远程服务时,如果当前线程存在未完成的TPC事务,则将事务ID和剩余timeout通过头部传给服务方。通过Header传送,避免了修改调用参数,减少对代码的侵入。
    这里写图片描述

    服务端@TPCTransactional的实现

    @TPCTransactional实现了around注入,将被注解的对象方法转换成异步执行。主线程启动Executor后,就把自己hold住了。
    这里写图片描述
    而executor执行完目标方法后,事务并不提交,而只是将结果返回给主线程,并把主线程唤醒,接着把自己hold住。主线程拿到结果后,直接将结果返回给调用方交差,完成了使命。executor还需要继续等待调用方最终的commit/rollback指令。

    发起方的commit/rollback

    调用方(也是事务发起方)继续其它业务逻辑和远程调用,各参与的远程服务也一个个将结果返回后将事务异步挂起。最后一切顺利的话,发起方调用TPCTransactionManager.commit()/rollback()。该方法将commit/rollback指令同时发向各参与方。这里使用了并发发送,以减少危险期。
    这里写图片描述

    服务方的TrasactionController

    TPCTransaction的服务端安排了TransactionController来监听commit/rollback指令,根据全局transaction id检查本地是否存在隶属于该ID的executor,若有将该executor唤醒,executor解除锁定后完成最后的commit/rollback操作。
    这里写图片描述

    其它细节

    因为发起方在commit/rollback前有可能宕机,那么服务端的executor会一直锁定资源,所以必须设置timeout。超时后,服务端的ExecutorManager自行唤醒executor完成回滚以解除记录锁定。

    在Spring Cloud环境下,每个服务端有可能存在多个实例,在Loadbalance作用下,调用和发送commit/rollback可能会被负载到不同的实例上。所以TPCTransaction框架对默认的RibbonRule进行了wrapper,对同一线程的调用始终返回同一个实例。和普通的本地事务一样,TPC发起端的事务也必须在同一个线程完成。

    其它细节请移步参观Github上的项目源码。

    TODO

    不论TCC还是其它两阶段方案,都存在危险期,最后提交的时候,若某一服务端宕机或网络故障,会存在部分提交的问题,这种情况需要记录日志,并报警待人工介入。TPCTransaction框架还未实现事务日志,有待继续完善。

    展开全文
  • TCC和两阶段分布式事务处理的区别

    万次阅读 2018-03-08 11:25:12
    TCC事务模型虽然说起来简单,然而要基于TCC实现一个通用的分布式事务框架,却比它看上去要复杂的多,不只是简单的调用一下Confirm/Cancel业务就可以了的。本文将以Spring容器为例,试图分析一下,实现一个通用的TCC...

    一个TCC事务框架需要解决的当然是分布式事务的管理。关于TCC事务机制的介绍,可以参考TCC事务机制简介
    TCC事务模型虽然说起来简单,然而要基于TCC实现一个通用的分布式事务框架,却比它看上去要复杂的多,不只是简单的调用一下Confirm/Cancel业务就可以了的。

    本文将以Spring容器为例,试图分析一下,实现一个通用的TCC分布式事务框架需要注意的一些问题。

    一、TCC全局事务必须基于RM本地事务来实现全局事务

    TCC服务是由Try/Confirm/Cancel业务构成的,
    其Try/Confirm/Cancel业务在执行时,会访问资源管理器(Resource Manager,下文简称RM)来存取数据。这些存取操作,必须要参与RM本地事务,以使其更改的数据要么都commit,要么都rollback。

    这一点不难理解,考虑一下如下场景:

    image

    假设图中的服务B没有基于RM本地事务(以RDBS为例,可通过设置auto-commit为true来模拟),那么一旦[B:Try]操作中途执行失败,TCC事务框架后续决定回滚全局事务时,该[B:Cancel]则需要判断[B:Try]中哪些操作已经写到DB、哪些操作还没有写到DB:假设[B:Try]业务有5个写库操作,[B:Cancel]业务则需要逐个判断这5个操作是否生效,并将生效的操作执行反向操作。
    不幸的是,由于[B:Cancel]业务也有n(0<=n<=5)个反向的写库操作,此时一旦[B:Cancel]也中途出错,则后续的[B:Cancel]执行任务更加繁重。因为,相比第一次[B:Cancel]操作,后续的[B:Cancel]操作还需要判断先前的[B:Cancel]操作的n(0<=n<=5)个写库中哪几个已经执行、哪几个还没有执行,这就涉及到了幂等性问题。而对幂等性的保障,又很可能还需要涉及额外的写库操作,该写库操作又会因为没有RM本地事务的支持而存在类似问题。。。可想而知,如果不基于RM本地事务,TCC事务框架是无法有效的管理TCC全局事务的。

    反之,基于RM本地事务的TCC事务,这种情况则会很容易处理:[B:Try]操作中途执行失败,TCC事务框架将其参与RM本地事务直接rollback即可。后续TCC事务框架决定回滚全局事务时,在知道“[B:Try]操作涉及的RM本地事务已经rollback”的情况下,根本无需执行[B:Cancel]操作。

    换句话说,基于RM本地事务实现TCC事务框架时,一个TCC型服务的cancel业务要么执行,要么不执行,不需要考虑部分执行的情况。

    二、TCC事务框架应该接管Spring容器的TransactionManager

    基于RM本地事务的TCC事务框架,可以将各Try/Confirm/Cancel业务看着一个原子服务:一个RM本地事务提交,参与该RM本地事务的所有Try/Confirm/Cancel业务操作都生效;反之,则都不生效。掌握每个RM本地事务的状态以及它们与Try/Confirm/Cancel业务方法之间的对应关系,以此为基础,TCC事务框架才能有效的构建TCC全局事务。

    TCC服务的Try/Confirm/Cancel业务方法在RM上的数据存取操作,其RM本地事务是由Spring容器的PlatformTransactionManager来commit/rollback的,TCC事务框架想要了解RM本地事务的状态,只能通过接管Spring的事务管理器功能。

    2.1. 为什么TCC事务框架需要掌握RM本地事务的状态?
    首先,根据TCC机制的定义,TCC事务是通过执行Cancel业务来达到回滚效果的。仔细分析一下,这里暗含一个事实:
    只有生效的Try业务操作才需要执行对应的Cancel业务操作。换句话说,只有Try业务操作所参与的RM本地事务被commit了,后续TCC全局事务回滚时才需要执行其对应的Cancel业务操作;否则,如果Try业务操作所参与的RM本地事务被rollback了,后续TCC全局事务回滚时就不能执行其Cancel业务,此时若盲目执行Cancel业务反而会导致数据不一致。

    其次,Confirm/Cancel业务操作必须保证生效。Confirm/Cancel业务操作也会涉及RM数据存取操作,其参与的RM本地事务也必须被commit。TCC事务框架需要在确切的知道所有Confirm/Cancel业务操作参与的RM本地事务都被成功commit后,才能将标记该TCC全局事务为完成。如果TCC事务框架误判了Confirm/Cancel业务参与RM本地事务的状态,就会造成全局事务不一致。

    最后,未完成的TCC全局,TCC事务框架必须重新尝试提交/回滚操作。重试时会再次调用各TCC服务的Confirm/Cancel业务操作。如果某个服务的Confirm/Cancel业务之前已经生效(其参与的RM本地事务已经提交),重试时就不应该再次被调用。否则,其Confirm/Cancel业务被多次调用,就会有“服务幂等性”的问题。

    2.2. 拦截TCC服务的Try/Confirm/Cancel业务方法的执行,根据其异常信息可否知道其RM本地事务是否commit/rollback了呢?
    基本上很难做到。为什么这么说呢?
    第一,事务是可以在多个(本地/远程)服务之间互相传播其事务上下文的,一个业务方法(Try/Confirm/Cancel)执行完毕并不一定会触发当前事务的commit/rollback操作。比如,被传播事务上下文的业务方法,在它开始执行时,容器并不会为其创建新的事务,而是它的调用方参与的事务,使得二者操作在同一个事务中;同样,在它执行完毕时,容器也不会提交/回滚它参与的事务的。因此,这类业务方法上的异常情况并不能反映他们是否生效。不接管Spring的TransactionManager,就无法了解事务于何时被创建,也无法了解它于何时被提交/回滚。
    第二、一个业务方法可能会包含多个RM本地事务的情况。比如: A(REQUIRED)->B(REQUIRES_NEW)->C(REQUIRED),这种情况下,A服务所参与的RM本地事务被提交时,B服务和C服务参与的RM本地事务则可能会被回滚。
    第三、并不是抛出了异常的业务方法,其参与的事务就回滚了。Spring容器的声明式事务定义了两类异常,其事务完成方向都不一样:系统异常(一般为Unchecked异常,默认事务完成方向是rollback)、应用异常(一般为Checked异常,默认事务完成方向是commit)。二者的事务完成方向又可以通过@Transactional配置显式的指定,如rollbackFor/noRollbackFor等。
    第四、Spring容器还支持使用setRollbackOnly的方式显式的控制事务完成方向;
    最后、自行拦截业务方法的拦截器和Spring的事务处理的拦截器还会存在执行先后、拦截范围不同等问题。例如,如果自行拦截器执行在前,就会出现业务方法虽然已经执行完毕但此时其参与的RM本地事务还没有commit/rollback。

    TCC事务框架的定位应该是一个TransactionManager,其职责是负责commit/rollback事务。而一个事务应该commit、还是rollback,则应该是由Spring容器来决定的:Spring决定提交事务时,会调用TransactionManager来完成commit操作;Spring决定回滚事务时,会调用TransactionManager来完成rollback操作。

    接管Spring容器的TransactionManager,TCC事务框架可以明确的得到Spring的事务性指令,并管理Spring容器中各服务的RM本地事务。否则,如果通过自行拦截的机制,则使得业务系统存在TCC事务处理、RM本地事务处理两套事务处理逻辑,二者互不通信,各行其是。这种情况下要协调TCC全局事务,基本上可以说是缘木求鱼,本地事务尚且无法管理,更何谈管理分布式事务?

    三、TCC事务框架应该具备故障恢复机制

    一个TCC事务框架,若是没有故障恢复的保障,是不成其为分布式事务框架的。

    分布式事务管理框架的职责,不是做出全局事务提交/回滚的指令,而是管理全局事务提交/回滚的过程。它需要能够协调多个RM资源、多个节点的分支事务,保证它们按全局事务的完成方向各自完成自己的分支事务。这一点,是不容易做到的。因为,实际应用中,会有各种故障出现,很多都会造成事务的中断,从而使得统一提交/回滚全局事务的目标不能达到,甚至出现”一部分分支事务已经提交,而另一部分分支事务则已回滚”的情况。比较常见的故障,比如:业务系统服务器宕机、重启;数据库服务器宕机、重启;网络故障;断电等。这些故障可能单独发生,也可能会同时发生。作为分布式事务框架,应该具备相应的故障恢复机制,无视这些故障的影响是不负责任的做法。

    一个完整的分布式事务框架,应该保障即使在最严苛的条件下也能保证全局事务的一致性,而不是只能在最理想的环境下才能提供这种保障。退一步说,如果能有所谓“理想的环境”,那也无需使用分布式事务了。

    TCC事务框架要支持故障恢复,就必须记录相应的事务日志。事务日志是故障恢复的基础和前提,它记录了事务的各项数据。TCC事务框架做故障恢复时,可以根据事务日志的数据将中断的事务恢复至正确的状态,并在此基础上继续执行先前未完成的提交/回滚操作。

    四、TCC事务框架应该提供Confirm/Cancel服务的幂等性保障

    一般认为,服务的幂等性,是指针对同一个服务的多次(n>1)请求和对它的单次(n=1)请求,二者具有相同的副作用。

    在TCC事务模型中,Confirm/Cancel业务可能会被重复调用,其原因很多。比如,全局事务在提交/回滚时会调用各TCC服务的Confirm/Cancel业务逻辑。执行这些Confirm/Cancel业务时,可能会出现如网络中断的故障而使得全局事务不能完成。因此,故障恢复机制后续仍然会重新提交/回滚这些未完成的全局事务,这样就会再次调用参与该全局事务的各TCC服务的Confirm/Cancel业务逻辑。

    既然Confirm/Cancel业务可能会被多次调用,就需要保障其幂等性。
    那么,应该由TCC事务框架来提供幂等性保障?还是应该由业务系统自行来保障幂等性呢?
    个人认为,应该是由TCC事务框架来提供幂等性保障。如果仅仅只是极个别服务存在这个问题的话,那么由业务系统来负责也是可以的;然而,这是一类公共问题,毫无疑问,所有TCC服务的Confirm/Cancel业务存在幂等性问题。TCC服务的公共问题应该由TCC事务框架来解决;而且,考虑一下由业务系统来负责幂等性需要考虑的问题,就会发现,这无疑增大了业务系统的复杂度。

    五、TCC事务框架不能盲目的依赖Cancel业务来回滚事务

    前文以及提到过,TCC事务通过Cancel业务来对Try业务进行回撤的机制暗含了一个事实:Try操作已经生效。也就是说,只有Try操作所参与的RM本地事务已经提交的情况下,才需要执行其Cancel操作进行回撤。没有执行、或者执行了但是其RM本地事务被rollback的Try业务,是一定不能执行其Cancel业务进行回撤的。因此,TCC事务框架在全局事务回滚时,应该根据TCC服务的Try业务的执行情况选择合适的处理机制。而不能盲目的执行Cancel业务,否则就会导致数据不一致。

    一个TCC服务的Try操作是否生效,这是TCC事务框架应该知道的,因为其Try业务所参与的RM事务也是由TCC事务框架所commit/rollbac的(前提是TCC事务框架接管了Spring的事务管理器)。所以,TCC事务回滚时,TCC事务框架可考虑如下处理策略:
    1)如果TCC事务框架发现某个服务的Try操作的本地事务尚未提交,应该直接将其回滚,而后就不必再执行该服务的cancel业务;
    2)如果TCC事务框架发现某个服务的Try操作的本地事务已经回滚,则不必再执行该服务的cancel业务;
    3)如果TCC事务框架发现某个服务的Try操作尚未被执行过,那么,也不必再执行该服务的cancel业务。

    总之,TCC事务框架应该保障:
    1)已生效的Try操作应该被其Cancel操作所回撤;
    2)尚未生效的Try操作,则不应该执行其Cancel操作。这一点,不是幂等性所能解决的问题。如上文所述,幂等性是指服务被执行一次和被执行n(n>0)次所产生的影响相同。但是,未被执行和被执行过,二者效果肯定是不一样的,这不属于幂等性的范畴。

    六、Cancel业务与Try业务并行,甚至先于Try操作完成

    这应该算TCC事务机制特有的一个不可思议的陷阱。一般来说,一个特定的TCC服务,其Try操作的执行,是应该在其Confirm/Cancel操作之前的。Try操作执行完毕之后,Spring容器再根据Try操作的执行情况,指示TCC事务框架提交/回滚全局事务。然后,TCC事务框架再去逐个调用各TCC服务的Confirm/Cancel操作。

    然而,超时、网络故障、服务器的重启等故障的存在,使得这个顺序会被打乱。比如:

    image

    上图中,假设[B:Try]操作执行过程中,网络闪断,[A:Try]会收到一个RPC远程调用异常。A不处理该异常,导致全局事务决定回滚,TCC事务框架就会去调用[B:Cancel],而此刻A、B之间网络刚好已经恢复。如果[B:Try]操作耗时较长(网络阻塞/数据库操作阻塞),就会出现[B:Try]和[B:Cancel]二者并行处理的现象,甚至[B:Cancel]先完成的现象。

    这种情况下,由于[B:Cancel]执行时,[B:Try]尚未生效(其RM本地事务尚未提交),因此,[B:Cancel]是不能执行的,至少是不能生效(执行了其RM本地事务也要rollback)的。然而,当
    [B:Cancel]处理完毕(跳过执行、或者执行后rollback其RM本地事务)后,[B:Try]操作完成又生效了(其RM本地事务成功提交),这就会使得[B:Cancel]虽然提供了,但却没有起到回撤[B:Try]的作用,导致数据的不一致。

    所以,TCC框架在这种情况下,需要:
    1)将[B:Try]的本地事务标注为rollbackOnly,阻止其后续生效;
    2)禁止其再次将事务上下文传递给其他远程分支,否则该问题将在其他分支上出现;
    3)相应地,[B:Cancel]也不必执行,至少不能生效。

    当然,TCC事务框架也可以简单的选择阻塞[B:Cancel]的处理,待[B:Try]执行完毕后,再根据它的执行情况判断是否需要执行[B:Cancel]。不过,这种处理方式因为需要等待,所以,处理效率上会有所不及。

    同样的情况也会出现在confirm业务上,只不过,发生在Confirm业务上的处理逻辑与发生在Cancel业务上的处理逻辑会不一样,TCC框架必须保证:
    1)Confirm业务在Try业务之后执行,若发现并行,则只能阻塞相应的Confirm业务操作;
    2)在进入Confirm执行阶段之后,也不可以再提交同一全局事务内的新的Try操作的RM本地事务。

    七、TCC服务复用性是不是相对较差?

    TCC事务机制的定义,决定了一个服务需要提供三个业务实现:Try业务、Confirm业务、Cancel业务。可能会有人因此认为TCC服务的复用性较差。怎么说呢,要是将 Try/Confirm/Cancel业务逻辑单独拿出来复用,其复用性当然是不好的,Try/Confirm/Cancel 逻辑作为TCC型服务中的一部分,是不能单独作为一个组件来复用的。Try、Confirm、Cancel业务共同才构成一个组件,如果要复用,应该是复用整个TCC服务组件,而不是单独的Try/Confirm/Cancel业务。

    八、TCC服务是否需要对外暴露三个服务接口?

    不需要。TCC服务与普通的服务一样,只需要暴露一个接口,也就是它的Try业务。Confirm/Cancel业务逻辑,只是因为全局事务提交/回滚的需要才提供的,因此Confirm/Cancel业务只需要被TCC事务框架发现即可,不需要被调用它的其他业务服务所感知。

    换句话说,业务系统的其他服务在需要调用TCC服务时,根本不需要知道它是否为TCC型服务。因为,TCC服务能被其他业务服务调用的也仅仅是其Try业务,Confirm/Cancel业务是不能被其他业务服务直接调用的。

    九、TCC服务A的Confirm/Cancel业务中能否调用它依赖的TCC服务B的Confirm/Cancel业务?

    最好是不要这样做。首先,没有必要。TCC服务A依赖TCC服务B,那么[A:Try]已经将事务上下文传播给[B:Try]了,后续由TCC事务框架来调用各自的Confirm/Cancel业务即可;其次,Confirm/Cancel业务如果被允许调用其他服务,那么它就有可能再次发起新的TCC全局事务。如此递归下去,将会导致全局事务关系混乱且不可控。

    TCC全局事务,应该尽量在Try操作阶段传播事务上下文。Confirm/Cancel操作阶段仅需要完成各自Try业务操作的确认操作/补偿操作即可,不适合再做远程调用,更不能再对外传播事务上下文。

    综上所述,本文倾向于认为,实现一个通用的TCC分布式事务管理框架,还是相对比较复杂的。一般业务系统如果需要使用TCC事务机制,并不推荐自行设计实现。
    这里,给大家推荐一款开源的TCC分布式事务管理器ByteTCC。ByteTCC基于Try/Confirm/Cancel机制实现,可与Spring容器无缝集成,兼容Spring的声明式事务管理。提供对dubbo框架、Spring Cloud的开箱即用的支持,可满足多数据源、跨应用、跨服务器等各种分布式事务场景的需求。

    TCC是两阶段提交的一种么?

    经常在网络上看见有人介绍TCC时,都提一句,”TCC是两阶段提交的一种”。其理由是TCC将业务逻辑分成try、confirm/cancel在两个不同的阶段中执行。其实这个说法,是不正确的。可能是因为既不太了解两阶段提交机制、也不太了解TCC机制的缘故,于是将两阶段提交机制的prepare、commit两个事务提交阶段和TCC机制的try、confirm/cancel两个业务执行阶段互相混淆,才有了这种说法。

    两阶段提交(Two Phase Commit,下文简称2PC),简单的说,是将事务的提交操作分成了prepare、commit两个阶段。其事务处理方式为:
    1、 在全局事务决定提交时,a)逐个向RM发送prepare请求;b)若所有RM都返回OK,则逐个发送commit请求最终提交事务;否则,逐个发送rollback请求来回滚事务;
    2、 在全局事务决定回滚时,直接逐个发送rollback请求即可,不必分阶段。
    * 需要注意的是:2PC机制需要RM提供底层支持(一般是兼容XA),而TCC机制则不需要。

    TCC(Try-Confirm-Cancel),则是将业务逻辑分成try、confirm/cancel两个阶段执行,具体介绍见TCC事务机制简介。其事务处理方式为:
    1、 在全局事务决定提交时,调用与try业务逻辑相对应的confirm业务逻辑;
    2、 在全局事务决定回滚时,调用与try业务逻辑相对应的cancel业务逻辑。
    可见,TCC在事务处理方式上,是很简单的:要么调用confirm业务逻辑,要么调用cancel逻辑。这里为什么没有提到try业务逻辑呢?因为try逻辑与全局事务处理无关。

    当讨论2PC时,我们只专注于事务处理阶段,因而只讨论prepare和commit,所以,可能很多人都忘了,使用2PC事务管理机制时也是有业务逻辑阶段的。正是因为业务逻辑的执行,发起了全局事务,这才有其后的事务处理阶段。实际上,使用2PC机制时————以提交为例————一个完整的事务生命周期是:begin -> 业务逻辑 -> prepare -> commit

    再看TCC,也不外乎如此。我们要发起全局事务,同样也必须通过执行一段业务逻辑来实现。该业务逻辑一来通过执行触发TCC全局事务的创建;二来也需要执行部分数据写操作;此外,还要通过执行来向TCC全局事务注册自己,以便后续TCC全局事务commit/rollback时回调其相应的confirm/cancel业务逻辑。所以,使用TCC机制时————以提交为例————一个完整的事务生命周期是:begin -> 业务逻辑(try业务) -> commit(comfirm业务)

    综上,我们可以从执行的阶段上将二者一一对应起来:
    1、 2PC机制的业务阶段 等价于 TCC机制的try业务阶段;
    2、 2PC机制的提交阶段(prepare & commit) 等价于 TCC机制的提交阶段(confirm);
    3、 2PC机制的回滚阶段(rollback) 等价于 TCC机制的回滚阶段(cancel)。

    因此,可以看出,虽然TCC机制中有两个阶段都存在业务逻辑的执行,但其中try业务阶段其实是与全局事务处理无关的。认清了这一点,当我们再比较TCC和2PC时,就会很容易地发现,TCC不是两阶段提交,而只是它对事务的提交/回滚是通过执行一段confirm/cancel业务逻辑来实现,仅此而已。

    TCC事务机制简介

    关于TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland于2007年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。在该论文中,TCC还是以Tentative-Confirmation-Cancellation作为名称;正式以Try-Confirm-Cancel作为名称的,可能是Atomikos(Gregor Hohpe所著书籍《Enterprise Integration Patterns》中收录了关于TCC的介绍,提到了Atomikos的Try-Confirm-Cancel,并认为二者是相似的概念)。

    国内最早关于TCC的报道,应该是InfoQ上对阿里程立博士的一篇采访。经过程博士的这一次传道之后,TCC在国内逐渐被大家广为了解并接受。相应的实现方案和开源框架也先后被发布出来,ByteTCC就是其中之一。

    TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。

    对于业务系统中一个特定的业务逻辑S,其对外提供服务时,必须接受一些不确定性,即对业务逻辑执行的一次调用仅是一个临时性操作,调用它的消费方服务M保留了后续的取消权。如果M认为全局事务应该rollback,它会要求取消之前的临时性操作,这将对应S的一个取消操作;而当M认为全局事务应该commit时,它会放弃之前临时性操作的取消权,这对应S的一个确认操作。

    每一个初步操作,最终都会被确认或取消。因此,针对一个具体的业务服务,TCC事务机制需要业务系统提供三段业务逻辑:初步操作Try、确认操作Confirm、取消操作Cancel。

    1. 初步操作(Try)
    TCC事务机制中的业务逻辑(Try),从执行阶段来看,与传统事务机制中业务逻辑相同。但从业务角度来看,却不一样。TCC机制中的Try仅是一个初步操作,它和后续的确认一起才能真正构成一个完整的业务逻辑。可以认为

    1
    [传统事务机制]的业务逻辑 = [TCC事务机制]的初步操作(Try) + [TCC事务机制]的确认逻辑(Confirm)。

    TCC机制将传统事务机制中的业务逻辑一分为二,拆分后保留的部分即为初步操作(Try);而分离出的部分即为确认操作(Confirm),被延迟到事务提交阶段执行。
    TCC事务机制以初步操作(Try)为中心的,确认操作(Confirm)和取消操作(Cancel)都是围绕初步操作(Try)而展开。因此,Try阶段中的操作,其保障性是最好的,即使失败,仍然有取消操作(Cancel)可以将其不良影响进行回撤。

    2. 确认操作(Confirm)
    确认操作(Confirm)是对初步操作(Try)的一个补充。当TCC事务管理器决定commit全局事务时,就会逐个执行初步操作(Try)指定的确认操作(Confirm),将初步操作(Try)未完成的事项最终完成。

    3. 取消操作(Cancel)
    取消操作(Cancel)是对初步操作(Try)的一个回撤。当TCC事务管理器决定rollback全局事务时,就会逐个执行初步操作(Try)指定的取消操作(Cancel),将初步操作(Try)已完成的事项全部撤回。

    在传统事务机制中,业务逻辑的执行和事务的处理,是在不同的阶段由不同的部件来完成的:业务逻辑部分访问资源实现数据存储,其处理是由业务系统负责;事务处理部分通过协调资源管理器以实现事务管理,其处理由事务管理器来负责。二者没有太多交互的地方,所以,传统事务管理器的事务处理逻辑,仅需要着眼于事务完成(commit/rollback)阶段,而不必关注业务执行阶段。

    而在TCC事务机制中的业务逻辑处理和事务处理,其关系就错综复杂:业务逻辑(Try/Confirm/Cancel)阶段涉及所参与资源事务的commit/rollback;全局事务commit/rollback时又涉及到业务逻辑(Try/Confirm/Cancel)的执行。其中关系,本站将另行撰文详细介绍,敬请关注!

    参考文献:

    1. http://www.infoq.com/cn/interviews/soa-chengli
    2. https://cs.brown.edu/courses/cs227/archives/2012/papers/weaker/cidr07p15.pdf
    3. http://www.enterpriseintegrationpatterns.com/patterns/conversation/TryConfirmCancel.html
    4. http://blog.51cto.com/robertleepeak/2083454?wx=
    展开全文
  • Seata框架是一个业务层的XA(两阶段提交)解决方案。在理解Seata分布式事务机制前,我们先回顾一下数据库层面的XA方案。一. MySQL XA方案MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:RM(Resource ...
  • Seata框架是一个业务层的XA(两阶段提交)解决方案。在理解Seata分布式事务机制前,我们先回顾一下数据库层面的XA方案。1. MySQL XA方案MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:RM(Resource ...
  • Seata分布式事务框架-AT模式 eata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 Seata ...
  • 阿里分布式事务框架Seata原理解析 作者:伊凡的一天 链接:https://www.jianshu.com/p/044e95223a17 Seata框架是一个业务层的XA(两阶段提交)解决方案。在理解Seata分布式事务机制前,我们先回顾一下数据库...
  • 高扩展,高性能)等优点,但是缺点也很明显,分布式项目一般都是分服务开发,且多个服务部署在不同的服务器上,这样就产了分布式事务, 使用本地事务技术处理分布式事务就无法生效,此时就需要一个优秀的分布式事务框架来...
  • 分布式事务框架Seata原理分析 (一) 客户端 分布式事务框架Seata原理分析 (二) 服务端 的研究,对之前的分析研究在这里做一个总结。 这里讨论的都是基于Seata的AT模式 通过前面的研究,可以发现,seata的分布式...
  • Seata 分布式事务原理Seata整体机制两阶段提交协议的演变:一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向...
  • 这是一个在dubbo rpc服务框架上实现的分布式事务补偿框架,该分布式事务框架没有采用传统的2pc(两阶段分布式事务提交的协议), 因为两阶段分布式事务提交协议有比较大的阻塞性能问题。所以distribu
  • 随着微服务、程序性能要求越来越高的情况下,微服务...传统跨数据库的事务有比较著名的两阶段提交、三阶段提交等。 站在一个比较高的角度来看,Seata实际上是在各个库执行前通过代理将执行前的数据保存下来,通过一个
  • 本篇文章将通过Seata第三方分布式事务框架,模拟新用户注册的同时,赠送用户积分过程。 用户微服务,插入新用户信息,连接用户数据库; 积分微服务,插入用户积分信息,连接积分数据库; 上述个步骤,要么一起...
  • 分布式事务想聊聊分布式事务。看了网上的一些说法,仔细思考之后感觉都不大统一,有些就是不对。本文加入了一些自己的思考,讨论了一些实现中的细节,如果不对欢迎指正。先说说两阶段和三阶段提交吧。两阶段提交我...
  • 下面就是两阶段提交的过程: 第一阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交. 第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。 缺点: 单点问题:事务...
  • X/Open DTP(X/Open Distributed Transaction Processing Reference Model)是X/Open这个组织定义的一套分布式事务的标准,提出用两阶段提交(2PC,Two-Phase-Commit)来保证分布式事务的完整性。 AP:Appl
  • 这里写目录标题一、简介背景二、理论依据CAP原则`BASE`理论基本可用最终一致性【有延迟】三、柔性事务解决方案一、两阶段型1.1 XA两段式【基础】阶段一:提交事务请求阶段二:执行事务提交总结1.2 三段式【变种】...
  • 如果都成功,则进行Confirm操作,如果任意一个Try发生错误,则全部Cancel,特征在于它不依赖 RM 对分布式事务的支持,而是通过对业务逻辑的分解来实现分布式事务,不同于AT的是就是需要自行定义各个阶段的逻辑,对...
  • 分布式事务

    2019-08-23 10:26:43
    文章目录什么是分布式事务分布式事务产生的原因什么是分布式事务分布式理论CAP理论BASE 理论分布式事务解决方案分布式事务模型与规范常见的分布式事务解决方案两阶段提交(2PC)TCC基于消息的最终一致性方案 ...
  • 需求背景: 由于系统涉及到多个数据源,所以想用两阶段提交事务(分布式事务)来保证数据的一致性,完整性。 测试环境: OS--&gt;win2000 DB--&gt;sql server 2K j2ee容器和IDE--&gt;WSAD 开发...
  • 为了解决大家在实施分布式服务化架构过程中关于分布式事务问题的困扰,本教程将基于支付系统真实业务中的经典场景来对“可靠消息的最终一致性方案”、“TCC两阶段型方案”和“最大努力通知型方案”这3种柔性事务解决...
  • 这里实现的两阶段就是:一阶段实现sql执行,二阶段全提交或全回滚 关键点描述: 1.@OuterTransactional用于声明参与分布式事务的service方法注解【@OuterTransactional、@Transactional同时存在于方法上】 2.TC...
  • 引言: Spring 通过AOP技术可以让我们在脱离EJB的...用框架的话只要配置好对个数据源,和工厂,公用一个JtaTransactionManager事物管理器(xa事物管理),一个事物中有多个不同数据库操作不同数据源(xa数据源)的时...
  • 由于系统涉及到多个数据源,所以想用两阶段提交事务(分布式事务)来保证数据的一致性,完整性。 [b]测试环境:[/b] OS-->win2000 DB-->sql server 2K j2ee容器和IDE-->WSAD 开发框架:struts+...
  • 分布式事务处理技术之LCN

    千次阅读 2019-09-18 14:13:14
    分布式事务LCN 第一章 分布式事务介绍一、什么是分布式事务二、XA 的两阶段提交方案三、TCC 解决方案四、分布式事务...LCN分布式事务框架应用一、LCN 分布式事务框架应用环境搭建创建入口项目 springcloud-porta...

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 173
精华内容 69
关键字:

两阶段分布式事务框架