背景
- 框架:mybatis-flex + springboot3.2
- 原业务逻辑代码如下:
public class VehicleCountServiceImpl{
........
@Override
public List<GetMapTopCountVo> selectMapTopCount(GetMapTopCountDto mapTopCountDto) {
......
Map<Long, Long> unSortMap = new HashMap<>();
List<VehicleCount> vehicleCountList = QueryChain.of(VehicleCount.class)
.select(sum(VehicleCount::getUpCount).as(VehicleCount::getUpCount))
.select(sum(VehicleCount::getDownCount).as(VehicleCount::getDownCount))
.where(VehicleCount::getRoadId).in(roadIdList)
.and(VehicleCount::getDatetime).between(startTime, endTime)
.groupBy(VehicleCount::getRoadId)
.orderBy(VehicleCount::getRoadId).asc()
.list();
for (int i = 0; i < roadIdList.size(); i++) {
long upCount = ObjUtil.isNotEmpty(vehicleCountList) ? vehicleCountList.get(i).getUpCount() : 0L;
long downCount = ObjUtil.isNotEmpty(vehicleCountList) ? vehicleCountList.get(i).getDownCount() : 0L;
unSortMap.put(roadIdList.get(i), upCount + downCount);
}
......
}
......
}
- 以上代码对于百万数据量、
roadIdList.size()=10
的查询需要10秒以上。sql逻辑较为简单,因此无法通过sql优化提高查询效率,故采用java多线程,为每个roadId
运行一个线程进行查询。
代码优化
- ThreadConfig
添加@EnableAsync注解,否则@Async注解无效
@Slf4j
@EnableAsync
@Configuration
public class ThreadConfig implements AsyncConfigurer {
......
@Value("${async.executor.thread.core-pool-size:20}")
private int corePoolSize;
@Value("${async.executor.thread.max-pool-size:50}")
private int maxPoolSize;
@Value("${async.executor.thread.queue-capacity:1024}")
private int queueCapacity;
@Value("${async.executor.thread.keep-alive-seconds:300}")
private int keepAliveSeconds;
@Value("${async.executor.thread.threadNamePrefix:vehicleCount-}")
private String threadNamePrefix;
@Bean(name = "vehicleCountThreadPool")
public ThreadPoolTaskExecutor vehicleCountThreadPool()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(threadNamePrefix);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
- VehicleCountMapper
之所以不将selectCount
方法放到service层,是因为Spring AOP是基于动态代理的,而调用该方法的方法也在service层,spring同一类中的方法调用不走动态代理,这样会导致@Async注解失效
@Mapper
public interface VehicleCountMapper extends BaseMapper<VehicleCount> {
@Async("vehicleCountThreadPool")
default Future<VehicleCount> selectCount(Long roadId, LocalDateTime startTime, LocalDateTime endTime){
VehicleCount vehicleCount = QueryChain.of(VehicleCount.class)
.select(sum(VehicleCount::getUpCount).as(VehicleCount::getUpCount))
.select(sum(VehicleCount::getDownCount).as(VehicleCount::getDownCount))
.where(VehicleCount::getRoadId).eq(roadId)
.and(VehicleCount::getDatetime).between(startTime, endTime)
.one();
System.out.println(Thread.currentThread());
vehicleCount.setRoadId(roadId);
return CompletableFuture.completedFuture(vehicleCount);
}
}
- VehicleCountServiceImpl
异步调用Mapper层封装好的selectCount
方法
public class VehicleCountServiceImpl{
........
@Override
public List<GetMapTopCountVo> selectMapTopCount(GetMapTopCountDto mapTopCountDto) {
......
Map<Long, Long> unSortMap = new HashMap<>();
List<Future<VehicleCount>> futureList = new ArrayList<>(roadIdList.size());
for (long roadId : roadIdList) {
Future<VehicleCount> vehicleCountFuture = mapper.selectCount(roadId, startTime, endTime);
futureList.add(vehicleCountFuture);
}
for (Future<VehicleCount> future : futureList) {
while (true) {
try {
if (future.isDone() && !future.isCancelled()) {
VehicleCount vehicleCount = future.get();
long upCount = ObjUtil.isNotEmpty(vehicleCount) ? vehicleCount.getUpCount() : 0L;
long downCount = ObjUtil.isNotEmpty(vehicleCount) ? vehicleCount.getDownCount() : 0L;
unSortMap.put(vehicleCount.getRoadId(), upCount + downCount);
break;
}
Thread.sleep(1);
} catch (InterruptedException | ExecutionException e){
throw new RuntimeException(e);
}
}
}
......
}
......
}