MySql事务隔离级别和Spring传播机制

目录

一、事务隔离级别

1.1、事务的四要素(ACID)

1.2、并发所带来的问题

1.3、事务隔离级别类型

1.4、场景复现

1.4.1、脏读

1.4.2、不可重复读

1.4.3、幻读

二、Spring事务的传播机制


 


一、事务隔离级别

1.1、事务的四要素(ACID)

原子性:事务的所有操作都是原子性,即要不当前操作全部做完。如果中间操作失败,则回到最初的状态。即要不全做完,要不全不做。

一致性:事务开始前和事务开始后,数据库的完整性约束没有被破坏。

隔离性:同一时间,只允许一个事务操作同一个数据源。

持久性:事务完成后,对数据库的所有操作都会持久化到数据库中,不能回滚。

1.2、并发所带来的问题

脏读:事务A读取了事务B更新的数据,然后事务B又进行回滚。此时事务A读取到了脏数据。

不可重复读:事务A多次读取同一数据源出现结果不一致。如:事务B在事务A读取数据源过程中,对数据源进行了更新,导致事务A读取到数据不一致。主要出现在修改场景。

幻读:事务A在更新数据时,事务B又新增了一条新的记录,事务A提交之后发现新增之后的数据没有更新,出现幻读。主要出现新增或者删除等场景。

数据准备:

