Spring 事务传播特性和事务隔离级别
- 一、认识事务
- 1.1 为什么需要事务?
- 1.2 什么是数据库事务?
- 1.3 事务的ACID特性
- 1.4 并发异常
- 二、Spring 事务的隔离级别(5个)
- 三、Spring 事物的传播行为(7个)
一、认识事务
1.1 为什么需要事务?
转账是生活中常见的操作,比如从A账户转账100元到B账号。站在用户角度而言,这是一个逻辑上的单一操作,然而在数据库系统中,至少会分成两个步骤来完成:
- 将A账户的金额减少100元
- 将B账户的金额增加100元
在这个过程中可能会出现以下几个问题:
- 转账操作的第一步执行成功,A账户减少100元,但是第二部执行失败或者未执行便发生系统崩溃导致B账户并没有相应的增加100元。
- 转账操作刚完成就发生系统崩溃,系统重启恢复时丢失崩溃前的转账记录
- 在A转账的同时,另一个账户C也同时转账,由于同时对B用户进行操作,可能会导致B账户金额出现异常。
为了便于解决这些问题,需要引入数据库事务的概念。
1.2 什么是数据库事务?
定义:事务时数据库系统中执行的一个独立工作单位,是由用户定义的一个或多个语句构成的一组操作序列,这组序列要么全部执行,要么全部不执行。
在SQL中定义事务的语句有三条:
- BEGIN TRANSACTION 表示事务开始
- CMOMIT 表示事务提交
- ROLLBACK 表示事务回滚
1.3 事务的ACID特性
- 原子性(Atomicity): 事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败。
- 一致性(Consistency): 事务的执行结果必须使数据库从一个一致性状态到另一个一致性状态。
- 隔离性(Isolation): 如果多个事务并行执行,应像各个事务独立执行一样,一个事务的执行过程不能陪其他事务干扰。
- 持久性(Durability): 事务一旦提交,其对数据库的更新就是持久的。任何事务或系统故障都不会导致数据丢失。
1.4 并发异常
脏读: 脏读是指一个事务读取了另一个事务未提交的数据。
事务1 | 事务2 |
Read(A) = 10 | |
A:=A+10 | |
Write(A) = 20 | |
Read(A)=20 | |
Rollback(A=10) |
在事务1对A的处理过程中,事务2读取了A的值,但之后事务1回滚,导致事务2读取的A是未提交的脏数据。
不可重复读: 不可重复读是指一个事务对同一数据的读取结果前后不一致。脏读和不可重复读的区别在于:前者读取的是事务未提交的脏数据,后者读取的是事务已经提交的数据,只不过因为数据被其他事务修改过导致前后两次读取的结果不一样
事务1 | 事务2 |
Read(A) = 10 | |
Read(A) = 10 | |
A:=A+10 | |
Write(A) = 20 | |
Commit | |
Read(A) = 20 |
由于事务2对A的已提交修改,事务1前后两次读取的结果不一致。
幻读: 幻读是指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致。幻读和不可重复读的区别在于,不可重复读是针对确定的某一行数据而言,而幻读是针对不确定的多行数据。因而幻读通常出现在带有查询条件的范围查询中
事务1 | 事务2 |
Read(A<5) = (1,2,3) | |
Write(A) = 4 | |
Commit | |
Read(A<5) = (1,2,3,4) |
事务1查询A<5的数据,由于事务2插入了一条A=4的数据,导致事务1两次查询得到的结果不一样
二、Spring 事务的隔离级别(5个)
- default (默认): 使用底层数据库的默认隔离级别。对于大多数的数据库来说,默认的隔离级别都是read_commted。
- read_commted (读已提交): 只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可以避免不可重复读和幻读
- read_uncommted (读未提交): 允许事务读取未被其他事务提交的变更,脏读,不可重复读,幻读都会出现
- repeatable_read (可重复读): 可避免脏读和不可重复读,但幻读问题仍然存在
- serlalizable (串行化):所有问题都可以避免,但性能十分低下。
三、Spring 事物的传播行为(7个)
propagation 控制事务传播行为
当一个具有事务控制的方法被另一个有事务控制的方法调用后,需要如何管理事务(新建事务?在事务中执行?把事务挂起?报异常)
保证同一事务中:
- REQUIRED (默认值–肯定有一个事务): 如果当前有事务,就在事务中执行,如果当前没有事务,就新建一个事务。
- SUPPORTS(有没有事务都可以): 如果当前有事务,就在事务中执行,如果当前没有事务,就在非事务下执行
- MANDATORY(必须有一个事务): 必须在事务状态下执行,如果当前有事务,就在事务中执行,如果当前没有事务,就报异常。
保证没有在同一事务中:
- REQUIRES_NEW(一个事务或者两个独立的事务): 必须在事务状态下执行,如果当前没有事务,就新建一个事务,如果当前有事务,将当前事务挂起(放在一边),新建一个事务。
- NOT_SUPPORTS(必须没有事务): 必须在非事务状态下执行,如果当前没有事务,正常执行,如果有事务,把当前事务挂起。
- NEVER: 必须在非事务状态下执行,如果当前没有事务,正常执行,如果有事务,报异常。
- NESTED(一个事务或者两个事务嵌套): 必须在事务状态下执行,如果当前没有事务,就新建一个事务,如果当前有事务,创建一个嵌套事务。