【面试真题拆解】Spring事务机制

之前面试的时候还真的有一个面试官问过我:

什么是事务?

事务,说人话就是:

多个数据库操作打包成一个"不可分割的整体",要么全成功,要么全失败并回滚到最初状态,绝对不会出现"只做了一半"的情况。

以生活中最经典的银行转账举个例子那是再合适不过了。

且看:

A给B转100块钱,这一笔转账业务,其实是由两步独立的数据库操作组成的:

  1. A的账户扣100块
  2. B的账户加100块

这两步必须同时成功、一起生效。

如果A扣了钱B没收到,或者B收到了钱A没扣,那银行就乱套了。

事务的四大特性

用银行转账的例子加深理解:

特性 解释 银行转账例子
原子性(Atomicity) 要么全做,要么全不做,就像"原子"一样不可分割 A扣钱和B加钱必须同时成功,不能A扣了B没加
一致性(Consistency) 事务前后,数据要保持"一致",总金额不变 转账前A有1000,B有0,总和是1000;转账后A有900,B有100,总和还是1000
隔离性(Isolation) 多个事务同时执行时,互相不干扰,就像"隔离开"一样 两个转账操作同时改A的账户,不能互相影响,不然钱就不对了
持久性(Durability) 事务提交后,数据就永久保存了,就算数据库挂了也不会丢 转账成功后,就算银行服务器断电重启,A和B的钱数也不会变

隔离级别

为什么要有隔离级别呢?

如果多个事务同时执行,没有隔离的话,会出现3个问题:

  1. 脏读

脏读就是读到了别人还没提交的数据。

比如A转给B100块,还没提交,B就查到自己多了100块,结果A回滚了,B白高兴一场。

  1. 不可重复读

不可重复读的意思是同一个事务里,两次读同一条数据的结果是不一样的。

比如A查自己有1000块,中间B转给了A100块提交了,A再查就变成1100了。

  1. 幻读

幻读就是同一个事务里,两次查的数量不一样。

比如A查账户里有10条记录,中间B插了一条提交了,A再查就变成11条了,就像"幻觉"一样。

这么看,不可重复读幻读 是由别的事务提交后导致的错误,而脏读是读到了别人还没提交的临时数据。

那不可重复读和幻读两者的区别在哪呢?

不可重复读是盯着一条记录看,发现这条记录的内容变了。

而幻读是盯着一堆记录数看,发现总数量变了。

类比一下就是不可重复读是你看自己的银行卡余额,钱数变了;而幻读是你数银行有多少张卡,卡的数量变了。

隔离级别 解决的问题 解释
读未提交(Read Uncommitted) 啥都解决不了 能读到别人没提交的数据,脏读、不可重复读、幻读都有,一般不用
读已提交(Read Committed) 脏读 只能读到别人提交后 的数据,但会有不可重复读、幻读
可重复读(Repeatable Read) 脏读、不可重复读 Spring默认的隔离级别。 同一个事务里,两次读同一个数据一样 ,但会有幻读(MySQL InnoDB用MVCC解决了幻读)
串行化(Serializable) 所有问题 事务一个接一个执行,就像"排队"一样,性能最差,一般不用

传播行为

传播行为就是:

当一个事务方法调用另一个事务方法时,这两个事务该怎么协作?是直接加入原事务?还是新建一个独立的事务?还是先挂起原事务,等新事务执行完再继续?

以下是3 个常用的传播行为:

传播行为 解释 适用场景
REQUIRED(默认) 有事务就加入,没有就新建一个事务 最常用,比如转账服务
REQUIRES_NEW 新建一个事务,挂起原事务,等新事务执行完再继续原事务 比如记录日志,不管主事务成功失败,日志都要记录
NESTED 嵌套事务,有一个保存点: 1. 子事务回滚:只回滚自己,不影响父事务和其他子事务 2. 父事务回滚:带着所有子事务一起回滚 系统批量发福利:某一个用户的福利发放失败,不影响其他用户;但整体回滚,所有用户的福利都会被收回

小贴士

  1. Spring AOP 是基于代理的,@Transactional 只能用在 public 方法上,不然不生效。不管是 JDK 动态代理还是 CGLIB 代理,Spring 都只保证 public 方法的事务生效,非 public 的写了也白写。
  2. 内部调用不会走代理类,直接调用this.method(),所以,同一个类里方法调用,@Transactional不生效。
  3. Spring默认只回滚非受检异常 (RuntimeException、Error),受检异常 (Exception)不回滚。如果要所有异常都回滚,需要在 @Transactional 里加rollbackFor = Exception.class
  4. MySQL的MyISAM引擎不支持事务,只有InnoDB支持。
相关推荐
程序员爱钓鱼2 小时前
Go PDF处理利器: github.com/pdfcpu/pdfcpu 深度指南
后端·面试·go
我是咸鱼不闲呀2 小时前
力扣Hot100系列21(Java)——[多维动态规划]总结(不同路径,最小路径和,最长回文子串,最长公共子序列, 编辑距离)
java·leetcode·动态规划
lihao lihao2 小时前
二分查找
java·数据结构·算法
Albert Edison2 小时前
【C++11】可变参数模板
java·开发语言·c++
ps酷教程2 小时前
spring batch动态示例及原理
spring·batch
代码栈上的思考2 小时前
消息队列持久化:文件存储设计与实现全解析
java·前端·算法
sg_knight2 小时前
设计模式实战:策略模式(Strategy)
java·开发语言·python·设计模式·重构·架构·策略模式
麦麦鸡腿堡2 小时前
JavaWeb_SpringBootWeb,HTTP协议,Tomcat快速入门
java·开发语言