环境:springboot2.3.9.RELEASE+JPA+MySQL一般我们在spring项目中的方法或类中添加事务支持,如下:@TransactionalpublicAccountdeduction(Longid,BigDecimalmoney){Optional<账户>op=accountDAO.findById(id);if(!op.isPresent()){thrownewRuntimeException("不存在");}account.setMoney(account.getMoney().subtract(money));returnaccountDAO.saveAndFlush(account);}以上应该是我们在项目中使用的姿势。这是方法级别的事务。方法执行时,通过动态代理开启事务,执行代码,提交/回滚事务,执行逻辑大致如下:transaction.begin();method.invoke(xxxx);transaction.commit();/transaction.rollback();上面的例子比较简单,整个操作就是计算扣除金额,然后更新数据。这个业务就是在保存数据的时候使用事务,其他一些计算不需要在事务中。想象一下,如果我们在这里保存操作上面的代码,计算逻辑是一个非常复杂的逻辑,可能需要几秒甚至十几秒,而实际保存操作可能在几毫秒内完成。我们也知道,这个方法级事务在执行时,首先要获取一个Connection对象(数据库连接对象),然后开启事务(设置automaticcommit为false,connection.setAutoCommit(false));说到这里,你应该能想到,从获取一个Connection对象到释放它需要几秒甚至十几秒的时间,而且大部分时间都是与事务无关的操作,即它不需要事务,而我们的数据库连接对象本身是非常珍贵和有限的,这就造成了我们系统资源的浪费,系统的吞吐量很低。接下来,我们将以编程方式控制交易供应系统的吞吐量。模拟常规事务以显示低吞吐量操作数据库连接配置:spring:datasource:driverClassName:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://localhost:3306/x?serverTimezone=GMT%2B8username:rootpassword:xxxxtype:com.zaxxer.hikari.HikariDataSourcehikari:minimumIdle:1maximumPoolSize:1autoCommit:truidleTimeout:30000poolName:MasterDatabookHikariCPmaxLifetime:1800000connectionTimeout:30000connectionTestQuery:SELECT1这里数据库连接池配置为一个。模拟Service中的耗时操作@TransactionalpublicAccountdeduction(Longid,BigDecimalmoney){System.out.println("Service当前执行线程:"+Thread.currentThread().getName()+",id="+id+",money="+money);Accountaccount=accountDAO.findById(id).orElse(null);if(account==null){returnnull;}try{TimeUnit.SECONDS.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}account.setMoney(account.getMoney().subtract(money));returnaccountDAO.saveAndFlush(account);}控制器接口@GetMapping("/deduction")publicObjectdeductionAction(Longid,BigDecimalmoney){System.out.println("控制器当前线程:"+Thread.currentThread().getName());returnaccountService.deduction(id,money);}启动两个浏览器测试,观察控制台的输出,两个浏览器都还在转,没有反应。控制台显示Controller方法都进入了,但是Service方法只进入了一个,因为我们的连接池只配置了一个,另一个在等待一个可用的连接对象。而且上面我也说了,其实Service里面一个很耗时的计算是不需要事务的。即使没有可用的连接对象,我们也应该允许执行这些不需要事务的操作。接下来修改代码。编程事务以提高系统吞吐量@ResourceprivateTransactionTemplatetransactionTemplate;publicAccountdeduction(Longid,BigDecimalmoney){System.out.println("服务当前执行线程:"+Thread.currentThread().getName()+",id="+id+",money="+money);Accountaccount=accountDAO.findById(id).orElse(null);if(account==null){returnnull;}try{TimeUnit.SECONDS.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}//以上业务代码执行可能是一个非常耗时的操作。returntransactionTemplate.execute(newTransactionCallback
