Spring声明事务和分布式事务处理技术

Spring声明事务的两种方式

方式一、传统的编程式事务管理:

需要手动编写代码在业务层注入事务管理模板(一般不用)

方式二、基于 AOP 技术实现的声明式事务管理:

Spring 声明式事务管理在底层采用了 AOP 技术,其最大的优点在于无须通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。

  • 基于 XML 方式的声明式事务管理。
  • 通过 @Transactional 注解方式的事务管理。

@Transactional潜在的问题

只要需要事务就需要占用一个数据库连接,如果在需要开启事务的方法里进行一些IO操作、网络通讯等需要长时间处理的操作,这个数据库连接就一直被占用着,直到方法执行结束后自动提交事务或执行过程中发生异常回滚事务,这个数据库连接才会被释放掉。这个过程中还有一个很可怕的问题,如果在需要开启事务的方法里进行了网络通讯操作,而这个操作没有设置网络超时时间,那这个数据库连接就会被一直占用着。

上述问题,在流量很大的情况下简直就是灾难,会直接导致应用系统挂掉。

正确使用@Transactional注解

1) 不要在类上标注Transactional注解,要在需要的方法上标注。即使类的每个方法都需要事务也不要在类上标注,因为有可能你或别人新添加的方法根本不需要事务。

2) 标注了Transactional注解的方法体中不要涉及耗时很久的操作,如IO操作、网络通信等。

3) 根据业务需要设置合适的事务参数,如是否需要新事务、超时时间等。

Spring事务异常处理机制

RuntimeException()和Exception()区别:

1.继承自RuntimeException或error的是非检查型异常,而继承自exception的则是检查型异常(当然,runtimeexception本身也是exception的子类)。
2.对非检查型类异常可以不用捕获,而检查型异常则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它。所以必须在service捕获异常,然后再次抛出,这样事务方才起效。

Spring事务默认只在发生未被捕获的RuntimeException()时才进行回滚。

Spring通过SpringAOP进行声明式事务管理:

SpringAOP异常捕获的原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下SpringAOP只捕获RuntimeException的异常,因此不是RuntimeException或其子类的异常不能够捕获,默认情况下不进行回滚,但可以通过配置来捕获特定的异常并回滚 。

因此:

方法1:service层不使用try......catch或者在catch中最后加上throw new RuntimeException(),这样程序异常时aop才可以捕获异常并进行回滚。

最终在service上层(如controller层、action层、view层)要继续捕获这个异常并处理。

方法2:在service层方法上进行自定义配置,改变默认规则,方法如下 :

(1)在整个方法前加上这个注解 @Transactional(rollbackFor=Exception.class),那么这个类里面的方法抛出异常throw new Exception(),也会回滚
 
(2)不需要回滚的事务: @Transactional(notRollbackFor=RunTimeException.class)
 
(3)不需要事务管理的(只读)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
 
(4)不需要事务管理的(同上)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务,可以提高效率。
 

 Java 中的分布式事务处理技术

一、什么是分布式事务?

分布式事务是指针对分布式系统中多个节点的事务进行协调和管理的过程。在分布式系统中,每个节点都有自己的事务处理,但这些事务可能涉及多个节点,如果不进行协调管理,就容易导致数据不一致的情况。因此,在分布式系统中,正确处理分布式事务是至关重要的。

二、分布式事务的实现方式

方式1、两阶段提交(Two-phase commit,简称2PC)

2PC是一种常见的分布式事务处理方式。它通过协调器来协调所有事务参与者的行动,并确保在所有参与者都同意提交事务之前,不会真正提交事务。

1)第一阶段:准备阶段(prepare)
协调者通知参与者准备提交,各参与者反馈事务执行结果,但参与者先不提交事务。

2)第二阶段:提交(commit)/回滚(rollback)阶段
协调者通知参与者开始提交,各参与者反馈事务提交结果。只要在这两个阶段中执行结果和提交结果有失败回复,整个事务回滚。

方式2、三阶段提交(Three-phase commit,简称3PC)

3PC在2PC的基础上进行了改进。它引入了一个预提交阶段,以便更好地处理故障和恢复。当所有的参与者都准备好提交事务时,先将事务提交请求发给协调器,协调器再向所有的目标参与者发送预提交请求,询问它们是否准备好提交事务。只有在所有参与者都回答“是”时,协调器才会发送提交请求。如果有任何一个参与者回答“否”或超时,则协调器会发送一个回滚请求,要求事务回滚。

三阶段提交引入两个机制

1、 引入超时机制,同时在协调者和参与者中都引入超时机制。
2、在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

