spring事务

什么是事务?

举个生活中的例子:

你去小卖铺买东西,“一手交钱,一手交货”就是一个事务的例子,交钱和交货必须全部成功,事务才算成功,任一个活动失败,事务将撤销所有已成功的活动。

明白上述例子,再来看事务的定义:

事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。

一个数据库事务是一个被视为一个工作单元的逻辑上的一组操作,这些操作要么全部执行,要么全部不执行

事务可以分为本地事务和分布式事务两种类型。这两种事务类型是根据访问并更新的数据资源的多少来进行区分的。本地事务是在单个数据源上进行数据的访问和更新,而分布式事务是跨越多个数据源来进行数据的访问和更新。在这里要说的事务是基于数据库这种数据源的。

1、事务的四个特性

ACID

事务具有 4 个特性:原子性、一致性、隔离性和持久性,简称为 ACID 特性。

原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的动作要么都做要么都不做。

一致性(Consistency):事务必须保证数据库从一个一致性状态变到另一个一致性状态,一致性和原子性是密切相关的。

隔离性(Isolation):一个事务的执行不能被其它事务干扰,即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能互相打扰。

持久性(Durability):持久性也称为永久性,指一个事务一旦提交,它对数据库中数据的改变就是永久性的,后面的其它操作和故障都不应该对其有任何影响。

我们的本地事务由资源管理器进行管理:

而事务的ACID是通过InnoDB日志和锁来保证。事务的隔离性是通过数据库锁的机制实现的,持久性通过redo log(重做日志)来实现,原子性和一致性通过Undo log来实现。UndoLog的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为UndoLog)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。 和Undo Log相反,RedoLog记录的是新数据的备份。在事务提交前,只要将RedoLog持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是RedoLog已经持久化。系统可以根据RedoLog的内容,将所有数据恢复到最新的状态。 对具体实现过程有兴趣的同学可以去自行搜索扩展。

分布式事务

什么是分布式事务

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

分布式事务产生的原因

从上面本地事务来看,我们可以看为两块,一个是service产生多个节点,另一个是resource产生多个节点。

service多个节点

随着互联网快速发展,微服务,SOA等服务架构模式正在被大规模的使用,举个简单的例子,一个公司之内,用户的资产可能分为好多个部分,比如余额,积分,优惠券等等。在公司内部有可能积分功能由一个团队维护,优惠券又是另外的团队维护

在电商系统中,支付订单,就是一个分布式事务了
第一步:支付服务,修改支付订单状态。
第二步:订单服务,订单状态修改为已支付
第三步:库存服务,减库存
第四步:积分服务,为该用户送积分。
以上4个步骤,在分布式系统中,是一个整体,也就是一个分布式事务。

resource多个节点

同样的,互联网发展得太快了,我们的Mysql一般来说装千万级的数据就得进行分库分表,对于一个支付宝的转账业务来说,你给的朋友转钱,有可能你的数据库是在北京,而你的朋友的钱是存在上海,所以我们依然无法保证他们能同时成功。

分布式事务的基础

从上面来看分布式事务是随着互联网高速发展应运而生的,这是一个必然的我们之前说过数据库的ACID四大特性,已经无法满足我们分布式事务,这个时候又有一些新的大佬提出一些新的理论:

CAP

