前言:
很早之前在苏州面试,面试官问我 MySQL 的事务隔离级别、Spring 的事务传播属性。当时两个概念混在一起,当时还是个刚入行的萌新,完全没有理解这两个概念,直接懵住了,虽然后面也顺利入职。
现在备考软考,再次翻到这块知识点,重新把这部分内容整理一下,如果有对这块不动的小伙伴可以参考:
✅ 事务隔离级别:是 MySQL(数据库层面)管的,控制【多个并发事务之间互相看不看得见对方的数据】
✅ 事务传播属性:是 Spring(框架层面)管的,控制【一个事务方法调用另外一个事务方法时,要不要共用事务、怎么创建事务】

二者不是一个层级!一个管并发打架,一个管嵌套调用。这是最核心的分界线。


一、先理清底层:什么是事务、ACID

事务就是一组 SQL,满足 ACID:

  • A 原子性:要么全成功,要么全回滚
  • C 一致性:事务前后数据合法
  • I 隔离性:并发事务之间互相隔离(隔离级别就是用来实现隔离性的)
  • D 持久性:提交之后永久落盘

并发事务如果隔离没做好,会产生 3 个经典问题:脏读、不可重复读、幻读,这是隔离级别要解决的目标。

三个并发问题快速回顾(案例)

  1. 脏读:读到其他事务未提交的数据,对方回滚之后,你读到的数据就是无效脏数据

例:B 修改余额=2000,没提交;A 查询看到2000;B回滚余额变回1000。A读到的2000就是脏读。

  1. 不可重复读:同一个事务内,两次查询同一行记录,中间被别的事务 update + commit,两次读到的值不一样

例:A 第一次查用户年龄=18;B update改成20并提交;A在同一个事务再次查询,年龄变成20。

  1. 幻读:同一个事务内,两次范围查询,别的事务 insert / delete + commit,返回的行数变多/变少,像凭空多出来一行幻影数据

例:A 查询 age < 18 得到2条;B插入一条age=16并提交;A再次相同查询,拿到3条。

⚠️ 考试&面试重点:理论标准 RR(可重复读)只能解决脏读、不可重复读,理论上不能解决幻读;MySQL InnoDB 的 RR 靠 MVCC + 间隙锁工程上规避了幻读,答题要区分【课本理论】和【MySQL实现】。


二、MySQL 四大事务隔离级别(数据库层面)

隔离级别从低到高,隔离越强,并发性能越差,冲突问题越少。

隔离级别 英文 脏读 不可重复读 幻读 说明
读未提交 Read Uncommitted ❌不能解决 ❌不能解决 ❌不能解决 能读到别人没提交的数据,几乎不会线上用
读已提交 Read Committed(RC) ✅解决 ❌不能解决 ❌不能解决 只能读到已经commit的数据;Oracle默认级别
可重复读 Repeatable Read(RR) ✅解决 ✅解决 理论❌,MySQL实现✅ InnoDB 默认隔离级别,面试最常问
串行化 Serializable ✅解决 ✅解决 ✅解决 事务串行排队执行,无并发,性能极低

实际业务案例理解

场景:电商下单,同时很多用户下单扣库存

  • 如果隔离级别是 RC(读已提交):可以防止脏读,但是会出现不可重复读。同一个事务里两次查库存,中间别的订单扣减并提交,库存前后数值不一致。
  • 如果隔离级别是 RR(可重复读,MySQL默认):同一个事务,多次查询库存,拿到的是快照版本,不受外部已提交update影响,避免不可重复读。
  • 如果改成 Serializable:所有读写加锁排队,不会有任何并发问题,但是高流量下单直接卡死,线上几乎不用。

一句话总结隔离级别:多个事务同时跑的时候,它们之间互相“偷看数据”的权限规则。数据库层面控制。


三、划重点:事务传播属性(Spring 层面!和数据库没关系)

很多人当年和我一样踩坑:把传播属性当成数据库的东西,这是致命错误!
传播行为是 Spring 声明式事务 @Transactional 的配置,解决【方法嵌套调用时,事务怎么传递】。

举个业务场景:

orderService.createOrder() 加了 @Transactional(主方法,已经开启事务)
里面调用 stockService.deductStock(),这个扣库存方法也加了 @Transactional
问题来了:扣库存是复用外层下单的事务?还是新开一个独立事务?如果内层异常,要不要回滚外层订单?
这个选择规则,就是事务传播属性。

Spring 一共7种传播属性,面试高频是前5个,剩下两个冷门。

1. REQUIRED(默认,最常用)

@Transactional(propagation = Propagation.REQUIRED)
规则:如果外层已经有事务,就加入当前事务;外层没有事务,自己新建一个事务。
案例:

  • 外层 createOrder 有事务 → deductStock 共用同一个事务
  • 内层扣库存抛异常 → 整个事务回滚,订单创建、库存扣减一起回滚,符合下单逻辑。
    适用:绝大多数业务,下单、保存主表+子表,要原子绑定。

2. REQUIRES_NEW

