您好,登录后才能下订单哦!
SpringBoot提升N倍性能的操作是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
环境:springboot2.3.9.RELEASE + JPA + MySQL
一般我们在spring项目中都是在方法或者是类上添加事务支持,如下使用方式:
@Transactional public Account deduction(Long id, BigDecimal money) { Optional<Account> op = accountDAO.findById(id); if (!op.isPresent()) { throw new RuntimeException("不存在"); } account.setMoney(account.getMoney().subtract(money)) ; return accountDAO.saveAndFlush(account) ; }
以上应该是我们在项目中使用事务的姿势了。这里是方法级别的事务,当方法执行的时候通过动态代理打开事务,执行代码,提交事务/回滚事务,执行的逻辑大体如下:
transaction.begin(); method.invoke(xxxx); transaction.commit(); / transaction.rollback();
在上面举的示例比较简单,整个操作就是计算扣减金额,然后更新数据。这个业务也就是在保存数据的时候需要使用到事务,其它的一些计算是不需要在一个事务中的。想象下如果我们这里保存操作之上的代码,计算逻辑是个非常复杂的逻辑可能需要消耗好几秒甚至是十几秒而实际保存操作可能就几毫秒就完成了。我们又知道这方法级的事务在执行的时候是要先获取一个Connection对象(数据库连接对象的)然后打开事务(设置自动提交为false,connection.setAutoCommit(false));说到这你应该能想到,从获取一个Connection对象到释放需要几秒甚至是十几秒的时间,而占用的这些时间中大部分的时间都是与事务无关的操作也就是说是不需要事务的,而我们的数据库连接对象本身就是很宝贵及有限的,这就造成了我们系统的资源浪费,系统的吞吐量非常的低。接下来我们就来通过编程的方式控制事务提供系统的吞吐量。
数据库连接配置:
spring: datasource: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/x?serverTimezone=GMT%2B8 username: root password: xxxx type: com.zaxxer.hikari.HikariDataSource hikari: minimumIdle: 1 maximumPoolSize: 1 autoCommit: true idleTimeout: 30000 poolName: MasterDatabookHikariCP maxLifetime: 1800000 connectionTimeout: 30000 connectionTestQuery: SELECT 1
这里把数据库连接池配置为1个。
Service中模拟耗时的操作
@Transactional public Account deduction(Long id, BigDecimal money) { System.out.println("Service 当前执行线程:" + Thread.currentThread().getName() + ", id = " + id + ", money = " + money) ; Account account = accountDAO.findById(id).orElse(null) ; if (account == null) { return null ; } try { TimeUnit.SECONDS.sleep(10) ; } catch (InterruptedException e) { e.printStackTrace(); } account.setMoney(account.getMoney().subtract(money)) ; return accountDAO.saveAndFlush(account) ; }
Controller接口
@GetMapping("/deduction") public Object deductionAction(Long id, BigDecimal money) { System.out.println("Controller 当前线程:" + Thread.currentThread().getName()) ; return accountService.deduction(id, money) ; }
启动两个浏览器测试,观察控制台的输出
两个浏览器都还在转圈,没有响应。
控制台展示Controller方法都进入了,但是Service方法只进入了一个,因为我们的连接池只配置了一个,另外一个在等待可用的连接对象。而上面我也说了,其实Service中很长的一个计算耗时是不需要事务的,即便没有连接对象可用,我们也应该让这些不需要事务的操作也进行执行。接下来修改代码。
@Resource private TransactionTemplate transactionTemplate ; public Account deduction(Long id, BigDecimal money) { System.out.println("Service 当前执行线程:" + Thread.currentThread().getName() + ", id = " + id + ", money = " + money) ; Account account = accountDAO.findById(id).orElse(null) ; if (account == null) { return null ; } try { TimeUnit.SECONDS.sleep(10) ; } catch (InterruptedException e) { e.printStackTrace(); } // 以上业务代码执行可能是个很耗时的操作。 return transactionTemplate.execute(new TransactionCallback<Account>() { @Override public Account doInTransaction(TransactionStatus status) { try { account.setMoney(account.getMoney().subtract(money)) ; return accountDAO.saveAndFlush(account) ; } catch (Exception e) { logger.error("发生错误:{}", e) ; status.setRollbackOnly() ; } return null ; } }) ; }
这里把方法上的事务注解删了,把需要事务的操作通过编程的方式包装,在Service中注入
TransactionTemplate对象,SpringBoot项目已经自动为我们配置好了,自动装配类:
TransactionAutoConfiguration.java
测试:
浏览器都还在转圈中,查看控制台:
2个Service方法都进去了,基本连接池只有一个连接对象,但是也不妨碍我非事务的代码执行,通过这样的改造,我们的系统吞吐量是不是提升了N呢?
看完上述内容,你们掌握SpringBoot提升N倍性能的操作是怎样的的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。