主要解决的问题:

避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。

优点:实现强一致性,部分关系数据库支持(Oracle、MySQL等)。

缺点:整个事务的执行需要由协调者在多个节点之间去协调,增加了事务的执行时间,性能较差,会存在长时间的锁表。

方式3、事务补偿 TCC

Tcc-transaction是一款开源的微服务架构下的TCC型分布式事务解决方案,致力于提高性能和简单易用的分布式事务服务。

try: 尝试执行业务,完成所有业务检查

confirm:提交事务,不做任何业务检查,只使用try阶段预留的业务资源,满足幂等性

cancel:取消事务,释放try阶段预留的业务资源,满足幂等性

如何解决上面退分账中分布式事务问题呢? 选择使用tcc-transaction框架,执行流程如下:

Try:
商家1收益户->冻结分账金额
商家2收益户->冻结分账金额
商家3收益户->冻结分账金额
平台手续费->冻结手续费

Try成功 => Confirm:
商家1收益户->扣除分账金额
商家2收益户->扣除分账金额
商家3收益户->扣除分账金额
平台手续费->扣除手续费
平台收益户-> 增加金额(总分账金额+手续费)

Try失败 => Cancel:
商家1收益户->解冻分账金额
商家2收益户->解冻分账金额
商家3收益户->解冻分账金额
平台手续费->解冻手续费

优点:最终保证数据的一致性,在业务层实现事务控制,灵活性好。

缺点:开发成本高,代码量大,可维护性差。每个事务操作每个参与者都需要实现try/confirm/cancel三个接口。

TCC 与Saga其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。确认和补偿都有采用幂等性设计。

方式4、消息队列实现最终一致性

消息事务其实就是基于消息中间件的两阶段提交,将本地事务和发消息放在同一个事务里,保证本地操作和发送消息同时成功。

本方案是将分布式事务拆分成多个本地事务来完成,并且由消息队列异步协调完成,如下图:

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

三、不建议大量去使用分布式事务中间件

优点:

  • 将分布式事务进行抽象,独立部署的与业务无关的事务组件进行事务控制

缺点:

  • 引入分布式事务中间件会造成系统大规模耦合
  • TC基础设置对外暴露,既不合规,也不安全
  • 用不好(AT、TCC、Saga、XA)就是超低的执行效率,同时也提高了工程师的应用门槛
  • 很多场景都是在某个业务中调用第三方服务,不可能强行让第三方服务配合引入分布式事务中间件

分布式事务本身就是一个技术难题,业务中具体使用哪种方案还是需要根据不同的业务特点自行选择。

分布式事务会大大的提高流程的复杂度,会带来很多额外的开销工作,能不使用分布式事务就不使用。

四、如果一定要用到分布式事务中间件

AT 模式
AT 模式是 Seata 创新的一种非侵入式的分布式事务解决方案,Seata 在内部做了对数据库操作的代理层,我们使用 Seata AT 模式时,实际上用的是 Seata 自带的数据源代理 DataSourceProxy,Seata 在这层代理中加入了很多逻辑,比如插入回滚 undo log 日志,检查全局锁等。
优点:简单易用,不需要改动业务代码,自动完成分布式事务的提交和回滚。
缺点:不适合跨多种存储资源的事务,且在高并发场景下性能可能成问题。
适用场景:主要用于CRUD(创建、读取、更新、删除)操作较多的业务场景,尤其是当业务逻辑直接操作数据库,并且可以容忍短暂的数据不一致时。AT模式通过记录数据的前后镜像来实现撤销(回滚)操作,适合于大部分单纯依赖于单个数据库事务的微服务场景。

TCC 模式
TCC 模式是 Seata 支持的一种由业务方细粒度控制的侵入式分布式事务解决方案,是继 AT 模式后第二种支持的事务模式,最早由蚂蚁金服贡献。其分布式事务模型直接作用于服务层,不依赖底层数据库,可以灵活选择业务资源的锁定粒度,减少资源锁持有时间,可扩展性好,可以说是为独立部署的 SOA 服务而设计的。
优点:TCC 完全不依赖底层数据库,能够实现跨数据库、跨应用资源管理,可以提供给业务方更细粒度的控制。
缺点:TCC是一种侵入式的分布式事务解决方案,需要业务系统自行实现 Try,Confirm,Cancel 三个操作,对业务系统有着非常大的入侵性,设计相对复杂。
适用场景:TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景。

