Java 开发

多数据源下的单元测试优化

言七墨 · 9月2日 · 2020年 · · 1986次已读

问题

水平分表完成业务代码后,在运行单元测试时,出现了以下两个问题:

  • 事务不回滚
  • 性能极差:200 个单元测试方法,分表之前,平均耗时 12 分钟左右,分表之后,耗时 50 分钟左右…

解释下,分表之前耗时 12 分钟左右的大概原因:

  1. 对于持久层代码,没有 mock,直接走的 DB
  2. 单元测试未使用 H2 等内存数据库,因为有一些操作 H2 不支持
  3. 其它历史问题,下面会提及

优化

1、事务不回滚言七墨问题

多数据源下,单元测试事务不会回滚的问题,网上已经有了解决办法,如下

/**
 * 链式事务管理:非分片数据源和分片数据源
 */
@Bean
public PlatformTransactionManager chainedTransactionManager(
        @Qualifier("dataSourceMessage") DataSource dataSourceMessage,
        @Qualifier("dataSourceMessageSharding") DataSource dataSourceMessageSharding) {
    DataSourceTransactionManager noShardingDataSource = new DataSourceTransactionManager(dataSourceMessage);
    DataSourceTransactionManager shardingDataSource = new DataSourceTransactionManager(dataSourceMessageSharding);
    return new ChainedTransactionManager(noShardingDataSource, shardingDataSource);
}
七墨博客
@Transactional(value = "chainedTransactionManager") // 注解在单元测试类或方法上
public class XxxWriteTest extends AbstractUnitTest {}

ChainedTransactionManager的原理:链式事务就是声明一个ChainedTransactionManager将所有的数据源事务按顺序放到该对象中,并按相反的顺序来执行事务。

ChainedTransactionManager处理事务的顺序:

  1. start message1 transaction
  2. receive message1
  3. start message2 transaction
  4. update message2
  5. commit me言七墨ssage2 transaction
  6. commit message1 transaction // 当这一步出现错误时,由于message2 transaction已经commit,所以message2 transaction不会rollback

总结,ChainedTransactionManager是一款轻量级的强一致事务管理器,它只能用于单机分布式事务,事务依次提交后,若后面的操作出现错误,已经提交的事务不能回滚。由于单元测试是针对程序模块来进行正确性检验的测试工作,故可以使用ChainedTransactionManager来保证单元测试方法执行完毕的事务回滚操作。

2、单元测试性能优化

  • 由于Sharding-JDBC的数据源配置spring.shardingsphere.datasource.shardingds.max-wait = 2000,如果存在网络波动或max-active设置的比较小,导致还https://qimok.cn没拿到数据库连接就超时或数据库连接不够用了,从而事务无法提交,就出现SQL卡死的情况,如果不将卡死的SQLkill掉,最后只能等待很久一段时间,单元测试才能结束。解决办法就是将max-waitmax-active都调大一些。
  • 通过查看@Sql注解的实现,发现其默认在单元测试方法执行之前执行
ExecutionPhase executionPhase() default ExecutionPhase.BEFORE_TEST_METHOD;

由于其是方法级别的,故当将注解@Sql("classpath:db/unitTest/xxx_unit_test.sql")标识在类上时,有多少个单元测试方法,就要执行多少次,且xxx_unit_test.sql中有几十条以REPLACE INTO开头的SQL,本应执行一次的操作,却执行了这么多次,故猜测耗时的主要原因就在这里。通过将注解@Sql("classpath:db/unitTest/xxx_unit_test.sql")标识在单元测试类的第一个执行的方法上后,总耗时8m 25s,性能问题得以解决(可恶的历史遗留问题…)。其实,还有优化空间,在每次单元测试启动的时候,由于Flyway要加载分表的https://qimok.cn配置,启动比较慢,后续考虑在单元测试启动时,停止一些无关的操作,保证每次单元测试的执行效率。

0 条回应