一 数据库事务四个特性

数据库的事务指的是一种机制,一系列的操作指令集合,是并发的系统上的最小控制单元

事务把一系列的的命令作为一个整体,一同向数据库进行提交或者回滚,一个事务内的命令要么全部成功,要么全部失败

事务的四个特性 ACID

  1. 原子性Atomicity:一个事务内的所有操作是一个整体,要么全部成功,要么全部失败
  2. 一致性Consistency:当事务完成时,数据库内的数据必须要处于一致的状态;在事务进行过程中,有可能修改了一条记录,但是另外一条记录还没有修改完成,此时就属于不一致的状态。例如,银行的转账,转账的事务完成之后,两个账户里的钱加起来的总数不变
  3. 隔离性Isolation:不同的事务之间的操作互相不能影响。事务必须是独立的,不能依赖于或者影响其他事务,1给2转账,2又给3转账,则两个操作不能同时进行,必须等其中一个事务结束之后才能访问2的数据,否则有可能会出现问题。
  4. 持久性Durability:事务一旦执行完成,那么就保存到了硬盘上,不管系统是否发生故障,数据都不会再发生改变,事务对数据的操作是永久性的

事务的ACID原则保证了事务要么全部执行成功后提交,要么失败全部回滚,使数据恢复到执行前的状态,不会影响数据

二 数据库事务的四个隔离等级以及并发的操作中可能出现的问题

2.1 四种隔离等级及并发操作引起的三种问题

数据库事务有四个隔离等级,从低到高分别是 Read uncommitted、 Read committed、Repeatable read、 Serializable

读未提交、读已提交、可重复读、序列化

不同的隔离等级在并发的环境下可能会出现不同的问题:脏读,不可重复读、幻读

  1. 读未提交:老板发工资(事务A),原本发一万,手抖多打了个0,同时程序员查工资(事务B开启),查到10万开心坏了;结果老板发现输错了就回滚了或者又多打了个0提交了,反正就是程序员看到的工资不对,这个时候事务B读到了A还没有提交的结果,这个就是读未提交,导致了脏读。

    由于读未提交的隔离等级,对于查询的操作是不加锁的,所以这种隔离等级的一致性是最差的,几乎没有人试用这种隔离等级

  2. 读已提交:Oracle,SqlServer的默认隔离等级,程序员去买单(事务A),第一次检测卡里余额还有一万,这时候他老婆转出来了一万(事务B),然后买单事务A要扣款检测的时候发现余额不足了,这就是在A事务中进行两次查询,查到的结果不一样,导致了不可重复读。

    读已提交和读未提交相同,在查询的时候都没有加锁,但是读已提交使用了快照读的机制,使得读已提交避免了由于事务B加了写锁导致事务A无法获取读锁而阻塞大大降低性能的问题

    读已提交会导致不可重复读的问题

  3. 可重复读:MySql的默认隔离等级,**程序员去买单开始检查卡里的余额(事务A开启),这个时候他老婆(事务B)就取钱执行更新update的操作了,但是程序员(事务A)在这个过程中看到的余额始终是事务A开启之初创建的视图,在一次事务内看到的一直是事务开启之初的视图,这样子就避免了不可重复读的问题

    程序员开始第一次查败家娘们最近一个月的账单即消费记录(事务A开启),但是这个时候败家娘们(事务B)又产生了一笔消费,在账单上又新增了一笔记录,然后程序员发现第二次查询多了一条记录,这个就是幻读,在A开启了查询的事务,可重复读的隔离等级会禁止事务B更新的操作,但是无法阻止插入或删除的操作,这个就是可重复读,会导致幻读多或者少出来的行叫做幻行

  4. 序列化:最高的隔离等级,在这种情况下,所有的事务都是一个一个排着队等待执行的,这种效率最低性能开销也很大,但是可以避免脏读、不可重复读、幻读

  1. 脏读:假如事务A开始执行更新,同时B也开始执行查询,在A更新之前,B读到了还没有更新的数据,此时就是脏读。由于A还没有提交,B读到的属于脏数据,此时就是脏读
  2. 不可重复读:事务A中要进行两次读取同一条数据的操作,A执行完第一次查询后,B开启事务,开始更新这条数据,导致A第二次读到的数据跟第一次读到的数据不一样,这就是不可重复读。**不可重复读对应的是更新update的操作。**读已提交是只能读到提交后的事务,A的第二次查询必须要等到B更新的事务提交后才能执行
  3. 幻读:事务A要读取两次同一范围内的数据,A读完第一次之后,B往里插入或者删除了几条数据,导致A读到的记录数与第一次不一样,这就导致了幻读。幻读对应的是插入insert或者删除delete的操作,多出来或者少的那些行记录叫做幻行

