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

博客主要介绍了MySQL事务隔离级别和Spring事务传播机制。事务隔离级别部分阐述了事务四要素、并发问题(脏读、不可重复读、幻读)及不同隔离级别类型;Spring事务传播机制部分介绍了PROPAGATION_REQUIRED、REQUIRES_NEW等多种传播机制的特点。

目录

一、事务隔离级别

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提交
<think> 事务隔离级别传播机制是数据库事务管理中的两个核心概念,主要应用于保证数据一致性处理事务边界问题。 ### 一、事务隔离级别(Isolation Level) 定义事务在并发环境中读取数据的隔离程度,解决脏读、不可重复读、幻读问题。标准隔离级别(以MySQL为例): 1. **读未提交(READ UNCOMMITTED)** - 最低隔离级别,可能读取其他事务未提交的修改 - 问题:脏读、不可重复读、幻读 2. **读已提交(READ COMMITTED)** - 只读取已提交的数据(Oracle默认级别) - 解决脏读,但存在不可重复读幻读 3. **可重复读(REPEATABLE READ)** - 同一事务多次读取结果一致(MySQL默认级别) - 解决脏读不可重复读,仍可能幻读 - MySQL通过MVCC机制避免幻读 4. **串行化(SERIALIZABLE)** - 最高隔离级别事务顺序执行 - 解决所有并发问题,但性能最低 ### 二、事务传播机制(Propagation Behavior) 定义事务方法相互调用时的事务边界(Spring框架定义): | 传播类型 | 说明 | |-------------------|----------------------------------------------------------------------| | **REQUIRED** | 默认值。当前有事务则加入,没有则新建 | | **SUPPORTS** | 当前有事务则加入,没有则以非事务运行 | | **MANDATORY** | 当前有事务则加入,没有则抛异常 | | **REQUIRES_NEW** | 新建事务,挂起当前事务(独立提交回滚) | | **NOT_SUPPORTED** | 以非事务方式执行,挂起当前事务 | | **NEVER** | 以非事务方式执行,当前存在事务则抛异常 | | **NESTED** | 嵌套事务,子事务回滚不影响父事务(使用保存点实现) | ### 三、应用示例(Spring声明式事务) ```java @Transactional( isolation = Isolation.REPEATABLE_READ, // 设置隔离级别 propagation = Propagation.REQUIRED // 设置传播机制 ) public void businessMethod() { // 业务操作 } ``` ### 四、注意事项 1. 隔离级别越高,并发性能越低 2. REQUIRES_NEW传播机制可能导致死锁 3. NESTED传播需数据库支持保存点(如Oracle,MySQL的InnoDB引擎) §§相关问题§§ 1. MySQL如何通过MVCC机制实现可重复读隔离级别? 2. 在哪些场景下应该使用REQUIRES_NEW传播机制? 3. 如何解决REQUIRES_NEW传播机制可能导致的死锁问题? 4. 不同数据库对事务隔离级别的实现有哪些主要差异? 5. 嵌套事务(NESTED)新建事务(REQUIRES_NEW)有何本质区别? </think> ### 事务隔离级别传播机制详解 #### 一、事务隔离级别(解决并发问题) | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现机制 | |-----------------------|------|------------|------|------------------------------| | **读未提交** (Read Uncommitted) | ✓ | ✓ | ✓ | 直接读取最新数据 | | **读已提交** (Read Committed) | × | ✓ | ✓ | 每次读取创建快照 | | **可重复读** (Repeatable Read) | × | × | ✓ | 事务开始时创建全局快照 (MySQL默认) | | **串行化** (Serializable) | × | × | × | 全表锁机制 | **典型问题场景**: 1. 脏读:事务A读到事务B未提交的修改 2. 不可重复读:同事务内两次读结果不同(数据被修改) 3. 幻读:同事务内两次查询结果集不同(数据被增删) #### 二、事务传播机制Spring框架定义) | 传播行为 | 说明 | 适用场景 | |-----------------------|----------------------------------------------------------------------|------------------------------| | **REQUIRED** (默认) | 当前有事务则加入,无事务则新建 | 通用业务逻辑 | | **REQUIRES_NEW** | 新建独立事务,挂起当前事务 | 日志记录/独立操作 | | **NESTED** | 嵌套事务,子事务回滚不影响父事务 | 可部分回滚的子流程 | | **SUPPORTS** | 当前有事务则加入,无事务则以非事务运行 | 查询方法 | | **NOT_SUPPORTED** | 以非事务方式执行,挂起当前事务 | 非核心操作(如发送通知) | | **MANDATORY** | 必须在事务中调用,否则抛异常 | 强制事务环境 | | **NEVER** | 必须在非事务中调用,否则抛异常 | 禁止事务的方法 | #### 三、配置示例(Spring声明式事务) ```java @Transactional( isolation = Isolation.REPEATABLE_READ, // 隔离级别 propagation = Propagation.REQUIRED, // 传播行为 timeout = 30, // 超时时间(秒) rollbackFor = {BusinessException.class} // 回滚异常 ) public void processOrder(Order order) { // 1. 扣减库存(REQUIRED传播) inventoryService.deductStock(order); // 2. 记录操作日志(REQUIRES_NEW传播) logService.recordOperation(order); // 3. 创建支付记录(NESTED传播) paymentService.createPayment(order); } ``` #### 四、重要注意事项 1. **隔离级别选择**: - MySQL默认可重复读(通过MVCC实现) - Oracle默认读已提交 - 串行化级别性能下降约50% 2. **传播行为陷阱**: ```java // 错误示例:同类内调用失效 public void mainMethod() { subMethod(); // 事务注解失效! } @Transactional(propagation = Propagation.REQUIRES_NEW) public void subMethod() { ... } ``` 3. **性能优化建议**: - 读操作使用`@Transactional(readOnly=true)` - 短事务避免使用串行化级别 - REQUIRES_NEW慎用(每次创建新连接) 4. **分布式事务**: - 跨服务调用需用Seata/Saga模式 - 避免跨库事务(使用最终一致性)
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值