事务概念
一组数据库操作要么全部成功,要么全部失败回滚
四大特性
ACID
A:原子性Atomicity,即不可分割
C:一致性Consistency
I:隔离性Isolation
D:持久性Durability
一致性
一致性不是指数据不变,而是事务执行前后数据完整性不变,即业务规则不能被破坏
隔离性
多个事务修改同一数据万一冲突怎么办?
- 锁机制:行锁,一个事务修改某行,其他事务阻塞等待;
- MVCC 多版本:InnoDB 保存旧版本快照,读的时候读快照,不加锁(快照读)。
持久性
本来不就是写入MySQL了,持久性有什么好说的
你以为 "修改成功写入数据库" 就是写到磁盘了,其实不是。 操作系统有页缓存 : SQL 执行完,数据库先把数据写到内存缓冲区,还没刷到硬盘 。 如果此时数据库进程宕机、服务器断电,内存数据直接消失。 持久性就是靠数据库的redo log:
事务提交前,先把变更日志写入磁盘(redo log 落盘)。就算内存里的数据没刷到数据文件,宕机重启后,数据库读取 redo log 恢复数据。
提交成功 = redo 日志持久到磁盘,不是一定把数据页刷盘。这就是持久性。
Spring事务两大实现方式
1.声明式事务(@Transactional注解)
可加在类/方法上
- 加类:类中所有 public 方法都继承该事务配置
- 加方法:仅该方法生效;方法优先级 > 类级别
⚠️ 注解只对 public 方法生效!private/protected 默认不触发 AOP 代理。

2.编程式事务
示例:
java
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void addUser(){
transactionTemplate.execute(status -> {
//数据库操作
userMapper.insert(...);
//抛异常自动回滚
return true;
});
}
}
事务传播机制
即A 方法有事务,调用 B 方法(也带 @Transactional),B 用哪种事务
即子方法遇到已有事务时,怎么选择事务
| 传播类型 | 含义 |
|---|---|
| REQUIRED(默认) | 有事务就加入当前事务;没有就新建事务。最常用 |
| REQUIRES_NEW | 新建独立事务,挂起外层事务;内外事务互不影响。外层回滚不影响内层提交 |
| SUPPORTS | 有事务就加入;无事务就非事务执行 |
| MANDATORY | 必须在已有事务内执行,外层无事务直接抛异常 |
| NOT_SUPPORTED | 不使用事务;如果外层有事务,先挂起外层事务执行 |
| NEVER | 绝对不能在事务内执行,外层存在事务直接抛异常 |
| NESTED | 嵌套事务,基于保存点 savepoint;内层异常回滚到保存点,不会回滚外层。外层回滚,内层一起回滚。区别于 REQUIRES_NEW:不是独立事务 |
有事务就加入当前事务,什么意思?
场景:A 方法带事务,A 调用 B 方法,B 也是
@Transactional(REQUIRED)。 A 已经开启事务了 → B 不会新建事务,直接共用 A 同一个事务,共用同一个数据库连接。
事务隔离级别(区分事务四大特性)
为了解决并发事务问题:脏读、不可重复读、幻读
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ_UNCOMMITTED 读未提交 | ✅存在 | ✅存在 | ✅存在 |
| READ_COMMITTED 读已提交 (Oracle 默认) | ❌ | ✅存在 | ✅存在 |
| REPEATABLE_READ 可重复读 (MySQL 默认) | ❌ | ❌ | ✅存在(InnoDB 靠 MVCC 解决大部分幻读) |
| SERIALIZABLE 串行化 | ❌ | ❌ | ❌ |
Spring 事务 和 MySQL 事务 的关系 & 区别
Spring 事务是上层封装,MySQL 事务是底层实现。Spring 事务本质就是调用 MySQL 原生事务 API。 没有 MySQL(数据库)的事务能力,Spring 事务完全无法生效。
spring 只是帮你自动写setAutoCommit(false)、commit、rollback。
| MySQL 事务 | Spring 事务 | |
|---|---|---|
| 所属层级 | 数据库层面 | Java 应用框架层面 |
| 能力 | 提供 ACID、锁、MVCC、redo/undo log,真正实现提交回滚 | 管理事务生命周期,传播行为,AOP 自动控制事务边界 |
| 隔离级别 | 真正生效,数据库实现脏读、幻读控制 | Spring 只是把隔离级别参数传给数据库,本身不实现隔离 |
| 传播行为 | ❌数据库没有传播机制 | ✅Spring 特有!数据库完全没有事务传播(REQUIRED/REQUIRES_NEW/NESTED 这些传播规则,是 Spring 在应用层实现) |
Spring事务底层原理(AOP+ThreadLocal)
1.容器启动=>扫描带@Transactional的类=>生成动态代理对象
2.调用事务方法时进入代理拦截器 TransactionInterceptor
java
【代理前置逻辑】 → 执行业务方法 → 【代理后置逻辑】
3.获取事务资源,ThreadLocal绑定链接
4.执行业务代码(mapper)此时不会自动提交
因为当前线程绑定的连接 autocommit=false,所有 SQL 都在同一个事务里
5.目标方法执行
- 正常结束:执行 commit 提交事务
- 抛出异常:执行 rollback 回滚
6.释放资源,清除ThreadLocal绑定
核心:事务资源绑定在线程上,同一个线程内多个方法共用同一个数据库连接
事务失效
核心根源:Spring 事务靠 AOP 动态代理拦截方法。只要没有走代理对象执行目标方法,事务直接失效 。 事务失效 =
TransactionInterceptor没有执行,不会执行开启 / 提交 / 回滚逻辑。
事务的超时、只读 特性
超时
@Transactional(timeout = 10) 单位:秒
含义:事务允许的最长执行时间,超过时间就强制回滚。
⚠️注意:这个超时判断是 Spring 层面,不是 MySQL 的 wait_timeout。
只读
告诉数据库:这个事务只做查询,不做增删改。这是一个提示,不是强制拦截。
有必要吗:
不加注解的话两条 SQL 是两个独立事务,中间别的事务提交修改,第二次查询会读到新数据。 加了 readOnly 事务:两次查询在同一个事务快照,保证可重复读。
