| 时间 | 事务1 | 事务2 | 备注 |
| — | — | — | — |
| 1 | 读取库存为1 | | |
| 2 | 扣减库存 | | 事务未提交 |
| 3 | | 读取库存为1 | |
| 4 | 提交事务 | | 库存变成0 |
| 5 | | 扣减库存 | 库存为0,无法扣减 |
不可重复读和脏读的区别是:脏读读取到的是未提交的数据,而不可重复读读到的确实已经提交的数据,但是违反了数据库事务一致性的要求。
一般来说,不可重复读的问题是可以接受的,因为其读到的是已经提交的数据,本身并不会带来很大的问题。因此,很多数据库如(ORACLE,SQL SERVER)将其默认隔离级别设置为READ COMMITTED,允许不可重复读的现象。
- REPEATABLE READ (可重复读):可重复读的目标是为了克服READ COMMITED中出现的不可重复读,它指在同一个事务内的查询都是与事务开始时刻一致,以上表为例,在REPEATABLE READ隔离级别下它会发生如下变化:
| 时间 | 事务1 | 事务2 | 备注 |
| — | — | — | — |
| 1 | 读取库存为1 | | |
| 2 | 扣减库存 | | 事务未提交 |
| 3 | | 读取库存 | 不允许读取,等待事务1提交 |
| 4 | 提交事务 | | 库存变成0 |
| 5 | | 读取库存 | 库存为0,无法扣减 |
REPEATABLE READ虽然解决了不可重复读问题,但是他又会带来幻读问题,幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
| 时间 | 事务A | 事务2 | 备注 |
| — | — | — | — |
| 1 | begin | begin | |
| 2 | | 读取id为100的数据 | 没有数据 |
| 3 | 插入id为100的数据 | | |
| 4 | 提交事务 | | |
| 5 | | 读取id为100的数据 | 没有数据 |
| 6 | | 更新id为100的数据 | 成功 |
| 7 | | 读取id为100的数据 | 读取成功 |
| 8 | | 提交事务 | |
事务B在第2步第一次读取id=99
的记录时,读到的记录为空,说明不存在id=99
的记录。随后,事务A在第3步插入了一条id=99
的记录并提交。事务B在第5步再次读取id=99
的记录时,读到的记录仍然为空,但是,事务B在第6步试图更新这条不存在的记录时,竟然成功了,并且,事务B在第8步再次读取id=99
的记录时,记录出现了。
- SERIALIZABLE(串行化):数据库最高的隔离级别,它要求所有的SQL都会按照顺序执行,这样可以克服上述所有隔离出现的各种问题,能够完全包住数据的一致性。
Spring中配置隔离级别
在Spring项目中配置隔离级别只需要做如下操作
@Transactional(isolation = Isolation.SERIALIZABLE)
public int insertUser(User user){
return userDao.insertUser(user);
}
上面的代码中我们使用了串行化的隔离级别来包住数据的一致性,这使它将阻塞其他的事务进行并发,所以它只能运用在那些低并发而又需要保证数据一致性的场景下。
隔离级别字典:
DEFAULT(-1), ## 数据库默认级别
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
在Spring中,当一个方法调用另外一个方法时,可以让事务采取不同的策略工作,如新建事务或者挂起当前事务等,这便是事务的传播行为。
定义
在Spring的事务机制中对数据库存在7种传播行为,通过枚举类Propagation
定义。
public enum Propagation {
/**
-
需要事务,默认传播性行为。
-
如果当前存在事务,就沿用当前事务,否则新建一个事务运行子方法
*/
REQUIRED(0),
/**
-
支持事务,如果当前存在事务,就沿用当前事务,
-
如果不存在,则继续采用无事务的方式运行子方法
*/
SUPPORTS(1),
/**
-
必须使用事务,如果当前没有事务,抛出异常
-
如果存在当前事务,就沿用当前事务
*/
MANDATORY(2),
/**
-
无论当前事务是否存在,都会创建新事务允许方法
-
这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立
*/
REQUIRES_NEW(3),
/**
- 不支持事务,当前存在事务时,将挂起事务,运行方法
*/
NOT_SUPPORTED(4),
/**
- 不支持事务,如果当前方法存在事务,将抛出异常,否则继续使用无事务机制运行
*/
NEVER(5),
/**
-
在当前方法调用子方法时,如果子方法发生异常
-
只回滚子方法执行过的SQL,而不回滚当前方法的事务
*/
NESTED(6);
…
}
日常开发中基本只会使用到REQUIRED(0)
,REQUIRES_NEW(3)
,NESTED(6)
三种。
NESTED
和REQUIRES_NEW
是有区别的。NESTED
传播行为会沿用当前事务的隔离级别和锁等特性,而REQUIRES_NEW
则可以拥有自己独立的隔离级别和锁等特性。
NESTED
的实现主要依赖于数据库的保存点(SAVEPOINT)技术,SAVEPOINT记录了一个保存点,可以通过ROLLBACK TO SAVEPOINT
来回滚到某个保存点。如果数据库支持保存点技术时就启用保存点技术;如果不支持就会新建一个事务去执行代码,也就相当于REQUIRES_NEW
。
Transactional自调用失效
如果一个类中自身方法的调用,我们称之为自调用。如一个订单业务实现类OrderServiceImpl中有methodA方法调用了自身类的methodB方法就是自调用,如:
@Transactional
public void methodA(){
for (int i = 0; i < 10; i ) {
methodB();
}
}
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRES_NEW)
public int methodB(){
…
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
面试结束复盘查漏补缺
每次面试都是检验自己知识与技术实力的一次机会,面试结束后建议大家及时总结复盘,查漏补缺,然后有针对性地进行学习,既能提高下一场面试的成功概率,还能增加自己的技术知识栈储备,可谓是一举两得。
以下最新总结的阿里P6资深Java必考题范围和答案,包含最全MySQL、Redis、Java并发编程等等面试题和答案,用于参考~
重要的事说三遍,关注+关注+关注!
更多笔记分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
程等等面试题和答案,用于参考~
重要的事说三遍,关注+关注+关注!
[外链图片转存中…(img-wTKeGuVa-1713743590674)]
[外链图片转存中…(img-L2u4WfPK-1713743590674)]
更多笔记分享
[外链图片转存中…(img-qvO7zBx0-1713743590674)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!