2.2 快照读

了解快照读

这里指的读,要抛开读写分离的思想,这里的读既包含了select还包含了insert、update等语句中的处理逻辑,读分为两种:当前读和快照读

  1. 当前读:读当前时刻已经提交的数据
  2. 快照读:为数据库创建一个快照,进行读的操作的时候从快照中进行读取

快照读可以理解为在进行读的操作的时候,为数据库创建的一个视图;那么快照是什么时候生成的呢?不同的隔离等级下快照的创建时间是不同的。接下来先复习一下隔离等级:

  1. 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到
  2. 读已提交:一个事务提交之后,它做的变更才会被其他事务看到
  3. 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交的数据对其他事务不可见
  4. 串行化。对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行

那么接下来继续看快照时间的创建时间:

  1. 读未提交下,不创建快照
  2. 读已提交下,在每次sql语句开始执行的时候创建快照
  3. 可重复读下,在事务开启的时候创建快照

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 总结

  1. 为什么会出现脏读?因为读到了没有提交的数据
  2. 为什么会出现不可重复读?因为创建了多次快照,读到了已经提交的数据
  3. 为什么会出现幻读?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);
}
  1. DEFAULT:默认值,对应的使用数据源的默认的隔离等级,Oracle、SqlServer默认隔离等级是读已提交,会导致不可重复读和幻读的问题;MySql默认隔离等级是可重复读,会导致幻读的问题
  2. READ_UNCOMMITTED:读未提交
  3. READ_COMMITTED:读已提交
  4. REPEATABLE_READ:可重复读
  5. 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);
}
  1. REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  3. MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  4. REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  5. NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  6. NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
  7. NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED

3.4 其他属性

  1. value:指定事务管理器的名称
  2. readOnly:是否只读
  3. rollbackFor:指定回滚的异常的类型
  4. rollbackForClassname:指定回滚的异常类名字
  5. noRollbackFor:
  6. 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:嵌套的

如果外层有事务,加入事务;如果外层没有事务,则内层开启事务

内层事务要等到外层提交才能提交,如果外层回滚,则内层也会滚,如果内层回滚不影响到外层,则外层正常提交

使用该事务内层回滚不影响外层是有前提的!!!

  1. JDK版本在1.4以上
  2. 事务管理器的nestedTransactionAllowed属性需要设置为true
  3. 外层try-catch内层的异常,因为这样子内层异常就不会影响到内层

