大数跨境
0
0

性能调优!Spring Boot 选择正确的事务方式,提升接口响应速度

性能调优!Spring Boot 选择正确的事务方式,提升接口响应速度 Spring全家桶实战案例
2024-12-25
0
导读:【Spring事务】优化代码选择合适的事务方式,将显著提升系统性能

Spring Boot 3实战案例合集》现已囊括超过70篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最前沿的技术资讯与实践经验。欢迎积极订阅,享受不断升级的知识盛宴!订阅用户将特别获赠合集内所有文章的最终版MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。

环境:SpringBoot2.7.18



1. 编程方式与声明方式

声明式工作中用的最多,因为太过方便,你甚至都不需要做任何的配置。

@Transactionalpublic void save() {  // todo}

编程方式,用的相对较少,因为比起声明式要麻烦点(写的代码多了);但是如果能结合适当的场景那么这种编程的方式会给你系统代理性能的提升。

@Resourceprivate TransactionTemplate template ;
public void save() { // todo template.execute(new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) {       // todo } } ; // todo}

上面两种对事务的使用方式非常明显大家一般都不会选择使用编程的方式。

编程事务应用场景

  1. 细粒度控制:编程式事务提供了对事务的细粒度控制。它允许开发人员在代码中明确地定义事务的开始、提交和回滚,从而可以精确地控制事务的边界和行为。这对于需要精确控制事务逻辑的场景非常有用,例如在复杂的业务逻辑中,可能需要根据不同的条件来决定是否提交或回滚事务。

  2. 非标准事务管理:当事务管理逻辑不符合标准的事务模型时,编程式事务是一个很好的选择。例如,在某些特殊情况下,可能需要在一个方法中执行多个数据库操作,并且这些操作需要被划分到不同的事务中。在这种情况下,如果使用了声明式事务只能控制一个数据源,没法对多个数据源进行控制。这里只是举例,这种情况属于分布式事务了,应该考虑如何保证事务的一致性了。


声明式事务应用场景

  1. 简化事务管理:声明式事务通过注解定义事务规则,使得事务管理变得简化。它不需要在业务逻辑代码中显式地编写事务管理的代码,从而减少了代码的复杂性。

  2. 标准事务管理:声明式事务通常用于标准的事务管理场景,例如数据库的增删改查操作。它提供了对事务的自动管理,包括自动提交和回滚事务,从而减少了开发人员对事务管理的关注。


2. 编程式事务应用

Spring提供了2中编程式的事务管理方式:

  1. 使用TransactionTemplate 或 TransactionalOperator

  2. 通过 TransactionManager


注意:TransactionTemplate是在命令式中使用,TransactionalOperator是在反应式中使用。

2.1 TransactionTemplate

在Spring Boot中如果你引入了如:data-jpa或者data-jdbc、data-r2dbc相关的依赖后,系统会自动的为我们配置TransactionTemplate或TransactionalOperator

源码如下

@Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(ReactiveTransactionManager.class)public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {  return TransactionalOperator.create(transactionManager);}
@Configuration(proxyBeanMethods = false)@ConditionalOnSingleCandidate(PlatformTransactionManager.class)public static class TransactionTemplateConfiguration { @Bean @ConditionalOnMissingBean(TransactionOperations.class) public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) { return new TransactionTemplate(transactionManager);  }}


TransactionTemplate完整使用示例

@Resourceprivate TransactionTemplate template ;@Resourceprivate JdbcTemplate jdbcTemplate ;public void save(Person person) {  template.execute(new TransactionCallback<Object>() {    @Override    public Object doInTransaction(TransactionStatus status) {      try {        int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(), person.getName());      } catch (Exception e) {        e.printStackTrace() ;        // 当发生异常后设置为回滚        status.setRollbackOnly() ;      }      return "success"    }  }) ;}

上面示例是有返回值的情况,如果你不需要返回值则可以将TransactionCallback替换为TransactionCallbackWithoutResult 。

属性配置

public class PersonService {
private final TransactionTemplate transactionTemplate ;
public PersonService(PlatformTransactionManager transactionManager) {    this.transactionTemplate = new TransactionTemplate(transactionManager);    // 设置事务的隔离级别   this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);    // 设置事务超时时间    this.transactionTemplate.setTimeout(30); // 30 seconds }}

注意:你最好不要使用Spring容器自动配置的TransactionTemplate来进行相关属性的配置,因为这是全局的,所有的操作都将使用这一份配置。

2.2 TransactionalOperator

方式1:

public class UserService {
@Resource private R2dbcEntityTemplate template ; private final TransactionalOperator transactionalOperator;
public UserService(ReactiveTransactionManager transactionManager) { this.transactionalOperator = TransactionalOperator.create(transactionManager); }
public Mono<User> save(User user) { return Mono.just(user) .then(template.insert(user)) .doOnNext(u -> {      // 人为的制造异常 System.out.println(1 / 0) ; }) .as(transactionalOperator::transactional); }}

在一个事务的上下文中运行。上面代码执行后你将在控制台看到事务回滚信息。

如果没有上面的as(transactionalOperator::transactional)操作,那么数据将会被正常的插入到数据库中。

方式2:

public Flux<Integer> save2(User user) {  return this.transactionalOperator.execute(new TransactionCallback<Integer>() {    @Override    public Mono<Integer> doInTransaction(ReactiveTransaction status) {      return Mono.just(user)          .then(template.insert(user))          .doOnNext(u -> {            System.out.println(1 / 0) ;          })          .doOnError(RuntimeException.class, e -> status.setRollbackOnly())          .map(User::getUid) ;    }  }) ;}


2.3 TransactionManager

public void save() {  Person person = new Person();  person.setAge(36);  person.setName("张三");  DefaultTransactionDefinition definition = new DefaultTransactionDefinition();  definition.setName("CustomTx") ;  definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED) ;  definition.setReadOnly(false) ;  definition.setTimeout(2) ;  TransactionStatus transactionStatus = tm.getTransaction(definition) ;  try {    jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(), person.getName());    // 制造异常    System.out.println(1 / 0) ;    // 提交    tm.commit(transactionStatus) ;  } catch (Exception e) {    e.printStackTrace() ;    // 回滚    tm.rollback(transactionStatus) ;  }}


3. 性能对比(错误的应用事务)

数据库连接配置

为了看到更好的效果,这里只配置了5个连接。

3.1 基于注解方式

业务方法

@Transactionalpublic void save(Person person) {  try {    // 模拟针对Person执行其它非事务耗时操作    TimeUnit.SECONDS.sleep(1) ;  } catch (InterruptedException e) {}  this.personRepository.saveAndFlush(person) ;}

通过jmeter测试

线程池配置

测试结果

吞吐量非常低,并且还出现了错误。该错误是由于在30s内没有获取到数据库连接。

3.2 基于编程方式

业务方法

public void save(Person person) {  try {    // 模拟针对Person执行其它非事务耗时操作    TimeUnit.SECONDS.sleep(1) ;  } catch (InterruptedException e) {}  this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {    @Override    protected void doInTransactionWithoutResult(TransactionStatus status) {      personRepository.saveAndFlush(person) ;    }  }) ;}

jmeter配置不变,测试结果

吞吐量大幅提升,并且没有出现错误情况。

从上面的测试结果能够充分的说明合理的使用事务方式在有些场景下是能够非常明显提升系统的整体性能。

总结:非事务性的操作应该拿到事务外执行,要么选择编程事务。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

Spring Boot 通过@JsonComponent注解完全控制JSON数据

这4个关于Spring AOP开发技巧你知道吗?

强大!基于 Spring Boot 通过2种模板技术,多种方式生成PDF文件

@Transactional用错!严重影响性能,Spring Boot 事务最佳实践

基于Spring Boot REST API设计指南

技巧!Spring Boot生产环境重新初始化Bean

自定义注解+SpEL实现强大的权限管理

借一古老技术考察你对SpringBoot掌握程度

SpringBoot读取配置文件信息8种方式,你会哪几种?

在SpringBoot中拦截修改请求Body的2种正确方式

Jackson才是王!SpringBoot优雅的控制JSON数据

高性能缓存神器Caffeine

SpringBoot请求参数还可以这样玩?很少有人知道

SpringBoot+Nginx+Lua接口性能提升N倍

【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读285
粉丝0
内容832