目录

一、事务隔离级别

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_REQUIRED

REQUIRES_NEW

NESTED

SUPPORTS

NOT_SUPPORTED

MANDATORY

NEVER

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提交