CAP定理,又被叫作布鲁尔定理。对于设计分布式系统来说(不仅仅是分布式事务)的架构师来说,CAP就是你的入门理论。

  • C (一致性):对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据上来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
  • A (可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的,这里的正确指的是比如应该返回50,而不是返回40。
  • P (分区容错性):当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。

这三个要素最多只能同时实现两点,不可能三者兼顾。在分布式系统中,网络无法100%可靠,分区其实是一个必然现象,如果我们选择了CA而放弃了P,那么当发生分区现象时,为了保证一致性,这个时候必须拒绝请求,但是A又不允许,所以分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。

对于CP来说,放弃可用性,追求一致性和分区容错性,我们的zookeeper其实就是追求的强一致。

对于AP来说,放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。

顺便一提,CAP理论中是忽略网络延迟,也就是当事务提交时,从节点A复制到节点B,但是在现实中这个是明显不可能的,所以总会有一定的时间是不一致。同时CAP中选择两个,比如你选择了CP,并不是叫你放弃A。因为P出现的概率实在是太小了,大部分的时间你仍然需要保证CA。就算分区出现了你也要为后来的A做准备,比如通过一些日志的手段,是其他机器回复至可用。

BASE

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually 一致 (最终一致性)三个短语的缩写。 是对CAP中AP的一个扩展其理论是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

1、基本可用(Basically Available): 基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。
2、软状态(Soft State): 软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。MySQL Replication 的异步复制也是一种体现。
3、最终一致性(Eventual Consistency): 最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

  1. 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
  2. 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。
  3. 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。

BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。 BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。

2PC(两阶段提交协议)

两阶段提交协议用于保证分布式事务的原子性,即所有数据库节点要么全部都执行 要么全部不执行,其执行过程主要分为两个阶段:第一阶段为准备阶段,第二阶段为提 交阶段,数据库两阶段提交协议如图所示。

 

 

 

 

 

1、 准备阶段

事务管理器( 事务协调者)为每个资源管理器(事务参与者)都发送 Prepare 消息, 每个参与者要么都直接返回失败(例如权限验证失败) ,要么都在本地执行事务,写本地的 redo undo 日志但是不提交,以达到"万事俱备,只欠东风"的状态。

2、提交阶段

如果事务协调者收到了某个事务参与者的事务操作失败问复或等待事务参与者反馈 超时,则事务协调者为每个事务参与者发送回滚( Rollback )消息,事务参与者再执行回 滚本地事务操作;如果事务协调者在规定时间内收到了所有事务参与者的成功回复,则 发送提交( Comm it 消息,事务参与者在本地执行事务提交操作并执行事务

两阶段提交的目的是尽可能晚地提交事务 在提交事务前尽可能确保每个数据库都能正常运行,并确保该事务对应的 SQL 语句能够在每个数据库上都正常执行,同时将数据的写日志操作提前完成,防止事务执行过程中发生意外。事务的执行和释放是在JPA 基于锁来实现控制的。

优点: 尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL是从5.5开始支持。

缺点:

  • 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
  • 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
  • 数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。

2、事务的四个隔离级别

第一种隔离级别:Read uncommitted(读未提交)

如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据,该隔离级别可以通过“排他写锁”,但是不排斥读线程实现。这样就避免了更新丢失,却可能出现脏读,也就是说事务B读取到了事务A未提交的数据

解决了更新丢失,但还是可能会出现脏读

第二种隔离级别:Read committed(读提交)

如果是一个读事务(线程),则允许其他事务读写,如果是写事务将会禁止其他事务访问该行数据,该隔离级别避免了脏读,但是可能出现不可重复读。事务A事先读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。

解决了更新丢失和脏读问题

第三种隔离级别:Repeatable read(可重复读取)

可重复读取是指在一个事务内,多次读同一个数据,在这个事务还没结束时,其他事务不能访问该数据(包括了读写),这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别,读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务(包括了读写),这样避免了不可重复读和脏读,但是有时可能会出现幻读。(读取数据的事务)可以通过“共享读镜”和“排他写锁”实现。

解决了更新丢失、脏读、不可重复读、但是还会出现幻读

第四种隔离级别:Serializable(可序化)

提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁”是无法实现序列化的,必须通过其他机制保证新插入的数据不会被执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读

 

以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低,像Serializeble这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况来,在MYSQL数据库中默认的隔离级别是Repeatable read(可重复读)。

 

注:设置数据库的隔离级别一定要是在开启事务之前

3、spring事务的传播行为

 

事务传播行为指的就是当一个事务方法(当前事务)被另一个事务方法(调用者)调用时,这个事务方法(当前事务)对另一个事务方法(调用者)的态度。

简单说就是:事务方法A调用了事务方法B,B需要怎么应对。

Spring事务传播行为的七种类型

事务传播行为类型 说明
PROPAGATION_REQUIRED

表示当前方法必须在事务中运行。

如果调用者有事务,则当前方法加入到调用者事务中运行。

如果调用者没有事务,则当前方法自己新开启一个事务运行。

PROPAGATION_SUPPORTS

表示当前方法不必在事务中运行。

如果调用者有事务,则当前方法加入到调用者事务中运行。

如果调用者没有事务,则当前方法以非事务的形式运行。

PROPAGATION_MANDATORY

表示当前方法必须在调用者事务中运行。

如果调用者有事务,则当前方法加入到调用者事务中运行。

如果调用者没有事务,则当前方法抛出异常。  

PROPAGATION_NESTED

表示当前方法必须在事务中运行。

如果调用者有事务,则当前方法以“嵌套事务”的形式加入到调用者事务中运行。

如果调用者没有事务,则当前方法自己新开启一个事务运行。

PROPAGATION_NEVER

表示调用者必须以非事务形式运行。

如果调用者有事务,则抛出异常。

如果调用者没有事务,则当前方法以非事务的形式运行。

PROPAGATION_REQUIRES_NEW

表示当前方法必须在事务中运行。

如果调用者有事务,则当前方法自己新开启一个事务运行。

如果调用者没有事务,则当前方法当前方法自己新开启一个事务运行。

PROPAGATION_NOT_SUPPORTED

表示当前方法不支持在事务中运行。

如果调用者有事务,则挂起调用者的事务,当前方法以非事务的形式运行。

如果调用者没有事务,则当前方法以非事务的形式运行。

 
posted @   羽十六  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示