Spring 事务深度解析:从 ACID 到分布式事务
Spring 事务是企业级应用中确保数据一致性的核心机制,基于 AOP(面向切面编程) 实现,将事务管理逻辑与业务逻辑解耦。它不仅支持本地事务(单数据库),还通过 JTA 等规范支持分布式事务(跨数据库 / 服务)。从 “事务基础→核心属性→实现原理→实战配置→分布式事务” 五个维度,系统拆解 Spring 事务的工作机制与最佳实践。
事务基础:理解 ACID 与并发问题
事务(Transaction)是数据库操作的最小单元,必须满足 ACID 特性,同时需解决并发场景下的脏读、不可重复读、幻读问题。
1. 事务的 ACID 特性
ACID 是事务的四大核心特性,确保数据操作的一致性与可靠性:
| 特性 | 英文全称 | 核心含义 | 示例场景(转账:A 转 100 给 B) |
|---|---|---|---|
| 原子性(Atomicity) | Atomicity | 事务中的操作 “要么全成功,要么全失败”,无中间状态 | A 扣款 100 成功,但 B 到账 100 失败 → 事务回滚,A 余额恢复 |
| 一致性(Consistency) | Consistency | 事务执行前后,业务状态需符合预期(数据完整性约束不被破坏) | 转账前 A+B 余额 = 2000 → 转账后仍为 2000(不会出现 A 扣了但 B 没到账) |
| 隔离性(Isolation) | Isolation | 多个事务并发执行时,彼此隔离,互不干扰(避免并发问题) | 事务 1 读取 A 余额时,事务 2 修改 A 余额 → 事务 1 读取的是隔离后的数据 |
| 持久性(Durability) | Durability | 事务提交后,结果永久保存到数据库(即使数据库崩溃,数据也不丢失) | 转账成功后,即使数据库重启,A 扣 100、B 加 100 的结果仍存在 |
2. 并发事务的三大问题
当多个事务同时操作同一批数据时,若隔离性不足,会引发三类典型问题:
| 并发问题 | 具体表现 | 示例(事务 T1 读取数据,事务 T2 修改数据) |
|---|---|---|
| 脏读(Dirty Read) | T1 读取 T2 未提交的修改 → T2 回滚后,T1 读取的是 “无效数据” | T2 给 A 加 100(未提交)→ T1 读 A 余额为 1100 → T2 回滚 → T1 基于 1100 做业务,结果错误 |
| 不可重复读(Non-Repeatable Read) | T1 多次读取同一数据 → T2 提交修改后,T1 两次读取结果不一致 | T1 第一次读 A 余额 1000 → T2 给 A 加 100(提交)→ T1 第二次读 A 余额 1100,结果不同 |
| 幻读(Phantom Read) | T1 按条件查询数据 → T2 插入 / 删除符合条件的行 → T1 再次查询,行数变化 | T1 查 “余额> 500 的用户”(2 人)→ T2 新增 1 个余额 600 的用户(提交)→ T1 再次查询,结果为 3 人 |
3. 事务隔离级别:解决并发问题
数据库通过 隔离级别 控制并发事务的干扰程度,Spring 支持数据库的所有隔离级别,并新增 DEFAULT(继承数据库默认级别)。不同级别对并发问题的解决能力不同,性能也不同:
| 隔离级别 | 解决脏读 | 解决不可重复读 | 解决幻读 | 性能 | 数据库默认(常见) |
|---|---|---|---|---|---|
| READ_UNCOMMITTED | ❌ | ❌ | ❌ | 最高 | 无 |
| READ_COMMITTED | ✅ | ❌ | ❌ | 较高 | Oracle、SQL Server |
| REPEATABLE_READ | ✅ | ✅ | ❌ | 中等 | MySQL(InnoDB) |
| SERIALIZABLE | ✅ | ✅ | ✅ | 最低 | 无 |
| DEFAULT(Spring 新增) | 继承数据库默认级别 | 继承数据库默认级别 | 继承数据库默认级别 | 取决于数据库 | - |