REQUIRES_NEW:不管外层有没有事务,内层永远新开一个独立新事务;外层事务暂停,等内层执行完再继续。
案例:
下单主事务,里面调用【记录操作日志】方法,日志就算失败,也不能把订单给回滚。

  • 外层下单事务T1,调用日志方法,日志新建独立事务T2
  • T2提交/回滚完全不影响T1;T1回滚,也不影响已经提交的T2日志
    ✅ 典型场景:操作日志、审计记录,日志失败不能干扰主业务。

3. SUPPORTS

SUPPORTS:外层有事务就加入;外层没有事务,就以非事务方式运行(无事务)。
案例:普通查询接口,可事务可不事务。有外层事务就跟着一起,没有就裸读。
注意:查询场景用得多,写操作不推荐。

4. MANDATORY

MANDATORY:必须在已有事务里运行,外层没有事务,直接抛异常。
案例:这个方法只能被别的事务方法调用,禁止单独执行。比如子表插入,不能单独跑。
如果直接单独调用这个方法,Spring直接报错:No existing transaction found for transaction marked with propagation 'mandatory'

5. NOT_SUPPORTED

NOT_SUPPORTED:挂起外层事务,内层方法永远非事务执行。
案例:里面执行一个超长的统计查询,不想占用事务锁、不想拉长主事务耗时。
外层有事务,调用这个方法的时候,外层事务先挂起来,方法裸跑,跑完恢复外层事务。

6. NEVER

NEVER:禁止在事务里运行。如果外层存在事务,直接抛异常。
和MANDATORY反过来:这个方法绝对不能放在事务里面执行。

7. NESTED(嵌套事务)【高频难点,容易和REQUIRES_NEW搞混】

NESTED:外层有事务,则在内层创建保存点(savepoint);外层没有事务,新建事务。
✅ 关键区别对比 REQUIRES_NEW:

  • REQUIRES_NEW:两个完全独立事务,互不干扰
  • NESTED:内层是外层事务里的保存点,内层异常,只回滚到保存点,外层可以选择继续提交;如果外层事务回滚,内层跟着一起回滚

案例:
下单主事务,包含【扣优惠券】子逻辑。优惠券扣减失败,只回滚优惠券,订单还可以继续走别的流程;但如果外层订单整体报错,优惠券也要跟着回滚。

一句话区分 NESTED vs REQUIRES_NEW
REQUIRES_NEW:分家,各是各的;
NESTED:是一家人,里面有个断点,断点坏了可以只回断点,全家翻车则一起翻车。


四、一张表搞定7种传播属性

传播属性 规则简述 核心场景
REQUIRED(默认) 有就加入,没有就新建 90%业务新增、修改,主从逻辑一起回滚
REQUIRES_NEW 一定开新独立事务,外层暂停 日志、审计,子逻辑失败不影响主业务
SUPPORTS 有事务就加入,无事务就裸跑 简单查询
MANDATORY 必须存在外层事务,否则报错 只能被事务方法调用的子业务
NOT_SUPPORTED 挂起外层事务,内层无事务执行 慢查询、大统计,避免长事务
NEVER 外层有事务直接抛异常 禁止事务内执行的逻辑
NESTED 外层事务内创建savepoint保存点 子步骤可局部回滚,但依附外层事务

五、面试标准回答模板(当年我缺失的答案,直接背)

面试官问:说下事务隔离级别和事务传播行为?

答:
事务隔离级别是数据库层面(MySQL InnoDB)的配置,用来控制多个并发事务之间数据可见性,解决脏读、不可重复读、幻读问题,一共4级:读未提交、读已提交、可重复读、串行化,MySQL默认是可重复读。

事务传播行为是Spring框架@Transactional提供的机制,作用于方法嵌套调用,控制内层方法是复用已有事务、新建事务还是不使用事务,一共7种,最常用是REQUIRED、REQUIRES_NEW、NESTED。

二者层级不一样:隔离级别管【事务和事务并发打架】;传播属性管【一个事务里面调用另一个方法时,事务怎么传递】。

面试官追加:REQUIRES_NEW 和 NESTED 区别?

REQUIRES_NEW 是全新独立事务,内外互不影响;NESTED 是在外层事务上建立保存点,内层局部回滚不影响外层,但是外层回滚,嵌套子事务一起回滚。


六、避坑总结(我当年踩的坑)

  1. ❌ 错误:传播属性是MySQL的
    ✅ 正确:传播属性是Spring框架的,数据库根本不知道这个东西
  2. ❌ 错误:隔离级别控制方法嵌套
    ✅ 正确:隔离级别只管并发,不管方法调用嵌套
  3. ❌ 混淆 NESTED 和 REQUIRES_NEW
    ✅ 记住:独立分家=REQUIRES_NEW;一家人、局部断点=NESTED
  4. ❌ 软考/面试坑:RR理论不能解决幻读,MySQL工程实现通过MVCC+间隙锁解决幻读,答题看场景


原文地址: https://www.cveoy.top/t/topic/qHPO 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录