本地事务和分布式事务

本地事务

1、事务的基本性质

数据库事务的几个特性:原子性(Atomicity)一致性(Consistency)隔离性或独立性(isolation)持久性(Durability),简称就是 ACID

  • 原子性:一系列的操作整体不可拆分,要么同时成功,要么同时失败。
  • 一致性:数据在事务的前后,业务整体一致。
    转账:A:1000; B:1000; 转 200 事务成功; A:800; B:1200
  • 隔离性:事务之间互相隔离
  • 持久性:一旦事务成功,数据一定会落盘在数据库。

比如买东西业务,扣库存,下订单,账户扣款,是一个整体,必须同时成功或失败。本地事务的适用非常简单,使用spring家在方法上加上@Transactional 注解就可以了。

2、事务的隔离级别

(1)read_uncommitted(读未提交)

可读取未提交事务的操作数据,最低的隔离级别,一般都没有用的。这种情况会出现脏读。

(2)read_committed(读已提交)

一个事务等另一个事务提交之后才可进行读取,解决了脏读问题,但会出现不可重复读

(3)repeatable_read(可重复读)

读取事务开启的时候不能对数据进行修改,解决了不可重复读问题,但是存在幻读问题;

(4)serializable(序列化):是最高的事务隔离级别,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用;

MySQL默认第三隔离级别。
Oracle默认第二隔离级别。
在代码中我们可以指定事务的隔离级别:

@Transactional(isolation=Isolation.READ_COMMITED)

3、事务的传播行为

  • 1、支持当前事务:
    TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
    TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。

  • 2、不支持当前事务:
    TransactionDefinition.PROPAGATION_REQUIRED_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
    TransactionDefinition.PROPAGETION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
    TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

  • 3、其他
    TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

示例:

@Transactional  // a事务的所有设置就传播到了和他共用一个事务的方法。
public void a() {

// b,c是否和a共用一个事务?
b();   // b需要一个事务,这时b和a共用一个事务,即在a事务里边
c();   // c是一个新事务

int i = 10 / 0;   // 哪个会回滚呢?
}

@Transactional(propagation=Propagation.REQUIRED)
public void b() {
}

@Transactional(propagation=Propagation.REQUIRED_NEW)
public void c() {
}

如果上边的代码 int i = 10 / 0;执行,哪个会回滚呢? 由于b和a是在同一个事务里边,所以,两个都会回滚,而c是一个新事物,所以,不会回滚。

我们还可以给事务添加超时时间 @Transactional(timeout =20),如果共用事务,则子事务的设置就不起作用了,会用父类的事务。

4、SpringBoot事务关键点

4.1、事务的自动配置

TransactionAutoConfiguration

4.2、事务的坑
在同一个类里面,编写两个方法,内部调用的时候,会导致事务设置失效。原因是没有用到代理对象的缘故。

在这里插入图片描述

事务使用代理对象来控制的,上边的b,c和a是相同的对象,同一个对象内事务方法互调默认失败,原因是绕过了代理对象,
解决:
1)、导入spring-boot-starter-aop,引入了 aspectj
2)、@EnableTransactionManagement(proxyTargetClass=true)
3)、@EnableAspectJAutoProxy(exposeProxy=true);开启 aspectj 动态代理功能。以后所有的动态代理都是 aspectj 创建(即使没有接口也可以创建动态代理),对外暴露代理对象。
4)、本类互调用调用对象(OrderServiceImpl obj = AopContext.currentProxy())
如果有本类互调的可以使用上边的解决方案。
在这里插入图片描述

小结@Transactional 是本地事务,在分布式系统,只能控制住自己的回滚,控制不了其它服务的回滚,所以,是有局限性的,如果方法里边需要调用其它服务的操作,则要使用分布式事务。

分布式事务

一、为什么会有分布式事务

分布式系统经常出现的异常,如机器宕机、网络异常、消息丢失、数据错误、不可靠的TCP、存储数据丢失等等。

二、分布式事务

分布式事务是指事务的参与者,支持事务的服务器,资源服务器分别位于分布式系统的不同节点之上,通常一个分布式事物中会涉及到对多个数据源或业务系统的操作。

典型的分布式事务场景:跨银行转操作就涉及调用两个异地银行服务

三、分布式理论

1、CAP理论

