问题
水平分表完成业务代码后,在运行单元测试时,出现了以下两个问题:
- 事务不回滚
- 性能极差:200 个单元测试方法,分表之前,平均耗时 12 分钟左右,分表之后,耗时 50 分钟左右…
解释下,分表之前耗时 12 分钟左右的大概原因:
- 对于持久层代码,没有 mock,直接走的 DB
- 单元测试未使用 H2 等内存数据库,因为有一些操作 H2 不支持
- 其它历史问题,下面会提及
优化
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
处理事务的顺序:
- start message1 transaction
- receive message1
- start message2 transaction
- update message2
- commit me
言七墨 ssage2 transaction - 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
卡死的情况,如果不将卡死的SQL
给kill
掉,最后只能等待很久一段时间,单元测试才能结束。解决办法就是将max-wait
和max-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
要加载分表的