#表结构
CREATE TABLE `Class_Info` (
  `Id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(11) DEFAULT NULL COMMENT '名称',
  `GradeId` int(11) DEFAULT NULL COMMENT '年级id',
  `HeadTeacher` varchar(11) DEFAULT NULL COMMENT '班主任',
  `AddTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `UpdateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `Class_Info` (`Id`, `Name`, `GradeId`, `HeadTeacher`, `AddTime`, `UpdateTime`)
VALUES
	(1, '李四', 1, '1', '2021-03-23 19:28:44', '2021-03-23 19:28:44');
INSERT INTO `Class_Info` (`Id`, `Name`, `GradeId`, `HeadTeacher`, `AddTime`, `UpdateTime`)
VALUES
	(2, '张三', 1, '1', '2021-03-23 19:28:44', '2021-03-23 19:28:44');

(1)#设置会话事务隔离级别为读未提交
SET session TRANSACTION ISOLATION LEVEL Read uncommitted;

(2)#设置事务为不自动提交
set autocommit=0;

1.3、事务隔离级别类型

读未提交(Read Uncommitted):事务B可以读取事务A为已修改但是未提交的数据。该级别会出现脏读、幻读、不可重复读等现象。

读提交(Read Committed):事务B只能读取事务A提交之后的数据。该场景可出现幻读和不可重复读。不可重复读场景:事务A在B提交之前读取数据,接着在事务B提交之后又读取了数据,发现两次数据不一致。

不可重复读(Repeatable Read):保证一个事务不能读取另一个事务未提交的数据。该级别可能出现幻读场景。

1.4、场景复现

1.4.1、脏读

(1)开启事务 A
start transaction;

(2)#修改数据但不提交
update Class_Info set name='王五'  where id=1;

(3)#查询语句,发现Name已经变成更新之后的值为 '王五'
select *  from Class_Info where id=1;

(4)#回滚修改命令
rollback

(5)#查询语句,发现Name值为更新之前的值 '李四'
select *  from Class_Info where id=1;

结果分析:(4)查询语句在还未回滚之前查询到了(1)还未提交的数据,读取到了脏数据。

1.4.2、不可重复读

(1)#开启事务 A
start transaction;

(2)#查询表中所有记录
select * from Class_Info;


(4)开启事务 B
start transaction;

(5)表添加记录
INSERT INTO `Class_Info` (`Id`, `Name`, `GradeId`, `HeadTeacher`, `AddTime`, `UpdateTime`)
VALUES
	(3, '李逵', 1, '1', '2021-03-23 19:28:44', '2021-03-23 19:28:44');

(6)提交B事务
commit;

(7)事务接着查询表中所有记录
select * from Class_Info;

(8)提交事务 A 
commit;

结果分析:同一个事务中(2)和(7)同一个查询,发现查询的结果不一样,(7)中结果多了一条数据。

1.4.3、幻读

(1)#开启事务 A
start transaction;

(2)#将表中所有名字改为 祁东
update Class_Info set name='祁东'

(4)开启事务 B
start transaction;

(5)事务B添加记录
INSERT INTO `Class_Info` (`Id`, `Name`, `GradeId`, `HeadTeacher`, `AddTime`, `UpdateTime`)
VALUES
	(3, '李逵', 1, '1', '2021-03-23 19:28:44', '2021-03-23 19:28:44');

(6)提交B事务
commit;

(7)事务接着查询表中所有记录
select * from Class_Info;

(8)提交事务 A 
commit;

结果分析:发现结果事务A中本来已经修改所有的名字为祁东,但是由于事务B中间又添加了一条数据,造成出现幻读现象。

 

二、Spring事务的传播机制

PROPAGATION_REQUIRED(默认值):支持当前事务,如果不存在则新建一个事务;如果当前存在事务,则将加入当前事务,合并成一个事务。由于两个操作都在同一个事务中,因此若第二事务出现回滚操作,则整个操作都会回滚。

REQUIRES_NEW:新建事务,如果当前事务存在,则把当前事务挂起;此事务独立提交,即使父级事务异常,子事务还能正常提交。

NESTED:如果当前存在事务,则他变成父事务的子事务,方法结束后不直接提交,而是等事务全部结束后才提交;如果当前没有事务,则新建一个事务;如果异常,父事务可以捕获异常但是不会滚,会正常提交;如果父事务异常,则一定回滚。

SUPPORTS:如果当前存在事务,则加入到当前事务;如果不存在事务则以非事务的方式运行。

NOT_SUPPORTED:以非事务的方式运行;如果当前存在事务,则会把事务挂起;

MANDATORY(强制):如果当前存在事务,则在当前事务中运行;如果当前不存在事务则抛出异常,即父级方法必须有事务。

NEVER:以非事务的方式运行,即当前父级方法必须有事务,如果没有事务则会抛出异常。

总结:方法A为父方法,方法A中执行方法B

正常/异常PROPAGATION_REQUIREDREQUIRES_NEWNESTEDSUPPORTSNOT_SUPPORTEDMANDATORYNEVER
A正常、B正常A提交、B提交A提交、B提交A提交、B提交A提交、B提交A提交、B提交A提交、B提交A提交、B提交
A异常、B正常A回滚、B回滚A回滚、B提交A回滚、B回滚A回滚、B回滚A回滚、B提交A回滚、B回滚A提交、B提交
A异常、B异常A回滚、B回滚A回滚、B回滚A回滚、B回滚A回滚、B回滚A回滚、B提交A回滚、B回滚A提交、B提交
A正常、B异常A回滚、B回滚A提交、B回滚

A提交(捕获异常)、B回滚

A回滚(不捕获异常)、B回滚

A回滚、B回滚A提交、B提交A回滚、B回滚A提交、B提交
### Spring 事务传播行为 Spring 定义了多种事务传播行为来决定当一个方法被另一个带有事务的方法调用时,两者之间的关系。具体来说,存在七种不同的传播行为[^1]: - **REQUIRED**:如果有现有事务,则加入该事务;如果没有则创建一个新的事务。 - **REQUIRES_NEW**:总是启动新的事务,在新事务内执行业务逻辑,即使外部已有事务也暂停旧的并开启新的。 - **SUPPORTS**:若有现成的事务便参其中,反之不运行于任何事务上下文中。 - **NOT_SUPPORTED**:完全不在事务环境中执行,如有活动事务会挂起它。 - **MANDATORY**:强制要求有活跃事务环境,否则抛异常。 - **NEVER**:绝对不允许在事务范围内工作,一旦检测到事务即刻报错。 - **NESTED**:如果当前存在事务,则在其内部建立子事务;若无则按 REQUIRED 处理。 ```java @Transactional(propagation = Propagation.REQUIRED) public void methodA() { // 方法体... } ``` ### MySQL 事务隔离级别及其 Spring 的关联 MySQL 提供了几种标准的事务隔离级别以确保数据读取的一致性准确性[^2]: - **READ UNCOMMITTED (未提交读)**:最低级别的隔离度,允许脏读、不可重复读幻影读现象发生。 - **READ COMMITTED (已提交读)**:防止脏读的发生,但仍可能出现不可重复读幻影读的情况。 - **REPEATABLE READ (可重复读)**:这是 InnoDB 存储引擎默认采用的方式,默认情况下能阻止前两种问题,但在某些场景下仍可能发生幻影读。 - **SERIALIZABLE (序列化)**:最高程度的隔离等级,杜绝一切并发冲突的可能性,但性能开销较大。 在 Spring 中可以通过 `@Transactional` 注解指定特定的操作应该遵循哪个隔离级别: ```java @Transactional(isolation = Isolation.SERIALIZABLE) public void criticalOperation() { // 关键操作代码... } ``` ### Spring MySQL 之间关于事务管理的合作方式 当应用程序使用 Spring 进行开发并 MySQL 数据库交互时,二者共同协作完成复杂的业务需求。一方面,Spring 负责定义事务边界并通过 AOP 技术自动管理协调事务生命周期;另一方面,MySQL 则依据所设定的隔离级别保障每次查询或更新都能获得一致的结果集。这种组合不仅简化了开发者的工作量还提高了系统的稳定性可靠性[^3]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值