01. 数据库中事务的隔离等级及如何设置
一 数据库事务四个特性
数据库的事务指的是一种机制,一系列的操作指令集合,是并发的系统上的最小控制单元
事务把一系列的的命令作为一个整体,一同向数据库进行提交或者回滚,一个事务内的命令要么全部成功,要么全部失败
事务的四个特性 ACID
- 原子性Atomicity:一个事务内的所有操作是一个整体,要么全部成功,要么全部失败
- 一致性Consistency:当事务完成时,数据库内的数据必须要处于一致的状态;在事务进行过程中,有可能修改了一条记录,但是另外一条记录还没有修改完成,此时就属于不一致的状态。例如,银行的转账,转账的事务完成之后,两个账户里的钱加起来的总数不变
- 隔离性Isolation:不同的事务之间的操作互相不能影响。事务必须是独立的,不能依赖于或者影响其他事务,1给2转账,2又给3转账,则两个操作不能同时进行,必须等其中一个事务结束之后才能访问2的数据,否则有可能会出现问题。
- 持久性Durability:事务一旦执行完成,那么就保存到了硬盘上,不管系统是否发生故障,数据都不会再发生改变,事务对数据的操作是永久性的
事务的ACID原则保证了事务要么全部执行成功后提交,要么失败全部回滚,使数据恢复到执行前的状态,不会影响数据
二 数据库事务的四个隔离等级以及并发的操作中可能出现的问题
2.1 四种隔离等级及并发操作引起的三种问题
数据库事务有四个隔离等级,从低到高分别是 Read uncommitted、 Read committed、Repeatable read、 Serializable
读未提交、读已提交、可重复读、序列化
不同的隔离等级在并发的环境下可能会出现不同的问题:脏读,不可重复读、幻读
读未提交:老板发工资(事务A),原本发一万,手抖多打了个0,同时程序员查工资(事务B开启),查到10万开心坏了;结果老板发现输错了就回滚了或者又多打了个0提交了,反正就是程序员看到的工资不对,这个时候事务B读到了A还没有提交的结果,这个就是读未提交,导致了脏读。
由于读未提交的隔离等级,对于查询的操作是不加锁的,所以这种隔离等级的一致性是最差的,几乎没有人试用这种隔离等级
读已提交:Oracle,SqlServer的默认隔离等级,程序员去买单(事务A),第一次检测卡里余额还有一万,这时候他老婆转出来了一万(事务B),然后买单事务A要扣款检测的时候发现余额不足了,这就是在A事务中进行两次查询,查到的结果不一样,导致了不可重复读。
读已提交和读未提交相同,在查询的时候都没有加锁,但是读已提交使用了快照读的机制,使得读已提交避免了由于事务B加了写锁导致事务A无法获取读锁而阻塞大大降低性能的问题
读已提交会导致不可重复读的问题
可重复读:MySql的默认隔离等级,**程序员去买单开始检查卡里的余额(事务A开启),这个时候他老婆(事务B)就取钱执行更新update的操作了,但是程序员(事务A)在这个过程中看到的余额始终是事务A开启之初创建的视图,在一次事务内看到的一直是事务开启之初的视图,这样子就避免了不可重复读的问题
程序员开始第一次查败家娘们最近一个月的账单即消费记录(事务A开启),但是这个时候败家娘们(事务B)又产生了一笔消费,在账单上又新增了一笔记录,然后程序员发现第二次查询多了一条记录,这个就是幻读,在A开启了查询的事务,可重复读的隔离等级会禁止事务B更新的操作,但是无法阻止插入或删除的操作,这个就是可重复读,会导致幻读,多或者少出来的行叫做幻行
序列化:最高的隔离等级,在这种情况下,所有的事务都是一个一个排着队等待执行的,这种效率最低性能开销也很大,但是可以避免脏读、不可重复读、幻读
- 脏读:假如事务A开始执行更新,同时B也开始执行查询,在A更新之前,B读到了还没有更新的数据,此时就是脏读。由于A还没有提交,B读到的属于脏数据,此时就是脏读
- 不可重复读:事务A中要进行两次读取同一条数据的操作,A执行完第一次查询后,B开启事务,开始更新这条数据,导致A第二次读到的数据跟第一次读到的数据不一样,这就是不可重复读。**不可重复读对应的是更新update的操作。**读已提交是只能读到提交后的事务,A的第二次查询必须要等到B更新的事务提交后才能执行
- 幻读:事务A要读取两次同一范围内的数据,A读完第一次之后,B往里插入或者删除了几条数据,导致A读到的记录数与第一次不一样,这就导致了幻读。幻读对应的是插入insert或者删除delete的操作,多出来或者少的那些行记录叫做幻行
2.2 快照读
了解快照读
这里指的读,要抛开读写分离的思想,这里的读既包含了select还包含了insert、update等语句中的处理逻辑,读分为两种:当前读和快照读
- 当前读:读当前时刻已经提交的数据
- 快照读:为数据库创建一个快照,进行读的操作的时候从快照中进行读取
快照读可以理解为在进行读的操作的时候,为数据库创建的一个视图;那么快照是什么时候生成的呢?不同的隔离等级下快照的创建时间是不同的。接下来先复习一下隔离等级:
- 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到
- 读已提交:一个事务提交之后,它做的变更才会被其他事务看到
- 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交的数据对其他事务不可见
- 串行化。对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行
那么接下来继续看快照时间的创建时间:
- 读未提交下,不创建快照
- 读已提交下,在每次sql语句开始执行的时候创建快照
- 可重复读下,在事务开启的时候创建快照
2.3 如何知道当前是快照读还是当前读
1. 默认隔离等级下,如果不显示加锁就是快照读
select a from t where id = 1
2. 加锁就是当前读
# 共享锁
select a from t where id = 1 lock in share mode;#排他锁
select a from t where id = 1 for update;
3. update操作是当前读
update t set a = a + 1;
关于数据库创建快照的更多可以参考网址:https://blog.csdn.net/qq_34679704/article/details/106161807
2.4 总结
- 为什么会出现脏读?因为读到了没有提交的数据
- 为什么会出现不可重复读?因为创建了多次快照,读到了已经提交的数据
- 为什么会出现幻读?insert和delete可以将快照中的记录删除,而且可以不提交就可以影响快照
三 如何设置
3.1 启动类
在启动类上添加@EnableTransationManageMent注解
对于系统需要提供默认事务管理的情况下,实现接口 TransactionManagementConfigurer 指定
为了避免不必要的问题,如果在业务中必须要明确指定 @Transactional 的value的情况下,不建议实现接口 TransactionManagementConfigurer,这样控制台会明确抛出异常,开发人员就不会忘记主动指定
@SpringBootApplication @EnableTransactionManagement
public class AppMain { public static void main(String[] args) { SpringApplication.run(AppMain.class, args); }
}
3.2 在Service层类或者方法上使用@Transactional注解
如果没有实现TransactionalManagementConfigurer接口,没有配置默认隔离等级,则必须要在注解中添加Isolation属性配置隔离等级,否则将会抛出异常
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
public class DefaultFooService implements FooService {public void getFoo(Foo foo) {// do something}//方法上注解属性会覆盖类注解上的相同属性@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)public void updateFoo(Foo foo) {// do something}
}
3.3 属性配置
3.3.1 隔离等级Isolation
隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读
我们可以看
org.springframework.transaction.annotation.Isolation
枚举类中定义了五个表示隔离级别的值:
public enum Isolation { DEFAULT(-1),READ_UNCOMMITTED(1),READ_COMMITTED(2),REPEATABLE_READ(4),SERIALIZABLE(8);
}
- DEFAULT:默认值,对应的使用数据源的默认的隔离等级,Oracle、SqlServer默认隔离等级是读已提交,会导致不可重复读和幻读的问题;MySql默认隔离等级是可重复读,会导致幻读的问题
- READ_UNCOMMITTED:读未提交
- READ_COMMITTED:读已提交
- REPEATABLE_READ:可重复读
- SERIALIZABLE:序列化
3.3.2 七种传播行为
事务的传播行为是针对嵌套事务而言
我们可以看
org.springframework.transaction.annotation.Propagation
枚举类中定义了7个表示传播行为的枚举值:
public enum Propagation { REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(3),NOT_SUPPORTED(4),NEVER(5),NESTED(6);
}
- REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
- NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED
3.4 其他属性
- value:指定事务管理器的名称
- readOnly:是否只读
- rollbackFor:指定回滚的异常的类型
- rollbackForClassname:指定回滚的异常类名字
- noRollbackFor:
- noRollbackForClassname:
四 事务的七种传播行为
事务的传播机制主要是针对的嵌套事务而言
@Transactional(propagation = Propagation.REQUIRED)
4.1 REQUIRED
spring默认的事务传播行为就是这个
支持事务,如果方法执行的时候已经在一个事务中,那么加入进去;如果没有事务,那么创建一个事务。
外层事务提交后,内层才会提交
内/外层如果抛出了异常,那么将会一起回滚;只要内层事务抛出了异常,那么就会回滚,无论外层是否有try-catch
因为内外层方法在同一个事务中,内层只要抛出了异常,这个事务就会被设置成rollback-only,即使外层try-catch内层的异常,该事务也会回滚
例子
外层方法在调用内层方法的时候包裹住try-catch,内层方法报错抛出异常。
外层:
@Override@Transactionalpublic int addUser(User user) {int i = userMapper.insertSelective(user);Student student = new Student();student.setCourse("cs");student.setName("sid");try {studentService.addStudent(student);}catch (Exception e){//不抛出}return i;}
内层:
@Override
@Transactional//(propagation = Propagation.NESTED)
public int addStudent(Student student) {int i = studentMapper.insertSelective(student);int j = 10/ 0; // 内层报错抛出异常return i;
}
如果内层抛出异常,则尽管外层catch了异常,没有跑出去,但是外层还是会跟着回滚,因为他们在同一个事务中,会一起失败
4.2 SUPPORTS
支持事务,如果当前有事务就加入,没有就算了
如果外层没有事务,就没有事务,不会开启事务;如果外层有事务,那么就加入事务。
如果有事务的话还是会一起提交一起回滚
有事务的话与REQUIRE一样
示例
内层:
@Override@Transactional(propagation = Propagation.SUPPORTS)// 这个addStudent方法的事务传播行为是SUPPORTS。public int addStudent(Student student) {int i = studentMapper.insertSelective(student);return i;}
外层:
@Override@Transactional //有事务public int addUser(User user) {int i = userMapper.insertSelective(user);Student student = new Student();student.setCourse("cs");student.setName("sid");studentService.addStudent(student);// 调用addStudent方法,addStudent方法的事务传播机制是SUPPORTSreturn i;}
结果:
如果外层没有事务,则内层被调用之后数据直接就被插入到表里了;如果外层有事务,则要等到外层事务提交后内层才会提交
4.3 MANDATORY
mandatory:强制的
如果存在事务,则加入;否则,如果不存在事务,则抛出异常
实例
内层:
@Override@Transactional(propagation = Propagation.MANDATORY)public int addStudent(Student student) {int i = studentMapper.insertSelective(student);return i;}
外层:
@Override@Transactional //有事务public int addUser(User user) {int i = userMapper.insertSelective(user);Student student = new Student();student.setCourse("cs");student.setName("sid");studentService.addStudent(student);// 调用addStudent方法return i;}
如果外层有事务,则加入;如果外层没有事务,则抛出IllegalTransactionStateException:No existing transaction found for transaction marked with propagation 'mandatory’
4.4 REQUIRES_NEW
如果外层没有事务,则创建一个事务
如果外层有事务,则创建一个新的事务!内部执行完就提交了,与外部没有关系!
如果内层抛出异常,则内层回滚;外层如果catch了这个异常,则外层不会回滚;如果外层抛出了异常,不会影响到内层
4.5 NOT_SUPPORTED
不支持事务,如果外层有事务,那么内层执行的时候会挂起外层事务,内层执行完毕后,外层事务恢复执行
如果内层抛出了异常,则外层有catch的话不会影响到外层
4.6 NEVER
不支持事务,如果外层有事务,则直接抛出异常IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never’
4.7 NESTED
nested:嵌套的
如果外层有事务,加入事务;如果外层没有事务,则内层开启事务
内层事务要等到外层提交才能提交,如果外层回滚,则内层也会滚,如果内层回滚不影响到外层,则外层正常提交
使用该事务内层回滚不影响外层是有前提的!!!
- JDK版本在1.4以上
- 事务管理器的nestedTransactionAllowed属性需要设置为true
- 外层try-catch内层的异常,因为这样子内层异常就不会影响到内层
01. 数据库中事务的隔离等级及如何设置相关推荐
- [数据库原理] 事务的隔离等级 (ANSI标准)
并发与数据读取 当多个会话同时访问(操作)相同的数据时,将会出现一些意想不到的结果,这包括: – 脏读 -- Dirty reads – 不可重复度-- Non-repeatable reads – ...
- mysql 事务sqlserver_SQLServer数据库:事务与隔离级别实例讲解
本文主要向大家介绍了SQLServer数据库:事务与隔离级别实例讲解,通过具体的内容向大家展现,希望对大家学习SQLServer数据库有所帮助. 上班途中,你在一处ATM机前停了下来.正当你在敲入密码 ...
- mysql数据库的事物日志在哪里_mysql数据库中事务日志的作用
mysql数据库中事务日志的作用 发布时间:2020-06-28 18:13:39 来源:亿速云 阅读:98 作者:Leah 这篇文章将为大家详细讲解有关mysql数据库中事务日志的作用,文章内容质量 ...
- android 数据库 事务,Android数据库中事务操作方法之银行转账示例
Android数据库中事务操作方法之银行转账示例 发布时间:2020-09-14 18:19:43 来源:脚本之家 阅读:79 作者:Qi_Yuan 本文实例讲述了Android数据库中事务操作方法之 ...
- 数据库中事务并发问题
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制.就会导致各种并发问题: 脏读: 对于两个事务T1,T2, T1读取了已经被T2更新但还没有被提交的字段 之后, ...
- 宅在家里写数据库中事务(ACID)
数据库中的事务(Transaction)的四大特征原子性(Atomicity).一致性(Consistency).隔离性(Isolation)和持久性(Durability),这里以一个银行转账的经典 ...
- 多图理解MySQL事务的隔离等级,脏读,不可重复读,幻读的几大概念
2021.3.17 今天在阅读<高性能MySQL>的第一章时,遇到了四大隔离等级的概念,反复琢磨了许久,最后弄出了几张图来帮助记忆,希望对路过的博友们有帮助. 目录 概念定义 三大问题之一 ...
- mysql数据库的事务 acid 隔离级别 脏读 脏写 幻读 不可重复读
事务的四大特征 原子性(atomicity):要么全部提交(commit),要么全部回滚(rollback) 一致性(consistency):数据从一个合法状态转换成另一种合法状态 隔离性(isol ...
- 数据库的事务和隔离级别
事务的基本特性 原子性,都做或者都不做 一致性,从一个状态装换到另一个状态 隔离性,一个事物执行不能被其他事物干扰 持久性,事务提交后不能更改 事务的隔离级别 未提交读:一个事务内部所有的操作过程,对 ...
最新文章
- 非监督HMP算法的物体识别
- 使用redis实现异步消息队列
- Fiori 出试(WEBIDE平台)day1
- 用html可以写游戏,javascript可以写游戏吗?
- 你必须足够强大,这个世界才会更加公平
- Qt中Ui名字空间以及setupUi函数的原理和实现 转
- [公告]博客迁移通知
- html中添加3dmax建模,HT for Web自定义3D模型的WebGL应用
- Python 帮助文件
- AcrelEMS-IDC数据中心综合能效管理解决方案
- 初学,这个报错怎么解决
- 计算机中的八卦知识,原来计算机的核心技术来自周易八卦
- JavaRSAJS加密解密(整合版,仅供自己参考)
- asp.net(c#)中IsPostBack是什么意思
- 2018-2019赛季多校联合新生训练赛第七场补题和题解(中石油)
- Android逆向学习(二):游戏开发物语反编译
- 零基础学Java语言--第6周编程题
- mysql增加ip访问
- IP路由基础及(三种)路由信息获取方式
- 微信小程序调用json数据接口并解析
热门文章
- Linux基本应用篇
- C++ 字符串压缩
- 2009年总结amp;amp;我在招商银行软件中心(融博)工作的日子
- 3_机器学习数学基础知识
- MySQL 部署MHA集群部署
- 计算机组装教学设计ppt,2016计算机组装与维护教案.ppt
- 僵尸计算机,第二课 事件-僵尸[计算机科学入门(Minecraft)]
- CEC2017:斑马优化算法(Zebra Optimization Algorithm,ZOA)求解cec2017(提供MATLAB代码)
- 伪随机数和随机数种子
- java onchange_select中onchange的用法