事务的奥秘:ACID特性、隔离级别与日常挑战

55 篇文章 0 订阅
23 篇文章 0 订阅
本文详细介绍了事务在并发环境中的作用,包括原子性、一致性、隔离性和持久性,以及事务并发问题如脏读、不可重复读和幻读。此外,还讨论了Spring事务管理的原理、回滚机制和失效情况,以及如何解决日常遇到的事务问题,如全局事务控制、空事务和大事务处理策略。
摘要由CSDN通过智能技术生成

一、事务的作用

1、当多个程序并发访问数据库时,事务可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作相互干扰
2、事务为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持数据一致性的方法

二、ACID特性

1、原子性:
A、事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行的过程中,要么全部执行,要么全部不执行
B、任何一个操作失败都将导致整个事务的失败,同时其它已经被执行的操作都将被撤销并回滚。只有所有操作全部成功,整个事务才算成功完成
2、一致性:
A、事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行前后,数据库必须处于一致性状态。换句话说,事务的执行结果必须是使数据库库从一个一致性状态转变到另一个一致性状态
例如:银行的转账操作就是一个事务。假设A和B原来的账户都有100元,此时A转账给B50元,转账结束后,应该是A账户减去50元变成50元,B账户增加50元变成150元。A、B的账户总和还是200元。转账前后,数据库就是从一个一致性状态(A100元,B100元,A、B共200元)转变到另一个一致性状态(A50元,B150元,A、B共200元)。假设转账结束后只扣了A账户,没有增加B账户,这时数据库就处于不一致的状态。
3、隔离性:
事务的隔离性是指在并发的环境中,并发的事务是互相隔离的,事务之间互不干扰。在标准的SQL规范中定义了4个事务隔离级别,不同隔离级别对事务的处理不同
4、持久性:
事务的持久性又称为永久性,是指一个事务一旦提交,对数据库中对应数据的状态变更就应该是永久性的。即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束时的状态。

三、事务并发问题

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,此时A读取到的数据是脏数据
2、不可重复读:事务A多次读取同一数据,事务B在事务A读取的过程中对数据进行了更新并提交,导致事务A多次读取同一数据时,结果不一致
例如:还是以上述银行转账为例,在事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导致A再读自己的工资时工资变为 2000;这就是不可重复读
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读
例如:某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

四、事务隔离级别

在这里插入图片描述
小结:以上4个级别的隔离性依次增强,分别解决不同的问题。事务隔离级别越高,就越能保证数据的完整性和一致性,但同时对并发性能的影响也越大。
MySQL事务隔离级别(默认隔离级别-可重复读REPEATABLE-READ)
补充:
1、 事务隔离级别为读提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、 事务隔离级别为串行化时,读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

五、Spring事务管理

1、Spring事务的原理:
我们先明白 Spring 事务的本质其实就是数据库对事务的支持。没有数据库的事务支持,Spring 是无法提供事务功能的。那么,我们一般使用 JDBC 操作事务的代码如下:
A、 获取连接 Connection con = DriverManager.getConnection();B、开启事务 con.setAutoCommit(true/false);
C、 执行 CRUD;
D、 提交事务、回滚事务:con.commit() ,con.rollback();
E、关闭连接 conn.close()。
小结:使用 Spring 事务管理后,我们可以省略步骤 B和步骤 D,让 AOP 帮你去做这些工作,关键类在 TransactionAspectSupport 这个切面里
2、spring事务的回滚
首先我们要明白, Spring 事务回滚机制是这样的:当所拦截的方法有指定异常抛出,事务才会自动进行回滚!
在这里插入图片描述
像上面代码,自行处理异常不抛出,事务是不回滚的。
小结:在实际开发中,我们会遇到这么一种情况:就是并没有异常发生,但是由于事务结果未满足具体业务需求,所以我们需要手动回滚事务。于是乎方法也很简单:自己在代码里抛出一个自定义异常(常用);通过编程用代码回滚(不常用)。
在这里插入图片描述
3、spring事务的失效:
A、方法修饰符不是public
B、发生了错误的异常默认回滚的是:RuntimeException。如果是其他异常想要回滚,需要在@Transactional 注解上加 rollbackFor 属性
C、 数据库不支持事务
D、多线程或者线程
E、后面会有一篇【大事务优化处理】的文章
4、spring事务传播:
spring提供了7种事务传播机制
在这里插入图片描述

在这里插入图片描述
小结:我们程序一般分为controller、service、mapper;一般我们把事务控制在service;上面的三层不可避免会出现service调用service,这样就产生了事务与事务之间的冲突,spring也为我们考虑到这点,就出现了spring事务传播。在领域驱动设计思想中,在上面三层service、mapper中间存在一个domain层,事务也控制在service,与上面三层不同的时,领域驱动设计思想是禁止service调用service层的,因为他们不是同一个域

六、日常遇到的事务问题及解决思路

1、有一些项目可能会定义全局的事务机制,如下
在这里插入图片描述
解决:
A、事务控制在com.demo.test.*.service包及子包下面
B、对上面该包下面所有类中query、list等前缀开头的方法(public)进行read-only;其他的方法都是REQUIRED机制
2、空事务:就是在service方法中并没有操作数据库,而框架且起一个事务
3、大事务:
在这里插入图片描述
上面代码问题:
A、上面的方法耗时最⻓的调用第三方api,这段时间数据库不会释放该链接,在高并发会造成数据库连接池不够用,处在等待
B、 占用资源及内存,得不到及时释放
改正:
A、另起一个service类(推荐)
B、在同一类中再起一个方法(推荐)
C、 ((AService) AopContext.currentProxy()).
第二种:
在这里插入图片描述
解决思考小结:
所有的service层方法都是从外部调用的。所以我们首先要从外部方法入手。
整体思路:
A、是否必须需要事务,例如单表保存,没有必要
B、大事务中对方法进行拆分,例如上面将一个方法拆分成两个方法C、对耗时的操作,例如查询、调用第三方API、上传文件等,不可出现在事务方法中大部分业务使用可以参考上述大事务处理由于业务的复杂性,对不同的业务场景需要具体分析进行修改
整体原则:
A、禁止空事务
B、禁止大事务
C、对耗时的操作,例如耗时查询、调用第三方API、上传文件等,不可出现在事务方法中
D、我的博客里面会有一篇关于【大事务优化处理】的文章

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值