CAP理论:一个分布式系统不可能同时满足一致性,可用性和分区容错性这个三个基本需求,最多只能同时满足其中两项
一致性(Consistency):数据在多个副本之间是否能够保持一致的特性。
可用性(Avaliability):是指系统提供的服务必须一致处于可用状态,对于每一个用户的请求总是在有限的时间内返回结果,超过时间就认为系统是不可用的
分区容错性(Partition tolerance):分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非整个网络环境都发生故障。

CAP定理的应用
放弃P(CA):如果希望能够避免系统出现分区容错性问题,一种较为简单的做法就是将所有的数据(或者是与事物先相关的数据)都放在一个分布式节点上(属于本地都放一个系统),这样虽然无法保证100%系统不会出错,但至少不会碰到由于网络分区带来的负面影响
放弃A(CP):其做法是一旦系统遇到网络分区或其他故障时,那受到影响的服务需要等待一定的时间,应用等待期间系统无法对外提供正常的服务,即不可用
放弃C(AP):这里说的放弃一致性,并不是完全不需要数据一致性,是指放弃数据的强一致性,保留数据的最终一致性。

分布式系统中实现一致性的 raft 算法:
任何一个节点都有三种状态:随从,候选者,领导

CP面临的问题:
对于多数大型互联网应用的场景,主机众多,部署分散,而且现在集群的规模越来越大,所以,节点故障、网络故障是常态,而且要保证服务可用性达到 99.999999%(N个9),即保证P和A,舍弃C。

2、BASE理论

BASE是基本可用软状态最终一致性。是对CAP中一致性和可用性权限的结果,是基于CAP定理演化而来的,核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特定,采用适当的方式来使系统达到最终一致性

强一致性、弱一致性、最终一致性:
对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这就是强一致性。
如果能容忍后续的部分或者全部访问不到,则是弱一致性,如果经过一段时间能访问到更新后的数据,则是最终一致性。

三、分布式事务几种方案

1、2PC提交(刚性事务)
二阶段提交协议是将事务的提交过程分成提交事务请求和执行事务提交两个阶段进行处理。

2、3PC提交(刚性事务)
三阶段提,也叫三阶段提交协议,是二阶段提交(2PC)的改进版本。
与两阶段提交不同的是,三阶段提交有两个改动点。引入超时机制。同时在协调者和参与者中都引入超时机制。在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。
3、柔性事务-TCC事务补偿型方案
刚性事务:遵循 ACID 原则,强一致性。
柔性事务:遵循 BASE 理论,最终一致性。
与刚性事务不同,柔性事务允许一定时间内,不同节点的数据不一致,但要求最终一致。

TCC是服务化的两阶段编程模型,其Try、Confirm、Cancel,3个方法均由业务编码实现
TCC要求每个分支事务实现三个操作:预处理Try,确认Confirm,撤销Cancel。
Try操作做业务检查及资源预留,
Confirm做业务确认操作
Cancel实现一个与Try相反的操作即回滚操作。
TM首先发起所有的分支事务Try操作,任何一个分支事务的Try操作执行失败,TM将会发起所有分支事务的Cancel操作,若Try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试。

4、柔性事务-最大努力通知型方案
最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是消息可能接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务,通知可靠性关键在于接收通知方

5、柔性事务-可靠消息+最终一致性方案(异步确保型)
实现:业务处理服务在业务事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不是真正的发送。业务处理服务在业务事务提交之后,向实时消息服务确认发送,只有在得到确认发送指令后,实时消息服务才会真正发送。

