精华内容
下载资源
问答
  • java实现分布式事务

    2020-10-07 13:53:02
    如何实现两个分布式服务(订单服务、库存服务)共同完成一件事即订单支付成功自动自动减库存,这里 的关键是如何保证两个分布式服务的事务的一致性。 尝试解决上边的需求,在订单服务中远程调用减库存接口,伪代码...

    问题描述
    用户支付完成会将支付状态及订单状态保存在订单数据库中,由订单服务去维护订单数据库。由库存服务去维护库存数据库的信息。下图是系统结构图:

    在这里插入图片描述

    如何实现两个分布式服务(订单服务、库存服务)共同完成一件事即订单支付成功自动减库存,这里
    的关键是如何保证两个分布式服务的事务的一致性。
    尝试解决上边的需求,在订单服务中远程调用减库存接口,伪代码如下:

    订单支付结果通知方法{

    ​ 更新支付表中支付状态为“成功”。 ​
    远程调用减库存接口减库存。

    上边的逻辑说明:
    1、更新支付表状态为本地数据库操作。
    2、远程调用减库存接口为网络远程调用请求。
    3、为保存事务上边两步操作由spring控制事务,当遇到Exception异常则回滚本地数据库操作。
    问题如下:
    1、如果更新支付表失败则抛出异常,不再执行远程调用,此设想没有问题。
    2、如果更新支付表成功,网络远程调用超时会拉长本地数据库事务时间,影响数据库性能。
    3、如果更新支付表成功,远程调用减库存成功(减库存数据库commit成功),最后更新支付表commit失败,此时出现操作不一致。
    上边的问题涉及到分布式事务控制。

    什么是分布式事务

    什么是分布式系统:

    部署在不同结点上的系统通过网络交互来完成协同工作的系统。
    比如:充值加积分的业务,用户在充值系统向自己的账户充钱,在积分系统中自己积分相应的增加。充值系统和积分系统是两个不同的系统,一次充值加积分的业务就需要这两个系统协同工作来完成。

    什么是事务:

    事务是指由一组操作组成的一个工作单元,这个工作单元具有原子性(atomicity)、一致性(consistency)、隔
    离性(isolation)和持久性(durability)。
    原子性:执行单元中的操作要么全部执行成功,要么全部失败。如果有一部分成功一部分失败那么成功的操作要全
    部回滚到执行前的状态。

    一致性:执行一次事务会使用数据从一个正确的状态转换到另一个正确的状态,执行前后数据都是完整的。

    隔离性:在该事务执行的过程中,任何数据的改变只存在于该事务之中,对外界没有影响,事务与事务之间是完全的隔离的。只有事务提交后其它事务才可以查询到最新的数据。

    持久性:事务完成后对数据的改变会永久性的存储起来,即使发生断电宕机数据依然在。

    什么是本地事务:

    本地事务就是用关系数据库来控制事务,关系数据库通常都具有ACID特性,传统的单体应用通常会将数据全部存储在一个数据库中,会借助关系数据库来完成事务控制。

    什么是分布式事务:

    在分布式系统中一次操作由多个系统协同完成,这种一次事务操作涉及多个系统通过网络协同完成的过程称为分布式事务。这里强调的是多个系统通过网络协同完成一个事务的过程,并不强调多个系统访问了不同的数据库,即使多个系统访问的是同一个数据库也是分布式事务,如下图:

    在这里插入图片描述

    另外一种分布式事务的表现是,一个应用程序使用了多个数据源连接了不同的数据库,当一次事务需要操作多个数据源,此时也属于分布式事务,当系统作了数据库拆分后会出现此种情况。

    在这里插入图片描述
    分布式事务有哪些应用场景:

    1)电商系统中的下单扣库存
    电商系统中,订单系统和库存系统是两个系统,一次下单的操作由两个系统协同完成
    2)金融系统中的银行卡充值
    在金融系统中通过银行卡向平台充值需要通过银行系统和金融系统协同完成。
    3)教育系统中下单选课业务
    在线教育系统中,用户购买课程,下单支付成功后学生选课成功,此事务由订单系统和选课系统协同完成。
    4) SNS系统的消息发送
    在社交系统中发送站内消息同时发送手机短信,一次消息发送由站内消息系统和手机通信系统协同完成。

    如何进行分布式事务控制

    CAP理论
    CAP理论是分布式事务处理的理论基础:分布式系统在设计时只能在一致性(Consistency)、可用性(Availability)、分区容忍性(PartitionTolerance)中满足两种,无法兼顾三种。

    在这里插入图片描述

    一致性(Consistency):服务A、B、C三个结点都存储了用户数据, 三个结点的数据需要保持同一时刻数据一致性。
    可用性(Availability):服务A、B、C三个结点,其中一个结点宕机不影响整个集群对外提供服务,如果只有服务A结点,当服务A宕机整个系统将无法提供服务,增加服务B、C是为了保证系统的可用性。
    分区容忍性(Partition Tolerance):分区容忍性就是允许系统通过网络协同工作,分区容忍性要解决由于网络分区导致数据的不完整及无法访问等问题。

    分布式系统不可避免的出现了多个系统通过网络协同工作的场景,结点之间难免会出现网络中断、网延延迟等现
    象,这种现象一旦出现就导致数据被分散在不同的结点上,这就是网络分区

    分布式系统如果兼顾CAP:
    在保证分区容忍性的前提下一致性和可用性无法兼顾,如果要提高系统的可用性就要增加多个结点,如果要保证数据的一致性就要实现每个结点的数据一致,结点越多可用性越好,但是数据一致性越差。
    所以,在进行分布式系统设计时,同时满足“一致性”、“可用性”和“分区容忍性”三者是几乎不可能的。
    CAP有哪些组合方式?

    CA:放弃分区容忍性,加强一致性和可用性,关系数据库按照CA进行设计。
    AP:放弃一致性,加强可用性和分区容忍性,追求最终一致性,很多NoSQL数据库按照AP进行设计。
    说明:这里放弃一致性是指放弃强一致性,强一致性就是写入成功立刻要查询出最新数据。追求最终一致性是指允许暂时的数据不一致,只要最终在用户接受的时间内数据 一致即可。
    CP:放弃可用性,加强一致性和分区容忍性,一些强一致性要求的系统按CP进行设计,比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。说明:由于网络问题的存在CP系统可能会出现待等待超时,如果没有处理超时问题则整理系统会出现阻塞。

    总结

    ​ 在分布式系统设计中AP的应用较多,即保证分区容忍性和可用性,牺牲数据的强一致性(写操作后立刻读取到最新数据),保证数据最终一致性。比如:订单退款,今日退款成功,明日账户到账,只要在预定的用户可以接受的时间内退款事务走完即可。

    分布式事务一致性解决方案

    两阶段提交协议(2PC)

    为解决分布式系统的数据一致性问题出现了两阶段提交协议(2 Phase Commitment Protocol),两阶段提交由协调者和参与者组成,共经过两个阶段和三个操作,部分关系数据库如Oracle、MySQL支持两阶段提交协议,本节讲解关系数据库两阶段提交协议。

    参考:2PC:https://en.wikipedia.org/wiki/Two-phase_commit_protocol

    2PC协议流程图

    在这里插入图片描述

    1)第一阶段:准备阶段(prepare)
    协调者通知参与者准备提交订单,参与者开始投票。
    参与者完成准备工作向协调者回应Yes|NO。
    2)第二阶段:提交(commit)/回滚(rollback)阶段
    协调者根据参与者的投票结果发起最终的提交指令。
    如果有参与者没有准备好则发起回滚指令。

    一个下单减库存的例子:

    在这里插入图片描述

    1、应用程序连接两个数据源。
    2、应用程序通过事务协调器向两个库发起prepare,两个数据库收到消息分别执行本地事务(记录日志),但不提交,如果执行成功则回复yes,否则回复no。
    3、事务协调器收到回复,只要有一方回复no则分别向参与者发起回滚事务,参与者开始回滚事务。
    4、事务协调器收到回复,全部回复yes,此时向参与者发起提交事务。如果参与者有一方提交事务失败则由事务协调器发起回滚事务。

    2PC的优点:实现强一致性,部分关系数据库支持(Oracle、MySQL等)。
    缺点:整个事务的执行需要由协调者在多个节点之间去协调,增加了事务的执行时间,性能低下。
    解决方案:springboot+Atomikos or Bitronix

    3PC主要是解决协调者与参与者通信阻塞问题而产生的,它比2PC传递的消息还要多,性能不高。详细参考3PC:
    https://en.wikipedia.org/wiki/Three-phase_commit_protocol

    事务补偿 TCC

    TCC事务补偿是基于2PC实现的业务层事务控制方案,它是Try、Confirm和Cancel三个单词的首字母,含义如下:
    1、Try 检查及预留业务资源完成提交事务前的检查,并预留好资源。
    2、Confirm确定执行业务操作对try阶段预留的资源正式执行。
    3、Cancel取消执行业务操作对try阶段预留的资源释放。

    下边用一个下单减库存的业务为例来说明:

    在这里插入图片描述

    1、Try
    下单业务由订单服务和库存服务协同完成,在try阶段订单服务和库存服务完成检查和预留资源。
    订单服务检查当前是否满足提交订单的条件(比如:当前存在未完成订单的不允许提交新订单)。
    库存服务检查当前是否有充足的库存,并锁定资源。
    2、Confirm
    订单服务和库存服务成功完成Try后开始正式执行资源操作。
    订单服务向订单写一条订单信息。
    库存服务减去库存。
    3、Cancel
    如果订单服务和库存服务有一方出现失败则全部取消操作。
    订单服务需要删除新增的订单信息。
    库存服务将减去的库存再还原。
    优点:最终保证数据的一致性,在业务层实现事务控制,灵活性好。
    缺点:开发成本高,每个事务操作每个参与者都需要实现try/confirm/cancel三个接口。

    注意:TCC的try/confirm/cancel接口都要实现幂等性,在为在try、confirm、cancel失败后要不断重试。

    什么是幂等性?
    幂等性是指同一个操作无论请求多少次,其结果都相同。 幂等操作实现方式有:
    1、操作之前在业务方法进行判断如果执行过了就不再执行。
    2、缓存所有请求和处理的结果,已经处理的请求则直接返回结果。
    3、在数据库表中加一个状态字段(未处理,已处理),数据操作时判断未处理时再处理。

    消息队列实现最终一致性

    本方案是将分布式事务拆分成多个本地事务来完成,并且由消息队列异步协调完成,如下图:
    下边以下单减少库存为例来说明:

    在这里插入图片描述

    可以把MQ去掉不使用MQ

    1、订单服务和库存服务完成检查和预留资源。
    2、订单服务在本地事务中完成添加订单表记录和添加“减少库存任务消息”。
    3、由定时任务根据消息表的记录发送给MQ通知库存服务执行减库存操作。
    4、库存服务执行减少库存,并且记录执行消息状态(为避免重复执行消息,在执行减库存之前查询是否执行过此消息)。
    5、库存服务向MQ发送完成减少库存的消息。
    6、订单服务接收到完成库存减少的消息后删除原来添加的“减少库存任务消息”。
    实现最终事务一致要求:预留资源成功理论上要求正式执行成功,如果执行失败会进行重试,要求业务执行方法实现幂等。

    优点
    由MQ按异步的方式协调完成事务,性能较高。
    不用实现try/confirm/cancel接口,开发成本比TCC低。
    缺点
    此方式基于关系数据库本地事务来实现,会出现频繁读写数据库记录,浪费数据库资源,另外对于高并发操作不是最佳方案。

    展开全文
  • java实现分布式事务的三种方案

    千次阅读 2019-10-20 23:50:51
    如何实现两个分布式服务(订单服务、库存服务)共同完成一件事即订单支付成功自动自动减库存,这里 的关键是如何保证两个分布式服务的事务的一致性。 尝试解决上边的需求,在订单服务中远程调用减库存接口,伪代码...

    问题描述:

    用户支付完成会将支付状态及订单状态保存在订单数据库中,由订单服务去维护订单数据库。由库存服务去维护库存数据库的信息。下图是系统结构图:

    如何实现两个分布式服务(订单服务、库存服务)共同完成一件事即订单支付成功自动减库存,这里的关键是如何保证两个分布式服务的事务的一致性。
    尝试解决上边的需求,在订单服务中远程调用减库存接口,伪代码如下:

    订单支付结果通知方法{

    ​ 更新支付表中支付状态为“成功”。
    ​ 远程调用减库存接口减库存。

    上边的逻辑说明:
    1、更新支付表状态为本地数据库操作。
    2、远程调用减库存接口为网络远程调用请求。
    3、为保存事务上边两步操作由spring控制事务,当遇到Exception异常则回滚本地数据库操作。
    问题如下:
    1、如果更新支付表失败则抛出异常,不再执行远程调用,此设想没有问题。
    2、如果更新支付表成功,网络远程调用超时会拉长本地数据库事务时间,影响数据库性能。
    3、如果更新支付表成功,远程调用减库存成功(减库存数据库commit成功),最后更新支付表commit失败,此时出现操作不一致。
    上边的问题涉及到分布式事务控制。

    什么是分布式事务

    什么是分布式系统:

    部署在不同结点上的系统通过网络交互来完成协同工作的系统。
    比如:充值加积分的业务,用户在充值系统向自己的账户充钱,在积分系统中自己积分相应的增加。充值系统和积分系统是两个不同的系统,一次充值加积分的业务就需要这两个系统协同工作来完成。

    什么是事务:

    事务是指由一组操作组成的一个工作单元,这个工作单元具有原子性(atomicity)、一致性(consistency)、隔
    离性(isolation)和持久性(durability)。
    原子性:执行单元中的操作要么全部执行成功,要么全部失败。如果有一部分成功一部分失败那么成功的操作要全
    部回滚到执行前的状态。

    一致性:执行一次事务会使用数据从一个正确的状态转换到另一个正确的状态,执行前后数据都是完整的。

    隔离性:在该事务执行的过程中,任何数据的改变只存在于该事务之中,对外界没有影响,事务与事务之间是完全的隔离的。只有事务提交后其它事务才可以查询到最新的数据。

    持久性:事务完成后对数据的改变会永久性的存储起来,即使发生断电宕机数据依然在。

    什么是本地事务:

    本地事务就是用关系数据库来控制事务,关系数据库通常都具有ACID特性,传统的单体应用通常会将数据全部存储在一个数据库中,会借助关系数据库来完成事务控制。

    什么是分布式事务:

    在分布式系统中一次操作由多个系统协同完成,这种一次事务操作涉及多个系统通过网络协同完成的过程称为分布
    式事务。这里强调的是多个系统通过网络协同完成一个事务的过程,并不强调多个系统访问了不同的数据库,即使
    多个系统访问的是同一个数据库也是分布式事务,如下图:

    另外一种分布式事务的表现是,一个应用程序使用了多个数据源连接了不同的数据库,当一次事务需要操作多个数
    据源,此时也属于分布式事务,当系统作了数据库拆分后会出现此种情况。

    分布式事务有哪些应用场景:

    1. 电商系统中的下单扣库存
      电商系统中,订单系统和库存系统是两个系统,一次下单的操作由两个系统协同完成
      2)金融系统中的银行卡充值
      在金融系统中通过银行卡向平台充值需要通过银行系统和金融系统协同完成。
      3)教育系统中下单选课业务
      在线教育系统中,用户购买课程,下单支付成功后学生选课成功,此事务由订单系统和选课系统协同完成。
      4) SNS系统的消息发送
      在社交系统中发送站内消息同时发送手机短信,一次消息发送由站内消息系统和手机通信系统协同完成。

    如何进行分布式事务控制

    CAP理论

    CAP理论是分布式事务处理的理论基础:分布式系统在设计时只能在一致性(Consistency)、可用性(Availability)、分区容忍性(PartitionTolerance)中满足两种,无法兼顾三种。

    一致性(Consistency):服务A、B、C三个结点都存储了用户数据, 三个结点的数据需要保持同一时刻数据一致性。
    可用性(Availability):服务A、B、C三个结点,其中一个结点宕机不影响整个集群对外提供服务,如果只有服务A结点,当服务A宕机整个系统将无法提供服务,增加服务B、C是为了保证系统的可用性。
    分区容忍性(Partition Tolerance):分区容忍性就是允许系统通过网络协同工作,分区容忍性要解决由于网络分区导致数据的不完整及无法访问等问题。

    分布式系统不可避免的出现了多个系统通过网络协同工作的场景,结点之间难免会出现网络中断、网延延迟等现
    象,这种现象一旦出现就导致数据被分散在不同的结点上,这就是网络分区

    分布式系统如果兼顾CAP:

    在保证分区容忍性的前提下一致性和可用性无法兼顾,如果要提高系统的可用性就要增加多个结点,如果要保证数据的一致性就要实现每个结点的数据一致,结点越多可用性越好,但是数据一致性越差。
    所以,在进行分布式系统设计时,同时满足“一致性”、“可用性”和“分区容忍性”三者是几乎不可能的。
    CAP有哪些组合方式?

    1. CA:放弃分区容忍性,加强一致性和可用性,关系数据库按照CA进行设计。
    2. AP:放弃一致性,加强可用性和分区容忍性,追求最终一致性,很多NoSQL数据库按照AP进行设计。
      说明:这里放弃一致性是指放弃强一致性,强一致性就是写入成功立刻要查询出最新数据。追求最终一致性是指允许暂时的数据不一致,只要最终在用户接受的时间内数据 一致即可。
    3. CP:放弃可用性,加强一致性和分区容忍性,一些强一致性要求的系统按CP进行设计,比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。说明:由于网络问题的存在CP系统可能会出现待等待超时,如果没有处理超时问题则整理系统会出现阻塞。

    总结

    ​ 在分布式系统设计中AP的应用较多,即保证分区容忍性和可用性,牺牲数据的强一致性(写操作后立刻读取到最新数据),保证数据最终一致性。比如:订单退款,今日退款成功,明日账户到账,只要在预定的用户可以接受的时间内退款事务走完即可。

    分布式事务一致性解决方案

    两阶段提交协议(2PC)

    ​ 为解决分布式系统的数据一致性问题出现了两阶段提交协议(2 Phase Commitment Protocol),两阶段提交由协调者和参与者组成,共经过两个阶段和三个操作,部分关系数据库如Oracle、MySQL支持两阶段提交协议,本节讲解关系数据库两阶段提交协议。

    参考:
    2PC:https://en.wikipedia.org/wiki/Two-phase_commit_protocol

    2PC协议流程图

    1)第一阶段:准备阶段(prepare)
    协调者通知参与者准备提交订单,参与者开始投票。
    参与者完成准备工作向协调者回应Yes|NO。
    2)第二阶段:提交(commit)/回滚(rollback)阶段
    协调者根据参与者的投票结果发起最终的提交指令。
    如果有参与者没有准备好则发起回滚指令。

    一个下单减库存的例子:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gg22D205-1571586550916)(https://i.loli.net/2019/10/20/4l8sradhvi1BuqG.png)]

    1、应用程序连接两个数据源。
    2、应用程序通过事务协调器向两个库发起prepare,两个数据库收到消息分别执行本地事务(记录日志),但不提交,如果执行成功则回复yes,否则回复no。
    3、事务协调器收到回复,只要有一方回复no则分别向参与者发起回滚事务,参与者开始回滚事务。
    4、事务协调器收到回复,全部回复yes,此时向参与者发起提交事务。如果参与者有一方提交事务失败则由事务协调器发起回滚事务。

    2PC的优点:实现强一致性,部分关系数据库支持(Oracle、MySQL等)。
    缺点:整个事务的执行需要由协调者在多个节点之间去协调,增加了事务的执行时间,性能低下。
    解决方案有:springboot+Atomikos or Bitronix
    3PC主要是解决协调者与参与者通信阻塞问题而产生的,它比2PC传递的消息还要多,性能不高。详细参考3PC:
    https://en.wikipedia.org/wiki/Three-phase_commit_protocol

    事务补偿 TCC

    TCC事务补偿是基于2PC实现的业务层事务控制方案,它是Try、Confirm和Cancel三个单词的首字母,含义如下:
    1、Try 检查及预留业务资源完成提交事务前的检查,并预留好资源。
    2、Confirm确定执行业务操作对try阶段预留的资源正式执行。
    3、Cancel取消执行业务操作对try阶段预留的资源释放。

    下边用一个下单减库存的业务为例来说明:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WV6xbtry-1571586550917)(https://i.loli.net/2019/10/20/qndSjc5VOaKMAh8.png)]

    1、Try
    下单业务由订单服务和库存服务协同完成,在try阶段订单服务和库存服务完成检查和预留资源。
    订单服务检查当前是否满足提交订单的条件(比如:当前存在未完成订单的不允许提交新订单)。
    库存服务检查当前是否有充足的库存,并锁定资源。
    2、Confirm
    订单服务和库存服务成功完成Try后开始正式执行资源操作。
    订单服务向订单写一条订单信息。
    库存服务减去库存。
    3、Cancel
    如果订单服务和库存服务有一方出现失败则全部取消操作。
    订单服务需要删除新增的订单信息。
    库存服务将减去的库存再还原。
    优点:最终保证数据的一致性,在业务层实现事务控制,灵活性好。
    缺点:开发成本高,每个事务操作每个参与者都需要实现try/confirm/cancel三个接口。

    注意:TCC的try/confirm/cancel接口都要实现幂等性,在为在try、confirm、cancel失败后要不断重试。

    什么是幂等性?
    幂等性是指同一个操作无论请求多少次,其结果都相同。
    幂等操作实现方式有:
    1、操作之前在业务方法进行判断如果执行过了就不再执行。
    2、缓存所有请求和处理的结果,已经处理的请求则直接返回结果。
    3、在数据库表中加一个状态字段(未处理,已处理),数据操作时判断未处理时再处理。

    消息队列实现最终一致性

    本方案是将分布式事务拆分成多个本地事务来完成,并且由消息队列异步协调完成,如下图:
    下边以下单减少库存为例来说明:

    可以把MQ去掉不使用MQ

    1、订单服务和库存服务完成检查和预留资源。
    2、订单服务在本地事务中完成添加订单表记录和添加“减少库存任务消息”。
    3、由定时任务根据消息表的记录发送给MQ通知库存服务执行减库存操作。
    4、库存服务执行减少库存,并且记录执行消息状态(为避免重复执行消息,在执行减库存之前查询是否执行过此消息)。
    5、库存服务向MQ发送完成减少库存的消息。
    6、订单服务接收到完成库存减少的消息后删除原来添加的“减少库存任务消息”。
    实现最终事务一致要求:预留资源成功理论上要求正式执行成功,如果执行失败会进行重试,要求业务执行方法实现幂等。

    优点 :
    由MQ按异步的方式协调完成事务,性能较高。
    不用实现try/confirm/cancel接口,开发成本比TCC低。
    缺点:
    此方式基于关系数据库本地事务来实现,会出现频繁读写数据库记录,浪费数据库资源,另外对于高并发操作不是最佳方案。

    浅谈分布式事务

    展开全文
  • 在上一篇《java事务(二)——本地事务》中...但是在本篇中并不会说明如何使用JTA,而是在不依赖其他框架以及jar包的情况下自己来实现分布式事务,作为对分布式事务的一个理解。假设现在有两个数据库,可以是在一台机...

    在上一篇《java事务(二)——本地事务》中已经提到了事务的类型,并对本地事务做了说明。而分布式事务是跨越多个数据源来对数据来进行访问和更新,在JAVA中是使用JTA(Java Transaction API)来实现分布式的事务管理的。但是在本篇中并不会说明如何使用JTA,而是在不依赖其他框架以及jar包的情况下自己来实现分布式事务,作为对分布式事务的一个理解。

    假设现在有两个数据库,可以是在一台机器上也可以是在不同机器上,现在要向其中一个数据库更新用户账户信息,另外一个数据库新增用户的消费信息。首先说明一下,分布式事务也是事务,在事务特性的那篇博客中就已经说明了事务的四个特性:原子性、一致性、隔离性和持久性,那么分布式事务也必然是符合这四个特性的,这就要求同时对两个数据库进行数据访问和更新的时候是作为一个单独的工作单元来进行处理,并且同时成功或者失败后进行回滚。但是在说明本地事务的时候已经提到了,本地事务是基于连接的,现在有两个数据库,分别保存数据,那么为了实现这个事务,必然会有两个数据库连接,这似乎是与事务基于连接的说法相悖。现在举个例子:之前回老家去了一趟医院,后来在办理出院手续的时候是这样的,办理出院时需要护士站的主任医生填写出院单,然后携带结账单到收费处缴纳费用并去药房取药,然后回护士站盖章,出院手续办理完毕。如果把不同地点的窗口看成是不同的连接,那么实现办理出院手续这个事务就必须保证在每个业务窗口上的事务都是成功的,最后出院手续才算真正完成。在最终盖章的时候,需要查看每个窗口给出的单子是否是已办理的,只有综合起来所有的单子才能判定出院手续是否成功。这主要就是为了说明分布式事务实现的关键其实是管理每个连接上的事务,用一个东西来判定每个连接上的事务执行情况,综合起来作为分布式事务执行成功与否的依据。这大概就是事务管理器要做的事情。虽然这个例子并不太恰当,很有挑毛病的地方,但是在不太钻牛角尖的情况下,还是可以用来说明要表达的东西的。

    实现例子

    我打开了两台虚拟机,分别命令为node1、node2,每台虚拟机上都安装了MySQL数据库,现在向node1上的数据库更新用户账户信息,向node2上的数据库新增用户消费信息。

    在node1上创建账户表,建表语句如下:

    CREATE TABLEACCOUNTS

    (

    IDINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',

    CUSTOMER_NOVARCHAR(25) NOT NULL COMMENT '客户号',

    CUSTOMER_NAMEVARCHAR(25) NOT NULL COMMENT '客户名称',

    CARD_IDVARCHAR(18) NOT NULL COMMENT '身份证号',

    BANK_IDVARCHAR(25) NOT NULL COMMENT '开户行ID',

    BALANCEDECIMAL NOT NULL COMMENT '账户余额',

    CURRENCYVARCHAR(10) NOT NULL COMMENT '币种',PRIMARY KEY(ID)

    )

    COMMENT= '账户表' ;

    然后向表中插入一条记录,如下图:

    2480ee4f09da4f2eca32a6db8dc91c11.png

    在node2上创建用户消费历史表,建表语句如下:

    CREATE TABLEUSER_PURCHASE_HIS

    (

    IDINT NOT NULL AUTO_INCREMENT COMMENT '自增主键',

    CUSTOMER_NOVARCHAR(25) NOT NULL COMMENT '客户号',

    SERIAL_NOVARCHAR(32) NOT NULL COMMENT '交易流水号',

    AMOUNTDECIMAL NOT NULL COMMENT '交易金额',

    CURRENCYVARCHAR(10) NOT NULL COMMENT '币种',

    REMARKVARCHAR(100) NOT NULL COMMENT '备注',PRIMARY KEY(ID)

    )

    COMMENT= '用户消费历史表';

    下面实现一个简陋的例子,代码如下:

    1、创建DBUtil类,用来获取和关闭连接

    packageperson.lb.example1;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.sql.Statement;public classDBUtil {static{try{//加载驱动类

    Class.forName("com.mysql.jdbc.Driver");

    }catch(ClassNotFoundException e) {

    e.printStackTrace();

    }

    }//获取node1上的数据库连接

    public staticConnection getNode1Connection() {

    Connection conn= null;try{

    conn=(Connection) DriverManager.getConnection("jdbc:mysql://192.168.0.108:3306/TEST","root","root");

    }catch(SQLException e) {

    e.printStackTrace();

    }returnconn;

    }//获取node2上的数据库连接

    public staticConnection getNode2Connection() {

    Connection conn= null;try{

    conn=(Connection) DriverManager.getConnection("jdbc:mysql://192.168.0.109:3306/TEST","root","root");

    }catch(SQLException e) {

    e.printStackTrace();

    }returnconn;

    }//关闭连接

    public static voidclose(ResultSet rs, Statement st, Connection conn) {try{if(rs != null) {

    rs.close();

    }if(st != null) {

    st.close();

    }if(conn != null) {

    conn.close();

    }

    }catch(SQLException e) {//TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    }

    2、创建XADemo类,用来测试事务

    packageperson.lb.example1;importjava.sql.Connection;importjava.sql.SQLException;importjava.sql.Statement;public classXADemo {public static voidmain(String[] args) {//获取连接

    Connection node1Conn =DBUtil.getNode1Connection();

    Connection node2Conn=DBUtil.getNode2Connection();try{//设置连接为非自动提交

    node1Conn.setAutoCommit(false);

    node2Conn.setAutoCommit(false);//更新账户信息

    updateAccountInfo(node1Conn);//增加用户消费信息

    addUserPurchaseInfo(node2Conn);//提交

    node1Conn.commit();

    node2Conn.commit();

    }catch(SQLException e) {

    e.printStackTrace();//回滚

    try{

    node1Conn.rollback();

    node2Conn.rollback();

    }catch(SQLException e1) {

    e1.printStackTrace();

    }

    }finally{//关闭连接

    DBUtil.close(null, null, node1Conn);

    DBUtil.close(null, null, node2Conn);

    }

    }/*** 更新账户信息

    *@paramconn

    *@throwsSQLException*/

    private static void updateAccountInfo(Connection conn) throwsSQLException {

    Statement st=conn.createStatement();

    st.execute("UPDATE ACCOUNTS SET BALANCE = CAST('9900.00' AS DECIMAL) WHERE CUSTOMER_NO = '88888888' ");

    }/*** 增加用户消费信息

    *@paramconn

    *@throwsSQLException*/

    private static void addUserPurchaseInfo(Connection conn) throwsSQLException {

    Statement st=conn.createStatement();

    st.execute("INSERT INTO USER_PURCHASE_HIS(CUSTOMER_NO, SERIAL_NO, AMOUNT, CURRENCY, REMARK) "

    + " VALUES ('88888888', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 100, 'CNY', '买衣服')");

    }

    }

    这是一个没有发生任何异常的例子,执行结果是nod1上ACCOUNTS 表中的BALANCE字段的值成功更新为9900,而node2上USER_PURCHASE_HIS表中新增了一条记录,两个连接上的事务都成功完成,事务目标实现。如果反向测试一下,更改Insert语句,把其中某一个要插入的值改为NULL,由于字段都是非空限制,所以会发生异常,这个连接上的事务会失败,那么跟它关联的node1上的事务也必须回滚,不对数据库进行任何更改。经测试,结果与预期目标一致。说明这个例子是符合事务特性的。

    但是这个例子不管是从代码的可读性和可维护性上来说都是比较差的。在使用spring开发项目的时候,配置了事务管理器以后,在我们的业务逻辑中几乎是察觉不到事务控制的,而且也看不到事务控制的代码。那么究竟spring中是怎么实现的事务控制呢,这篇博客中不会详细说明,但是要提到两个东西,事务管理器和资源管理器,现在自己来实现一个简单的事务管理器和资源管理器来对事务进行控制。

    代码示例如下:

    1、创建AbstractDataSource 类

    packageperson.lb.datasource;importjava.sql.Connection;importjava.sql.SQLException;public abstract classAbstractDataSource {//获取连接

    public abstract Connection getConnection() throwsSQLException ;//关闭连接

    public abstract void close() throwsSQLException;

    }

    2、创建Node1DataSource 类,用来连接node1上的数据库

    packageperson.lb.datasource;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.SQLException;public class Node1DataSource extendsAbstractDataSource {//使用ThreadLocal类保存当前线程使用的Connection

    protected static final ThreadLocal threadSession = new ThreadLocal();static{try{//加载驱动类

    Class.forName("com.mysql.jdbc.Driver");

    }catch(ClassNotFoundException e) {

    e.printStackTrace();

    }

    }private final static Node1DataSource node1DataSource = newNode1DataSource();privateNode1DataSource() {}public staticNode1DataSource getInstance() {returnnode1DataSource;

    }/*** 获取连接*/@Overridepublic Connection getConnection() throwsSQLException {

    Connection conn= null;if(threadSession.get() == null) {

    conn=(Connection) DriverManager.getConnection("jdbc:mysql://192.168.0.108:3306/TEST","root","root");

    threadSession.set(conn);

    }else{

    conn=threadSession.get();

    }returnconn;

    }/*** 关闭并移除连接*/@Overridepublic void close() throwsSQLException {

    Connection conn=threadSession.get();if(conn != null) {

    conn.close();

    threadSession.remove();

    }

    }

    }

    3、创建Node2DataSource类,用来连接node2机器上的数据库

    packageperson.lb.datasource;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.SQLException;public class Node2DataSource extendsAbstractDataSource {//使用ThreadLocal类保存当前线程使用的Connection

    protected static final ThreadLocal threadSession = new ThreadLocal();static{try{//加载驱动类

    Class.forName("com.mysql.jdbc.Driver");

    }catch(ClassNotFoundException e) {

    e.printStackTrace();

    }

    }private static final Node2DataSource node2DataSource = newNode2DataSource();privateNode2DataSource() {};public staticNode2DataSource getInstance() {returnnode2DataSource;

    }/*** 获取连接*/@Overridepublic Connection getConnection() throwsSQLException {

    Connection conn= null;if(threadSession.get() == null) {

    conn=(Connection) DriverManager.getConnection("jdbc:mysql://192.168.0.109:3306/TEST","root","root");

    threadSession.set(conn);

    }else{

    conn=threadSession.get();

    }returnconn;

    }/*** 关闭并移除连接*/@Overridepublic void close() throwsSQLException {

    Connection conn=threadSession.get();if(conn != null) {

    conn.close();

    threadSession.remove();

    }

    }

    }

    4、创建Node1Dao类,在node1的数据库中更新账户信息

    packageperson.lb.dao;importjava.sql.Connection;importjava.sql.SQLException;importjava.sql.Statement;importperson.lb.datasource.Node1DataSource;public classNode1Dao {private Node1DataSource dataSource =Node1DataSource.getInstance();/*** 更新账户信息

    *@throwsSQLException*/

    public void updateAccountInfo() throwsSQLException {

    Connection conn=dataSource.getConnection();

    Statement st=conn.createStatement();

    st.execute("UPDATE ACCOUNTS SET BALANCE = CAST('9900.00' AS DECIMAL) WHERE CUSTOMER_NO = '88888888' ");

    }

    }

    5、创建Node2Dao,在node2机器上增加用户消费信息

    packageperson.lb.dao;importjava.sql.Connection;importjava.sql.SQLException;importjava.sql.Statement;importperson.lb.datasource.Node2DataSource;public classNode2Dao {private Node2DataSource dataSource =Node2DataSource.getInstance();/*** 增加用户消费信息

    *@throwsSQLException*/

    public void addUserPurchaseInfo() throwsSQLException {

    Connection conn=dataSource.getConnection();

    Statement st=conn.createStatement();

    st.execute("INSERT INTO USER_PURCHASE_HIS(CUSTOMER_NO, SERIAL_NO, AMOUNT, CURRENCY, REMARK) "

    + " VALUES ('88888888', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', null, 'CNY', '买衣服')");

    }

    }

    6、创建NodeService类,把两个操作作为一个事务来执行

    packageperson.lb.service;importjava.sql.SQLException;importperson.lb.dao.Node1Dao;importperson.lb.dao.Node2Dao;importperson.lb.transaction.TransactionManager;public classNodeService {public voidexecute() {//启动事务

    TransactionManager.begin();

    Node1Dao node1Dao= newNode1Dao();

    Node2Dao node2Dao= newNode2Dao();try{

    node1Dao.updateAccountInfo();

    node2Dao.addUserPurchaseInfo();//提交事务

    TransactionManager.commit();

    }catch(SQLException e) {

    e.printStackTrace();

    }

    }

    }

    7、最后是测试类TestTx

    packageperson.lb.test;importperson.lb.service.NodeService;public classTestTx {public static voidmain(String[] args) {

    NodeService nodeService= newNodeService();

    nodeService.execute();

    }

    }

    经测试,与第一个例子效果一致,但是从代码上来说要比第一个例子的可读性和可维护性高。不过这个例子并不能说明分布式事务中的事务管理器和资源管理器的真正原理,也不是一个可使用的代码,毕竟存在缺陷,而且dao层需要抛出异常才能实现事务的回滚。我想,作为一个理解分布式事务的作用的例子是够了。

    展开全文
  • 1、分布式事务相关概念分布式事务处理(Distributed Transaction Processing,DTP)是指一个事务可能涉及多个数据库操作,分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务...

    1、分布式事务相关概念

    分布式事务处理(Distributed Transaction Processing,DTP)是指一个事务可能涉及多个数据库操作,分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务的决定必须产生统一的结果(全部提交或全部回滚)。X/Open组织(即现在的Open Group)定义了分布式事务处理模型。X/Open DTP模型(1994)包括应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理器(CRM)四部分。一般,常见的事务管理器(TM)是交易中间件,常见的资源管理器(RM)是数据库,常见的通信资源管理器(CRM)是消息中间件。通常把一个数据库内部的事务处理,如对多个表的操作,作为本地事务看待。数据库的事务处理对象是本地事务,而分布式事务处理的对象是全局事务。

    所谓全局事务,是指分布式事务处理环境中,多个数据库可能需要共同完成一个工作,这个工作即是一个全局事务,例如,一个事务中可能更新几个不同的数据库。对

    数据库的操作发生在系统的各处但必须全部被提交或回滚。此时一个数据库对自己内部所做操作的提交不仅依赖本身操作是否成功,还要依赖与全局事务相关的其它

    数据库的操作是否成功,如果任一数据库的任一操作失败,则参与此事务的所有数据库所做的所有操作都必须回滚。

    一般情况下,某一数据库无法知道其它数据库在做什么,因此,在一个DTP环境中,交易中间件是必需的,由它通知和协调相关数据库的提交或回滚。而一个数据库只将其自己所做的操作(可恢复)影射到全局事务中。

    XA就是X/Open DTP定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。XA接口函数由数据库厂商提供。

    XA与两阶段提交协议

    通常情况下,交易中间件与数据库通过XA接口规范,使用两阶段提交来完成一个全局事务,XA规范的基础是两阶段提交协议。在第一阶段,交易中间件请求所有相关数据库准备提交(预提交)各自的事务分支,以确认是否所有相关数据库都可以提交各自的事务分支。当某一数据库收到预

    提交后,如果可以提交属于自己的事务分支,则将自己在该事务分支中所做的操作固定记录下来,并给交易中间件一个同意提交的应答,此时数据库将不能再在该事

    务分支中加入任何操作,但此时数据库并没有真正提交该事务,数据库对共享资源的操作还未释放(处于上锁状态)。如果由于某种原因数据库无法提交属于自己的

    事务分支,它将回滚自己的所有操作,释放对共享资源上的锁,并返回给交易中间件失败应答。在第二阶段,交易中间件审查所有数据库返回的预提交结果,如所有数据库都可以提交,交易中间件将要求所有数据库做正式提交,这样该全局事务被提交。而如果有任一数据库预提交返回失败,交易中间件将要求所有其它数据库回滚其操作,这样该全局事务被回滚。以一个全局事务为例,AP首先通知交易中间件开始一个全局事务,交易中间件通过XA接口函数通知数据库开始事务,然后AP可以对数据库管理的资源进行操作,数据库系统记录事务对本地资源的所有操作。操作完成后交易中间件通过XA接口函数通知数据库操作完成。交易中间件负责记录AP操作过哪些数据库(事务分支)。AP根据情况通知交易中间件提交该全局事务,交易中间件会通过XA接口函数要求各个数据库做预提交,所有数据库返回成功后要求各个数据库做正式提交,此时一笔全局事务结束。

    XA规范对应用来说,最大好处在于事务的完整性由交易中间件和数据库通过XA接口控制,AP只需要关注与数据库的应用逻辑的处理,而无需过多关心事务的完整性,应用设计开发会简化很多。

    具体来说,如果没有交易中间件,应用系统需要在程序内部直接通知数据库开始、结束和提交事务,当出现异常情况时必须由专门的程序对数据库进行反向操作才能完成回滚。如果是有很多事务分支的全局事务,回滚时情况将变得异常复杂。而使用XA接口,则全局事务的提交是由交易中间件控制,应用程序只需通知交易中间件提交或回滚事务,就可以控制整个事务(可能涉及多个异地的数据库)的全部提交或回滚,应用程序完全不用考虑冲正逻辑。在一个涉及多个数据库的全局事务中,为保证全局事务的完整性,由交易中间件控制数据库做两阶段提交是必要的。但典型的两阶段提交,对数据库来说事务从开

    始到结束(提交或回滚)时间相对较长,在事务处理期间数据库使用的资源(如逻辑日志、各种锁),直到事务结束时才会释放。因此,使用典型的两阶段提交相对

    来说会占用更多的资源,在网络条件不是很好,如低速网、网络颠簸频繁,情况会更为严重。当一个全局事务只涉及一个数据库时,有一种优化方式,即一阶段提交。当AP通知交易中间件提交事务时,交易中间件直接要求数据库提交事务,省去两阶段提交中的第一阶段,可以缩短处理一个事务的时间,以提高事务处理的效率。作为两阶段提交的一种特例,与两阶段一样,一阶段提交也是标准的。

    XA的要求

    对于jms来说,需要配置支持XA的 connection factory.

    对于db来说,需使用支持XA的JDBC driver.

    两阶段提交(2 phase commit)

    先说transaction的管理,如下图。

    cd3bb76b76e5c34b88287d8f6928247e.png

    transaction manager与不同的resource manager都有交互。

    在phase1时,transaction manager向两个resouce询问是否可以准备提交。Resouce可回复ready,

    not_ready或是read_only. 当两个都ready时,则可以进入phase2

    进行提交。任何一个回复not_ready则整个transaction 回滚。回复read_only的资源在phase2阶段被排除在提交过程之外。

    1640e40c8bd11f0baf25ac0347adbaa2.png

    2.JTA

    JTA(Java Transaction API) 为 J2EE 平台提供了分布式事务服务。

    要用 JTA 进行事务界定,应用程序要调用 javax.transaction.UserTransaction 接口中的方法。

    让我们来关注下面的话:

    “用 JTA 界定事务,那么就需要有一个实现 javax.sql.XADataSource 、

    javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC

    驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection

    对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。”

    要使用JTA事务,必须使用XADataSource来产生数据库连接,产生的连接为一个XA连接。

    XA连接(javax.sql.XAConnection)和非XA(java.sql.Connection)连接的区别在于:XA可以参与JTA的事务,而且不支持自动提交。

    Note:

    Oracle, Sybase, DB2, SQL Server,MySQL(5.0以后InnoDB存储引擎)等大型数据库才支持XA, 支持分布事务。

    3.OFBiz中实现的事务OFBiz采用Apache Commons DBCP作为数据库连接池技术,分布式事务是通过Apache Geronimo Transaction实现的。下面是部分实现类关系:

    8795ddf2b0f0b6cc94521755921955c4.png

    TransactionFactory和ConnectionFactory都可以自己实现并且在entityengine.xml中进行配置,OFBiz中默认配置的是

    展开全文
  • 如果想让JTA管理多台数据库操作的分布式事务,需要XA支持,Open Group设计的X / Open分布式事务处理定义了一种标准通信架构。允许多个应用程序共享多个资源管理器提供的资源,并允许其工作协调到全局事务中...
  • 在上一篇《java事务(二)——本地事务》中已经提到了事务的类型...但是在本篇中并不会说明如何使用JTA,而是在不依赖其他框架以及jar包的情况下自己来实现分布式事务,作为对分布式事务的一个理解。 假设现在有...
  • SpringBoot2.x系列教程68--Spring Boot+JPA+Jta-atomikos实现分布式事务作者:一一哥我在上一章节中介绍了分布式事务的相关理论知识,本章节中我会在SpringBoot中结合JPA,整合jta-atomikos来实现多数据源环境下的...
  • tomcat里面的a接口向mysql插入数据,weblogic里面的b接口向oracle删除数据,jetty里面的c接口向db2更新数据. ...现在a接口里面添加了2行代码,即调用b接口和c接口,当c接口更新失败的时候如何实现a接口与b接口的事务性回滚?
  • 参考:Java实现转账业务
  • 最近小伙伴们的要求越来越高,学完设计模式学高并发,学完高并发又想学Java8新特性,学完Java8新特性又要学Spring,学着Spring又让我整理一篇关于分布式事务的文章,而且还提出了要求:要实战型的!那好吧,安排上!...
  • Spring Cloud基于TCC补偿模式实现分布式事物Try Confirm Cancel补偿模式本实例遵循的是Atomikos公司对微服务的分布式事务所提出的RESTful TCC解决方案RESTful TCC模式分3个阶段执行Trying阶段主要针对业务系统检测及...
  •  JTA是J2EE的规范之一,如果使用JTA,我们需要去实现相应接口。...本文介绍如何使用tomcat+JTA实现多数据源的分布式事务。  一 选型   tomcat需要使用插件实现JTA,常用插件有jotm和atomikos,本文以atomi...
  • java事务和分布式事务详解

    千次阅读 2018-11-06 23:18:14
    面试经常会问到分布式锁、分布式事务、SOA 服务化、分布式系统等业务、架构的问题和解决方案,工作中接触的业务方面事关金融,也需要解决一些类似的业务问题,所以总结了一篇浅谈分享,后面实战篇正在准备,这几周会...
  • Java规范对分布式事务定义了标准的规范Java事务API和Java事务服务,分别是JTA和JTS一个分布式事务必须包括一个事务管理器和多个资源管理器。 资源管理器是任意类型的持久化数据存储; 而事务管理器则是承担着所有...
  • 首先解释一个概念:本地事务和分布式事务。 本地事务:只处理单一数据源,比如单个数据库下,事务进行控制。... 在Java中,分布式事务主要的规范是JTA/XA。其中:JTA是Java的事务管理器规范, XA是工业标准的X/Open C
  • 我在上一章节中介绍了分布式事务的相关理论知识,本章节中我会在SpringBoot中结合JPA,整合jta-atomikos来实现多数据源环境下的分布式事务。 一. 多数据源环境下分布式事务代码实现 代码环境: Spring Boot2.2.5 ...
  • Java小马哥 2017-10-22 22:20 概述 分布式系统(distributedsystem)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。因此,网络和分布式系统之间的区别更多的在于高层...
  • 简述分布式事务指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。解决方案在分布式系统中,要实现分布式事务,...
  • Java分布式事务概念与实现示例

    千次阅读 2005-05-26 15:13:00
    java中有如下三种事务,简单的JDBC级的事务 JTA - 在EJB...下面讨论如何在Java程序里实现分布式事务,即在同一个事务里访问多个数据源。实际上就是如何使用JTA. 这里假设使用Oracle数据库,使用WebLogic部署应用,
  • 当数据库的数据比较大的时候达到成千上万的数据的时候,我们就需要对数据库进行分表分库处理来实现对服务器的压力,这时候如何保证数据的一致性,就需要引入分布式事务; 2.2 应用SOA化 所谓的SOA化就是把服务器...
  • 分布式事务的几种实现思路 总结 什么是事务? 首先抛出来一个问题:什么是事务?有人会说事务就是一系列操作,要么同时成功,要么同时失败;然后会从事务的 ACID 特性(原子性、一致性、隔离性、持久性)展开叙述。 ...
  • java实现简单的分布式事务

    千次阅读 2015-05-31 23:52:58
    package transactionTest;...import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.logging.Level; import java.util.logging.Logger; import javax.sql.XAConn
  • 目录CAP原则Rocket mq实现思路Rabbit mq实现思路需要考虑的问题后记严格的来说,消息中间件并不能实现分布式事务,而是通过事后补偿机制,达到和分布式事务一样的数据一致性。这里主要探讨Rocket mq 和 Rabbit mq的...
  • java微服务 之 分布式事务 seata1 分布式事务介绍1.1 什么是事务1.2 本地事务1.3 什么是分布式事务1.4 分布式事务应用架构1.4.1 单一服务分布式事务1.4.2 多服务分布式事务1.4.3 多服务多数据源分布式事务1.5 CAP...
  • 很多人都说php实现不了分布式事务,java才能实现。事实其实并非如此。事务和数据库有关,和php或者java...下面看看php如何实现分布式事务。mysql中的innodb引擎支持xa事务。可通过以下命令查看innodb是否开启xa事...
  • 从0到1实现分布式事务 我是lorne,7年软件开发经验,熟悉java语言,...
  • 使用JOTM实现分布式事务的例子 发布时间:2009/4/18 11:55:07 | 32 人感兴趣 | 0 人参与 | 评分:3 import java.sql.Connection; import javax.transaction.TransactionManager; import javax.transaction....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,070
精华内容 428
关键字:

java实现分布式事务

java 订阅