【面试真题拆解】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支持。
相关推荐
艾莉丝努力练剑12 分钟前
【QT:解决问题】Qt5Core.dll:无法定位程序输入点
java·开发语言·qt·学习·面试
小蒜学长22 分钟前
“喵汪联盟”宠物领养系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·宠物
程序员黑豆23 分钟前
Java字符串常量池完全指南:原理、intern()方法与性能优化最佳实践
java·前端·ai编程
AI人工智能+电脑小能手26 分钟前
大白话说Java设计模式-14-适配器模式(业务实战篇)
java·设计模式·适配器模式·系统兼容·多渠道对接
xbgRS3 小时前
springboot的自动装配
java·spring boot
嘻哈∠※10 小时前
0061基于 SpringBoot 的投稿与稿件处理系统设计与实现
java·spring boot·后端
白狐_79811 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander25812 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
ShineWinsu12 小时前
对于C++:C++11中lambda、function、bind的解析
c++·面试·笔试·开发·lambda·bind·function
码匠许师傅12 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试