<think>好的,我现在需要回答用户关于Spring框架中本地事务分布式事务的区别及实现方法的问题。首先,我得理清这两个概念的基本定义它们在Spring中的处理方式。 本地事务通常涉及单一数据库,遵循ACID特性,而分布式事务则跨多个资源,比如多个数据库或微服务。用户可能想知道它们的主要区别,比如作用范围、一致性的保证方式,以及实现时的不同机制。 接下来,我需要回顾Spring中的事务管理机制。记得Spring通过PlatformTransactionManager接口抽象事务管理,对于本地事务,可能是使用DataSourceTransactionManager,而分布式事务可能需要JtaTransactionManager。这里要确保引用的资料是否正确,比如引用[4]提到Spring委托事务管理给Hibernate或JTA,所以需要正确引用。 然后,分布式事务的实现方式部分,用户可能对XA模式、AT模式、TCC等感兴趣。根据用户提供的引用,比如引用[3]解释了XA模式的两个阶段,引用[5]提到Seata支持多种模式,需要将这些内容整合进去,说明不同模式的实现原理适用场景。 在比较部分,需要明确本地分布式事务在资源数量、一致性、性能等方面的差异。比如本地事务性能更好,而分布式事务因为协调开销性能较差。同时,需要提到不同的解决方案,如两阶段提交、Seata等,结合引用[2]中的内容,说明选择依据。 最后,生成相关问题时要确保覆盖用户可能进一步询问的方向,比如如何选择事务模式、XA与AT的区别,以及Spring Cloud集成Seata的步骤等。检查引用是否恰当,比如引用[1][5]都涉及Seata,需要正确关联。 现在需要确保回答结构清晰,分点说明,并且正确应用LaTeX格式。比如,提到ACID时用行内公式$ACID$,独立公式用$$...$$,但可能这里不需要太多数学公式,主要注意术语的格式。同时,代码块部分如果有的话要正确使用语法,但用户的问题可能不需要代码示例,所以可能不需要添加。 最后检查引用标识是否与内容对应,比如引用[3]对应XA模式的阶段,引用[5]对应Seata的不同模式。确保每个引用都自然衔接,没有错误。回答完成后,生成的相关问题要围绕主题,帮助用户深入理解。</think>### Spring框架中本地事务分布式事务的区别及实现方法 #### 一、本地事务分布式事务的核心区别 1. **作用范围** - **本地事务**:基于单一数据库实例,确保事务的$ACID$特性(原子性、一致性、隔离性、持久性),仅涉及单个资源[^4]。 - **分布式事务**:跨多个数据库或微服务,需要协调多个资源(如多个数据库、消息队列等)的一致性[^1][^2]。 2. **一致性实现方式** - **本地事务**:直接依赖数据库的事务管理机制(如MySQL的InnoDB引擎)。 - **分布式事务**:需引入分布式事务协议(如两阶段提交、三阶段提交)或框架(如Seata)来协调多节点操作[^3][^5]。 3. **性能与复杂度** - 本地事务性能高、实现简单;分布式事务因需跨网络协调,存在延迟复杂度高的问题。 --- #### 二、本地事务的实现方法(Spring框架支持) 1. **声明式事务管理** 通过`@Transactional`注解实现,底层依赖`PlatformTransactionManager`接口。 ```java @Service public class UserService { @Transactional public void updateUser(User user) { // 数据库操作 } } ``` - **核心组件**: - `DataSourceTransactionManager`:用于JDBC或MyBatis等基于单一数据源的事务管理[^4]。 2. **编程式事务管理** 手动控制事务边界,适用于复杂逻辑场景。 ```java @Autowired private PlatformTransactionManager transactionManager; public void executeWithTransaction() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { // 业务逻辑 transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); } } ``` --- #### 三、分布式事务的实现方法 1. **基于XA协议的两阶段提交(2PC)** - **原理**: - **一阶段(准备)**:所有参与者执行操作但不提交,返回就绪状态。 - **二阶段(提交/回滚)**:协调者根据参与者状态决定全局提交或回滚。 - **Spring集成**:通过`JtaTransactionManager`支持(如Atomikos、Narayana)。 2. **Seata框架(AT/TCC/Saga模式)** - **AT模式(自动补偿)**: 通过全局锁机制实现数据快照,自动生成反向SQL补偿操作[^5]。 ```java @GlobalTransactional public void distributedOperation() { serviceA.update(); serviceB.update(); } ``` - **TCC模式(手动补偿)**: 需开发者实现`Try`、`Confirm`、`Cancel`接口,适用于高一致性要求的场景。 3. **消息最终一致性(异步补偿)** - 通过消息中间件(如RocketMQ)实现异步通知,确保最终一致性。 --- #### 四、对比与选型建议 | **维度** | **本地事务** | **分布式事务** | |------------------|----------------------------|---------------------------| | 适用场景 | 单数据库操作 | 跨服务、跨数据库操作 | | 性能 | 高(无网络协调) | 低(需多节点通信) | | 一致性强度 | 强一致性 | 最终一致性或弱一致性[^2] | | 实现复杂度 | 低 | 高(需引入框架或协议) | **选型建议**: - 单数据库场景优先使用本地事务。 - 分布式场景中,高一致性需求选XA或TCC,性能敏感场景选AT或消息队列[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值