01. 数据库中事务的隔离等级及如何设置相关推荐

  1. [数据库原理] 事务的隔离等级 (ANSI标准)

    并发与数据读取 当多个会话同时访问(操作)相同的数据时,将会出现一些意想不到的结果,这包括: – 脏读 -- Dirty reads – 不可重复度-- Non-repeatable reads – ...

  2. mysql 事务sqlserver_SQLServer数据库:事务与隔离级别实例讲解

    本文主要向大家介绍了SQLServer数据库:事务与隔离级别实例讲解,通过具体的内容向大家展现,希望对大家学习SQLServer数据库有所帮助. 上班途中,你在一处ATM机前停了下来.正当你在敲入密码 ...

  3. mysql数据库的事物日志在哪里_mysql数据库中事务日志的作用

    mysql数据库中事务日志的作用 发布时间:2020-06-28 18:13:39 来源:亿速云 阅读:98 作者:Leah 这篇文章将为大家详细讲解有关mysql数据库中事务日志的作用,文章内容质量 ...

  4. android 数据库 事务,Android数据库中事务操作方法之银行转账示例

    Android数据库中事务操作方法之银行转账示例 发布时间:2020-09-14 18:19:43 来源:脚本之家 阅读:79 作者:Qi_Yuan 本文实例讲述了Android数据库中事务操作方法之 ...

  5. 数据库中事务并发问题

    对于同时运行的多个事务,当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制.就会导致各种并发问题: 脏读: 对于两个事务T1,T2, T1读取了已经被T2更新但还没有被提交的字段 之后, ...

  6. 宅在家里写数据库中事务(ACID)

    数据库中的事务(Transaction)的四大特征原子性(Atomicity).一致性(Consistency).隔离性(Isolation)和持久性(Durability),这里以一个银行转账的经典 ...

  7. 多图理解MySQL事务的隔离等级,脏读,不可重复读,幻读的几大概念

    2021.3.17 今天在阅读<高性能MySQL>的第一章时,遇到了四大隔离等级的概念,反复琢磨了许久,最后弄出了几张图来帮助记忆,希望对路过的博友们有帮助. 目录 概念定义 三大问题之一 ...

  8. mysql数据库的事务 acid 隔离级别 脏读 脏写 幻读 不可重复读

    事务的四大特征 原子性(atomicity):要么全部提交(commit),要么全部回滚(rollback) 一致性(consistency):数据从一个合法状态转换成另一种合法状态 隔离性(isol ...

  9. 数据库的事务和隔离级别

    事务的基本特性 原子性,都做或者都不做 一致性,从一个状态装换到另一个状态 隔离性,一个事物执行不能被其他事物干扰 持久性,事务提交后不能更改 事务的隔离级别 未提交读:一个事务内部所有的操作过程,对 ...

最新文章

  1. 非监督HMP算法的物体识别
  2. 使用redis实现异步消息队列
  3. Fiori 出试(WEBIDE平台)day1
  4. 用html可以写游戏,javascript可以写游戏吗?
  5. 你必须足够强大,这个世界才会更加公平
  6. Qt中Ui名字空间以及setupUi函数的原理和实现 转
  7. [公告]博客迁移通知
  8. html中添加3dmax建模,HT for Web自定义3D模型的WebGL应用
  9. Python 帮助文件
  10. AcrelEMS-IDC数据中心综合能效管理解决方案
  11. 初学,这个报错怎么解决
  12. 计算机中的八卦知识,原来计算机的核心技术来自周易八卦
  13. JavaRSAJS加密解密(整合版,仅供自己参考)
  14. asp.net(c#)中IsPostBack是什么意思
  15. 2018-2019赛季多校联合新生训练赛第七场补题和题解(中石油)
  16. Android逆向学习(二):游戏开发物语反编译
  17. 零基础学Java语言--第6周编程题
  18. mysql增加ip访问
  19. IP路由基础及(三种)路由信息获取方式
  20. 微信小程序调用json数据接口并解析

热门文章

  1. Linux基本应用篇
  2. C++ 字符串压缩
  3. 2009年总结amp;amp;我在招商银行软件中心(融博)工作的日子
  4. 3_机器学习数学基础知识
  5. MySQL 部署MHA集群部署
  6. 计算机组装教学设计ppt,2016计算机组装与维护教案.ppt
  7. 僵尸计算机,第二课 事件-僵尸[计算机科学入门(Minecraft)]
  8. CEC2017:斑马优化算法(Zebra Optimization Algorithm,ZOA)求解cec2017(提供MATLAB代码)
  9. 伪随机数和随机数种子
  10. java onchange_select中onchange的用法