2025-05-08🌱上海: ☀️ 🌡️+19°C 🌬️↖19km/h
# **Part007 使用 TransactionTemplate 优化接口性能
# 1. 为什么(Why)
# 1.1 项目背景
part007
模块实现了一个基于 Spring 的事务管理示例,主要针对在数据库操作中事务范围控制的优化。在实际业务系统中,良好的事务管理对系统性能和可靠性具有重要影响。本模块通过对比声明式事务和编程式事务的实现,展示了在不同场景下如何选择合适的事务管理方式,特别是当业务逻辑中包含耗时操作时,如何优化事务范围以提高系统性能和资源利用率。
# 1.2 解决的问题
-
事务范围过大:声明式事务可能导致事务持有时间过长,尤其是当方法中包含耗时操作时,会降低数据库连接的利用率,增加死锁风险。
-
资源占用:长事务会占用数据库连接资源,在高并发场景下可能导致连接池耗尽。
-
性能瓶颈:事务范围过大会增加数据库锁的持有时间,降低系统并发性能。
-
灵活性不足:声明式事务难以精细控制事务边界,不适合复杂业务场景。
# 2. 如何实现(How)
# 2.1 项目结构
part007/ | |
├── src/ | |
│ ├── main/ | |
│ │ ├── java/ | |
│ │ │ └── com/ | |
│ │ │ └── muzi/ | |
│ │ │ └── part7/ | |
│ │ │ ├── controller/ # 控制层 | |
│ │ │ │ └── part7Controller.java # 控制器 | |
│ │ │ ├── service/ # 服务层 | |
│ │ │ │ └── part7Service.java # 服务实现 | |
│ │ │ ├── mapper/ # 数据访问层 | |
│ │ │ │ └── part7Mapper.java # MyBatis Mapper | |
│ │ │ └── po/ # 持久化对象 | |
│ │ │ └── part7Po.java # 数据模型 | |
│ │ └── resources/ # 配置文件 | |
│ └── test/ # 测试类 | |
└── pom.xml # Maven配置文件 |
# 2.2 关键技术点
# 2.2.1 案例分析:声明式事务与事务范围
技术实现: 声明式事务通过 @Transactional
注解实现,将整个方法纳入事务管理:
/**
* 声明式事务,事务范围比较大
*/
@Transactional
public void bigTransaction() throws InterruptedException {
// 1、getData()方法模拟一个比较耗时的获取数据的操作,这个方法内部会休眠5秒
String data = this.getData();
//2、将上面获取到的数据写入到db中
part7Po po = new part7Po();
po.setId(UUID.randomUUID().toString());
po.setData(data);
this.part7Mapper.insert(po);
}
public String getData() throws InterruptedException {
//休眠5秒
TimeUnit.SECONDS.sleep(5);
return UUID.randomUUID().toString();
}
原理分析:
-
事务范围
-
使用
@Transactional
注解时,整个方法执行期间都处于事务中 -
包括方法中的所有操作,无论是否涉及数据库
-
在示例中,5 秒钟的休眠也包含在事务范围内
-
-
事务资源占用
-
事务开始时,会从连接池获取数据库连接
-
整个事务期间,连接被独占,不释放回连接池
-
长时间的非数据库操作会导致连接资源浪费
-
-
潜在问题
-
数据库连接长时间占用,降低连接利用率
-
在高并发场景下,可能导致连接池耗尽
-
增加数据库锁定时间,提高死锁风险
-
# 2.2.2 案例分析:编程式事务与事务优化
技术实现: 编程式事务通过 TransactionTemplate
实现,可以精确控制事务边界:
/**
* 使用 TransactionTemplate 编程式事务,可以灵活的控制事务的范围
*/
public void smallTransaction() throws InterruptedException {
// 1、调用getData()方法,获取数据,耗时5秒
String data = this.getData();
//2、将上面获取到的数据写入到db中
part7Po po = new part7Po();
po.setId(UUID.randomUUID().toString());
po.setData(data);
// transactionTemplate.executeWithoutResult可以传入一个Consumer,表示在事务中执行的业务操作
this.transactionTemplate.executeWithoutResult(action -> {
this.part7Mapper.insert(po);
});
}
原理分析:
-
精确的事务控制
-
通过
TransactionTemplate
明确定义事务的开始和结束 -
只有实际需要数据库操作的代码才被包含在事务中
-
耗时操作(如
getData()
)被排除在事务之外
-
-
资源优化
-
数据库连接只在执行事务代码块时被获取和占用
-
大大减少了每个请求占用数据库连接的时间
-
显著提高了数据库连接的利用率
-
-
性能提升
-
在示例中,事务持有时间从 5 秒以上减少到毫秒级
-
系统可以处理更多的并发请求
-
数据库锁持有时间大幅缩短,降低死锁风险
-
# 2.2.3 案例分析:事务传播行为
虽然本模块没有直接展示,但事务传播行为是 Spring 事务管理的重要概念:
技术实现: 在 @Transactional
注解中可以指定传播行为:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 业务逻辑
methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 业务逻辑
}
原理分析:
-
传播行为类型
-
REQUIRED:如果当前存在事务,则加入该事务;如果不存在事务,则创建新事务(默认行为)
-
REQUIRES_NEW:创建新事务,如果当前存在事务,则挂起当前事务
-
SUPPORTS:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行
-
NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
-
MANDATORY:如果当前存在事务,则加入该事务;如果不存在事务,则抛出异常
-
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
-
NESTED:如果当前存在事务,则创建一个嵌套事务;如果不存在事务,则创建新事务
-
-
事务嵌套场景
-
服务方法之间相互调用时,事务的处理方式由传播行为决定
-
可以实现局部事务回滚而不影响外部事务
-
适合复杂业务逻辑的事务管理
-
# 3. 技术点详解(Detail)
# 3.1 Spring 事务管理原理
Spring 事务管理基于 AOP(面向切面编程)实现:
-
事务代理
-
Spring 通过创建代理对象,在方法执行前后添加事务处理逻辑
-
对于声明式事务,代理对象负责事务的开启、提交或回滚
-
对于编程式事务,通过模板方法模式封装事务操作
-
-
事务同步管理器
-
TransactionSynchronizationManager
负责管理当前线程的事务资源 -
使用 ThreadLocal 保存事务上下文,确保线程安全
-
维护事务状态、连接资源和同步回调
-
-
事务管理器
-
抽象接口
PlatformTransactionManager
定义了事务操作的标准方法 -
不同数据源有对应的实现类,如
DataSourceTransactionManager
-
负责事务的开启、提交和回滚等底层操作
-
# 3.2 声明式事务与编程式事务对比
-
声明式事务
-
优点:
-
使用简单,只需添加注解
-
代码侵入性小,业务逻辑与事务管理解耦
-
适合简单的 CRUD 操作
-
-
缺点:
-
事务边界控制不够精细
-
可能导致事务范围过大
-
在复杂业务逻辑中难以优化
-
-
-
编程式事务
-
优点:
-
事务边界控制精确
-
可以优化事务持有时间
-
适合复杂业务场景和性能优化
-
-
缺点:
-
代码侵入性较高
-
需要显式编写事务管理代码
-
开发工作量略高
-
-
# 3.3 事务隔离级别
事务隔离级别定义了数据库事务中数据的可见性:
-
隔离级别类型
-
READ_UNCOMMITTED:读未提交,允许读取其他事务未提交的数据变更
-
READ_COMMITTED:读已提交,只允许读取其他事务已提交的数据变更
-
REPEATABLE_READ:可重复读,确保在一个事务中多次读取同一数据时结果一致
-
SERIALIZABLE:序列化,完全隔离,事务串行执行
-
-
隔离级别与并发问题
-
脏读:一个事务读取了另一个事务未提交的数据
-
不可重复读:一个事务多次读取同一数据,但结果不一致(数据被修改)
-
幻读:一个事务多次查询数据时,结果集发生变化(数据被新增或删除)
-
-
Spring 中的配置
@Transactional(isolation = Isolation.READ_COMMITTED)
public void myMethod() {
// 业务逻辑
}
# 3.4 事务超时与只读事务
-
事务超时
-
可以为事务设置超时时间,防止长时间运行的事务占用资源
-
超过指定时间后,事务会自动回滚
-
配置方式:
@Transactional(timeout = 10)
-
-
只读事务
-
对于只读操作,可以将事务标记为只读
-
数据库可能会针对只读事务进行优化
-
配置方式:
@Transactional(readOnly = true)
-
-
使用示例(Usage)
# 4. 事务详解
# 4.1 声明式事务
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void createUser(User user) {
// 执行一些业务逻辑
userMapper.insert(user);
// 可能抛出异常,导致事务回滚
if (user.getAge() < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}
}
# 4.2 编程式事务
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private TransactionTemplate transactionTemplate;
public void createUser(final User user) {
// 执行一些不需要事务的业务逻辑
validateUser(user);
// 使用编程式事务
transactionTemplate.executeWithoutResult(action -> {
userMapper.insert(user);
if (user.getAge() < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
});
}
private void validateUser(User user) {
// 复杂的验证逻辑,不需要包含在事务中
}
}
# 4.3 事务传播行为示例
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
@Transactional
public void createOrder(Order order) {
// 创建订单
orderMapper.insert(order);
// 可能需要独立事务的支付操作
try {
paymentService.processPayment(order);
} catch (Exception e) {
// 支付失败不影响订单创建
log.error("支付失败", e);
}
}
}
@Service
public class PaymentService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment(Order order) {
// 处理支付逻辑
// 如果失败,只回滚支付事务,不影响订单事务
}
}
# 4.4 事务隔离级别示例
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateUserInfo(Long userId, String name) {
User user = userMapper.selectById(userId);
user.setName(name);
userMapper.updateById(user);
}
# 5. 总结与优化方向(Summary)
# 5.1 技术总结
本模块通过对比声明式事务和编程式事务,展示了在不同场景下的事务管理优化:
-
声明式事务简单易用,但事务范围不易控制,可能导致性能问题
-
编程式事务可以精确控制事务边界,优化事务持有时间,提高系统性能
-
对于包含耗时操作的业务逻辑,应将非数据库操作排除在事务之外
-
合理的事务管理可以显著提高系统并发性能和资源利用率
# 5.2 优化方向
-
分布式事务处理
-
在微服务架构中,业务往往跨越多个服务和数据源
-
可引入分布式事务解决方案,如 Seata、TCC 模式等
-
保证跨服务、跨数据源操作的数据一致性
-
-
异步事务处理
-
对于非关键路径的数据处理,可以使用异步事务
-
通过消息队列或事件驱动架构解耦同步操作
-
提高系统响应性能和吞吐量
-
-
事务监控与优化
-
监控长事务和事务执行情况
-
识别和优化频繁执行的事务操作
-
根据业务场景动态调整事务策略
-
-
NoSQL 与混合持久化
-
对于不需要强事务保证的场景,可以使用 NoSQL 数据库
-
实现 SQL 和 NoSQL 的混合持久化策略
-
平衡数据一致性和系统性能
-
-
细粒度权限控制
-
在多租户系统中,实现行级别的数据隔离
-
与事务管理结合,确保数据安全和一致性
-
提供更精细的数据访问控制机制
-