SAGA模式
适用场景:适用于长事务场景,其中业务流程包含一系列的本地事务,这些本地事务需要按照一定的顺序执行。SAGA模式通过定义一系列的事务步骤和相对应的补偿操作(回滚操作)来管理事务,适合于微服务架构下的复杂业务流程。
优点:适合长事务处理,可以保证分布式事务的最终一致性。
缺点:需要定义每个步骤的补偿操作,对业务侵入性较高。

XA 模式
XA 模式是从 1.2 版本支持的事务模式。XA 规范 是 X/0pen 组织定义的分布式事务处理(DTP,DistributedTransaction Processing)标准。Seata XA 模式是利用事务资源(数据库、消息服务等)对 XA 协议的支持以 XA 协议的机制来管理分支事务的一种事务模式。
优点:可以提供跨多种资源的强一致性事务保证,与 Seata 支持的其它事务模式不同,XA 协议要求事务资源本身提供对规范和协议的支持,所以事务资源如数据库)可以保障从任意视角对数据的访问有效隔离,满足全局数据一致性。此外的一些优点还包括:
业务无侵入:和 AT 一样,XA 模式将是业务无侵入的,不给应用设计和开发带来额外负担。
数据库的支持广泛:XA 协议被主流关系型数据库广泛支持,不需要额外的适配即可使用。
缺点:XA prepare 后,分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞等待。事务资源长时间得不到释放,锁定周期长,而且在应用层上面无法干预,性能差。
适用场景:适用于想要迁移到 Seata 平台基于 XA 协议的老应用,使用 XA 模式将更平滑,还有 AT 模式未适配的数据库应用。且适用于需要强一致性保证的分布式事务,且参与事务的资源管理器(如数据库)支持XA协议。XA模式基于两阶段提交(2PC)协议,适合于跨多种数据库或消息队列等资源的分布式事务。

基础理论知识

相关名词:

XA :XA规范的目的是允许多个资源(如数据库,应用服务器,消息队列,等等)在同一事务中访问,这样可以使ACID属性跨越应用程序而保持有效。XA使用两阶段提交来保证所有资源同时提交或回滚任何特定的事务。

JTA: Java事务API(Java Transaction API,简称JTA ) 是一个Java企业版 的应用程序接口,在Java环境中,允许完成跨越多个XA资源的分布式事务。

CAP理论

一致性(Consistency) :在分布式系统中所有的数据备份,在同一时刻都保持一致状态,如无法保证状态一致,直接返回错误;
可用性(Availability):在集群中一部分节点故障,也能保证客户端访问系统并得到正确响应,允许一定时间内数据状态不一致;
分区容错性(Partition tolerance):分布式系统在遇到任何网络分区故障时,仍然能保证对外提供满足一致性和可用性的服务,除非整个网络环境都发生故障;

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

本地事务四大特性(ACID)

事务应该是具备原子性一致性隔离性和持久性,简称 ACID。
原子性(Atomicity) ,     可以理解为一个事务内的所有操作要么都执行,要么都不执行。
一致性(Consistency),可以理解为数据是满足完整性约束的,也就是不会存在中间状态的数据,事务前后数据的完整性必须保持一致。。
隔离性(Isolation) ,     指的是多个事务并发执行的时候不会互相干扰,即一个事务内部的数据对于其他事务来说是隔离的。
持久性(Durability) ,   指的是一个事务完成了之后数据就被永远保存下来,之后的其他操作或故障都不会对事务的结果产生影响。

BASE理论

基本可用(Basically Available):分布式系统在出现故障时,保证核心可用,允许损失部分可用性。(响应时间上的损失、功能上的损失)
软状态(Soft State):系统中的数据允许存在中间状态,中间状态不影响系统的整体可用性。(支付中、处理中等)
最终一致性(Eventually Consistent):系统中的数据不可一直处于软状态,必须在有时间期限,在期限过后应当保证数据的一致性。(支付中变为支付成功)

相比于本地事务的ADIC强一致性模型,BASE理论提出通过牺牲一定的强一致性来获得可用性;
不同业务单元和业务组件对数据一致性的要求不一样,因此分布式系统中BASE理论和ACID特性会结合使用。

幂等性设计

幂等(Idempotent)是一个数学与计算机学中的概念。f(n) = 1^n , 无论n等于多少,f(n)永远值等于1;
在程序中,使用相同参数执行同一个方法,每一次执行结果都是相同的,即具有幂等性;
以订单状态处理为例的幂等性设计,不论执行多少次orderProcess()方法,都只会扣减一次库存,并且返回true。

 

查看更多关于Spring如何支持分布式事务

posted @ 2021-05-14 10:55  zhαojh  阅读(703)  评论(0编辑  收藏  举报