spring事务
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 新增) | 继承数据库默认级别 | 继承数据库默认级别 | 继承数据库默认级别 | 取决于数据库 | - |
注意:隔离级别越高,并发能力越弱(锁机制更严格),需在 “数据一致性” 与 “性能” 间平衡。例如:金融核心业务用
REPEATABLE_READ,非核心查询用READ_COMMITTED。
Spring 事务核心属性:定制事务行为
Spring 事务通过 TransactionDefinition 接口定义核心属性,包括传播行为、隔离级别、超时时间、只读属性、回滚规则,这些属性决定了事务的执行逻辑。
1. 事务传播行为(核心)
传播行为定义了 “当一个事务方法调用另一个事务方法时,如何处理事务上下文”,是 Spring 事务最灵活的特性之一,共 7 种:
| 传播行为常量 | 核心含义 | 适用场景 |
|---|---|---|
| PROPAGATION_REQUIRED | 支持当前事务;若当前无事务,新建事务;若有事务,加入当前事务(默认值) | 绝大多数业务场景(如转账、下单) |
| PROPAGATION_SUPPORTS | 支持当前事务;若当前无事务,以非事务方式执行 | 可选事务的查询方法(如 “获取订单详情”,有事务则加入,无则非事务) |
| PROPAGATION_MANDATORY | 支持当前事务;若当前无事务,抛出异常(强制要求事务上下文) | 必须在事务中执行的方法(如 “扣减库存”,防止调用者遗漏事务) |
| PROPAGATION_REQUIRES_NEW | 新建事务;若当前有事务,将当前事务挂起(新事务与原事务独立) | 独立事务(如 “记录操作日志”,即使主事务回滚,日志仍需提交) |
| PROPAGATION_NOT_SUPPORTED | 以非事务方式执行;若当前有事务,挂起当前事务 | 无需事务的操作(如 “发送短信通知”,避免事务过长影响性能) |
| PROPAGATION_NEVER | 以非事务方式执行;若当前有事务,抛出异常(禁止事务上下文) | 绝对不能在事务中执行的方法(如 “数据备份”,防止锁表) |
| PROPAGATION_NESTED | 若当前有事务,在嵌套事务中执行(嵌套事务可独立回滚);若无事务,新建事务 | 部分回滚场景(如 “下单”:扣库存失败回滚,但生成订单记录不回滚) |
传播行为示例:REQUIRED vs REQUIRES_NEW
public class OrderService {
private InventoryService inventoryService;
// 主事务:传播行为 REQUIRED
public void createOrder() {
// 1. 生成订单(主事务内)
System.out.println("生成订单");
try {
// 2. 调用库存服务(传播行为 REQUIRES_NEW,新建独立事务)
inventoryService.deductStock();
} catch (Exception e) {
// 3. 库存扣减失败,仅回滚 deductStock() 的独立事务,主事务继续执行
System.out.println("库存扣减失败,订单仍保留");
}
}
}
public class InventoryService {
// 子事务:传播行为 REQUIRES_NEW(与主事务独立)
public void deductStock() {
System.out.println("扣减库存");
throw new RuntimeException("库存不足"); // 子事务回滚
}
}
结果:deductStock() 回滚(库存不变),createOrder() 继续执行(订单生成成功)。
2. 其他核心属性
| 属性 | 作用 | 配置示例 |
|---|---|---|
| 隔离级别(isolation) | 控制并发事务的隔离程度 | @Transactional(isolation = Isolation.REPEATABLE_READ) |
| 只读(readOnly) | 标记事务仅执行查询操作(数据库优化:避免加写锁) | @Transactional(readOnly = true)(仅查询方法用) |
| 超时时间(timeout) | 事务超时时间(秒),超时后自动回滚(防止长事务占用资源) | @Transactional(timeout = 30)(30 秒超时) |
| 回滚规则(rollbackFor) | 指定哪些异常触发事务回滚(默认仅回滚运行时异常,如 RuntimeException) | @Transactional(rollbackFor = Exception.class)(所有异常回滚) |
| 不回滚规则(noRollbackFor) | 指定哪些异常不触发回滚 | @Transactional(noRollbackFor = BusinessException.class)(业务异常不回滚) |
注意:
readOnly=true不可用于修改操作(如 insert/update/delete),否则可能导致数据不一致(数据库可能忽略写操作或不加锁)。
Spring 事务实现原理:基于 AOP 的动态代理
Spring 事务的底层是 AOP 动态代理,通过 PlatformTransactionManager(事务管理器)实现事务的 “开启→提交→回滚” 生命周期管理。
1. 核心接口:PlatformTransactionManager
PlatformTransactionManager 是 Spring 事务的顶层接口,定义了事务管理的三大核心操作,不同持久化框架有不同实现:
public interface PlatformTransactionManager {
// 1. 获取事务(根据 TransactionDefinition 配置,开启事务或加入现有事务)
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 2. 提交事务(若事务正常完成,提交;若有保存点或异常,回滚)
void commit(TransactionStatus status) throws TransactionException;
// 3. 回滚事务(事务异常时触发)
void rollback(TransactionStatus status) throws TransactionException;
}
常用实现类(按持久化框架分类):
| 实现类 | 适用框架 | 核心作用 |
|---|---|---|
| DataSourceTransactionManager | MyBatis、Spring JDBC | 管理 JDBC 连接的事务(基于 Connection) |
| HibernateTransactionManager | Hibernate | 管理 Hibernate Session 的事务 |
| JpaTransactionManager | JPA | 管理 JPA EntityManager 的事务 |
| JtaTransactionManager | JTA(分布式事务) | 管理跨数据库 / 服务的分布式事务 |
2. 事务执行流程(AOP 动态代理)
Spring 事务通过 AOP 生成代理对象,在目标方法执行前后插入事务管理逻辑,流程如下:
graph TD
A[调用代理对象的事务方法] --> B["获取 TransactionDefinition(隔离级别、传播行为等)"]
B --> C["PlatformTransactionManager.getTransaction():开启/加入事务"]
C --> D["执行目标方法(业务逻辑)"]
D --> E{方法执行结果}
E -->|正常完成| F["PlatformTransactionManager.commit():提交事务"]
E -->|抛出异常| G["PlatformTransactionManager.rollback():回滚事务"]
F --> H[方法执行结束]
G --> H
关键细节:
- 代理对象生成:若目标类实现接口,用 JDK 动态代理;否则用 CGLIB 动态代理;
- 事务上下文传递:通过
ThreadLocal存储当前事务状态(TransactionStatus),确保同一线程内的事务方法共享事务上下文; - 异常判断:仅当异常符合
rollbackFor配置时,才触发回滚(默认仅回滚运行时异常)。
Spring 事务实战:注解与 XML 配置
Spring 事务支持两种配置方式:注解驱动(推荐,Spring Boot 常用) 和 XML 配置(传统方式),核心是 “配置事务管理器 + 定义事务属性”。
前置准备:环境与依赖
1. 数据库表与初始数据
-- 新建用户表(转账场景)
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
account DOUBLE NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 插入初始数据(A 和 B 各 1000 元)
INSERT INTO user (name, account) VALUES ('张三', 1000), ('李四', 1000);
2. 依赖配置(Maven)
<!-- Spring 核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>
<!-- Spring JDBC(数据源+事务依赖) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>
<!-- Spring 事务核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- Spring AOP(事务基于 AOP 实现) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
方式一:注解驱动配置(推荐)
通过 @Transactional 注解标记事务方法,配合 tx:annotation-driven 启用自动代理,步骤如下:
1. 配置 Spring 上下文(XML)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1. 组件扫描(扫描 @Service、@Component 等) -->
<context:component-scan base-package="com.zhanghe.study.spring4.beans.tx"/>
<!-- 2. 配置数据源(DriverManagerDataSource 仅用于测试,生产用 Druid/C3P0) -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 3. 配置事务管理器(MyBatis/JDBC 用 DataSourceTransactionManager) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/> <!-- 关联数据源 -->
</bean>
<!-- 4. 启用事务注解(自动扫描 @Transactional 注解,生成代理对象) -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2. 编写事务服务类(@Transactional)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class TransferService {
// 注入 JdbcTemplate(简化 JDBC 操作)
private JdbcTemplate jdbcTemplate;
/**
* 转账事务方法:张三给李四转 100 元
* 配置:REQUIRED 传播行为、REPEATABLE_READ 隔离级别、所有异常回滚
*/
public void transfer() {
try {
// 1. 张三扣款 100 元
jdbcTemplate.update("UPDATE user SET account = account - 100 WHERE name = '张三'");
// 2. 模拟异常(测试回滚):取消注释后,事务会回滚,张三和李四余额不变
// throw new RuntimeException("转账失败,模拟异常");
// 3. 李四到账 100 元
jdbcTemplate.update("UPDATE user SET account = account + 100 WHERE name = '李四'");
} catch (Exception e) {
// 异常会被 Spring 捕获,触发事务回滚
throw new RuntimeException("转账失败:" + e.getMessage());
}
}
/**
* 只读查询方法:查询用户余额(配置 readOnly=true 优化性能)
*/
public Double getAccount(String name) {
return jdbcTemplate.queryForObject(
"SELECT account FROM user WHERE name = ?",
new Object[]{name},
Double.class
);
}
}
3. 测试事务效果
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TransactionTest {
public static void main(String[] args) {
// 1. 初始化 Spring 容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-tx.xml");
// 2. 获取事务服务类(实际是代理对象)
TransferService transferService = context.getBean(TransferService.class);
// 3. 转账前查询余额
System.out.println("转账前:张三余额=" + transferService.getAccount("张三")
+ ",李四余额=" + transferService.getAccount("李四"));
try {
// 4. 执行转账
transferService.transfer();
System.out.println("转账成功!");
} catch (Exception e) {
System.out.println("转账失败:" + e.getMessage());
}
// 5. 转账后查询余额
System.out.println("转账后:张三余额=" + transferService.getAccount("张三")
+ ",李四余额=" + transferService.getAccount("李四"));
}
}
测试结果:
- 无异常时:张三余额 900,李四余额 1100(事务提交);
- 有异常时:张三和李四余额仍为 1000(事务回滚)。
方式二:XML 配置(传统方式)
通过 <tx:advice> 定义事务属性,<aop:config> 关联切入点与事务通知,无需注解,步骤如下:
1. XML 配置(核心是事务通知 + AOP 切面)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1. 组件扫描 -->
<context:component-scan base-package="com.zhanghe.study.spring4.beans.tx"/>
<!-- 2. 配置数据源(同注解方式) -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 3. 配置事务管理器(同注解方式) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 4. 配置事务通知(tx:advice):定义事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 4.1 匹配“get*”“find*”开头的方法:只读事务 -->
<tx:method name="get*" read-only="true" isolation="REPEATABLE_READ"/>
<tx:method name="find*" read-only="true" isolation="REPEATABLE_READ"/>
<!-- 4.2 匹配“transfer”“save*”“update*”开头的方法:REQUIRED 传播行为,所有异常回滚 -->
<tx:method name="transfer" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<!-- 4.3 其他方法:默认配置 -->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 5. 配置 AOP 切面:关联切入点与事务通知 -->
<aop:config>
<!-- 5.1 定义切入点:匹配 TransferService 的所有方法 -->
<aop:pointcut id="txPointcut"
expression="execution(* com.zhanghe.study.spring4.beans.tx.TransferService.*(..))"/>
<!-- 5.2 关联切面:切入点 txPointcut 应用事务通知 txAdvice -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
2. 服务类(无注解)
public class TransferService {
private JdbcTemplate jdbcTemplate;
// 无需 @Transactional 注解,XML 已配置事务属性
public void transfer() {
// 逻辑同注解方式(张三扣款→李四到账)
}
// 无需 @Transactional 注解,XML 已配置 readOnly=true
public Double getAccount(String name) {
// 逻辑同注解方式
}
}
测试效果:与注解方式完全一致,XML 配置通过 “方法名匹配” 替代了注解,更适合不允许使用注解的传统项目。
分布式事务:跨数据库 / 服务的一致性
当业务涉及 分库分表 或 跨服务调用 时,本地事务无法保证数据一致性,需使用 分布式事务。Spring 基于 JTA(Java Transaction API)规范支持分布式事务,常用实现有 Atomikos、Bitronix 等。
1. 分布式事务场景
- 分库场景:订单数据存在 DB1,库存数据存在 DB2,下单时需同时修改 DB1 和 DB2;
- 微服务场景:用户服务(修改用户余额)调用订单服务(创建订单),需保证两个服务的事务同时提交或回滚。
2. Spring 分布式事务实现(Atomikos)
Spring Boot 中通过 spring-boot-starter-jta-atomikos 快速集成分布式事务,步骤如下:
1. 依赖配置(Maven)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
<version>2.7.17</version> <!-- 与 Spring Boot 版本匹配 -->
</dependency>
2. 配置多数据源(application.yml)
spring:
jta:
atomikos:
properties:
service: com.atomikos.icatch.standalone.UserTransactionServiceFactory
max-timeout: 300000 # 事务超时时间(毫秒)
datasource:
# 数据源 1(订单库)
order:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
unique-resource-name: orderDataSource # 唯一资源名(分布式事务标识)
# 数据源 2(库存库)
inventory:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/inventory_db?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
unique-resource-name: inventoryDataSource
3. 配置分布式事务管理器
import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.sql.DataSource;
import java.util.Properties;
public class DistributedTxConfig {
// 1. 配置订单库数据源(Atomikos 管理)
public DataSource orderDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
Properties props = new Properties();
props.setProperty("url", "jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf-8");
props.setProperty("user", "root");
props.setProperty("password", "123456");
dataSource.setXaProperties(props);
dataSource.setUniqueResourceName("orderDataSource");
return dataSource;
}
// 2. 配置库存库数据源(Atomikos 管理)
public DataSource inventoryDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
Properties props = new Properties();
props.setProperty("url", "jdbc:mysql://localhost:3306/inventory_db?useUnicode=true&characterEncoding=utf-8");
props.setProperty("user", "root");
props.setProperty("password", "123456");
dataSource.setXaProperties(props);
dataSource.setUniqueResourceName("inventoryDataSource");
return dataSource;
}
// 3. 配置 JTA 事务管理器(分布式事务核心)
public JtaTransactionManager jtaTransactionManager() {
return new JtaTransactionManager();
}
// 4. 配置 JdbcTemplate(分别关联两个数据源)
public JdbcTemplate orderJdbcTemplate() {
return new JdbcTemplate(orderDataSource());
}
public JdbcTemplate inventoryJdbcTemplate() {
return new JdbcTemplate(inventoryDataSource());
}
}
4. 分布式事务服务类
public class DistributedOrderService {
private JdbcTemplate orderJdbcTemplate;
private JdbcTemplate inventoryJdbcTemplate;
// 分布式事务:同时操作订单库和库存库
public void createOrderWithInventory() {
try {
// 1. 订单库:创建订单
orderJdbcTemplate.update("INSERT INTO `order` (order_no, user_id) VALUES ('ORDER001', 1)");
// 2. 库存库:扣减库存
inventoryJdbcTemplate.update("UPDATE inventory SET stock = stock - 1 WHERE goods_id = 1");
// 3. 模拟异常(测试分布式回滚)
// throw new RuntimeException("分布式事务异常");
} catch (Exception e) {
throw new RuntimeException("分布式事务失败:" + e.getMessage());
}
}
}
效果:若任一数据源操作失败,两个数据源的事务会同时回滚,确保数据一致性。
3. 分布式事务注意事项
- 性能问题:分布式事务需协调多个数据库 / 服务,锁机制更严格,性能远低于本地事务,避免过度使用;
- 最终一致性:对于高并发场景,优先采用 “最终一致性” 方案(如 RocketMQ 事务消息、Seata TCC/SAGA 模式),而非强一致性的 JTA 事务;
- 资源释放:确保分布式事务超时时间合理,避免长时间占用数据库连接或网络资源。
Spring 事务常见问题与避坑指南
- @Transactional 注解不生效:
- 原因 1:方法为
private/static/final(动态代理无法代理这些方法); - 原因 2:异常被
try-catch捕获且未重新抛出(Spring 无法感知异常,不触发回滚); - 原因 3:未启用
tx:annotation-driven或@EnableAspectJAutoProxy; - 解决方案:方法用
public修饰,异常捕获后重新抛出,确保自动代理启用。
- 原因 1:方法为
- 事务传播行为选择错误:
- 例:子事务用
REQUIRED时,主事务回滚会导致子事务也回滚(若需子事务独立,用REQUIRES_NEW); - 解决方案:根据业务场景选择传播行为,核心业务用
REQUIRED,独立操作(如日志)用REQUIRES_NEW。
- 例:子事务用
- 隔离级别设置过高:
- 例:用
SERIALIZABLE导致并发能力急剧下降(锁表严重); - 解决方案:非核心业务用
READ_COMMITTED,核心业务用REPEATABLE_READ,避免SERIALIZABLE。
- 例:用
总结:Spring 事务的核心价值
Spring 事务通过 “声明式配置” 与 “AOP 动态代理”,实现了事务管理与业务逻辑的解耦,其核心价值体现在:
- 简化开发:无需手动编写
Connection.commit()/rollback(),注解 / XML 配置即可; - 灵活定制:支持 7 种传播行为、5 种隔离级别,满足复杂业务场景;
- 一致性保障:本地事务确保单库一致性,分布式事务支持跨库 / 服务一致性;
- 兼容性强:适配 MyBatis、Hibernate、JPA 等主流持久化框架