情景:
前几天在工作上遇到个优化需求,某个接口速度几乎接近了10s,这会严重影响用户操作感受,在开发上也是无法接受的,于是我开始着手进行了接口优化。
第一步:SQL优化
首先查看代码里面的逻辑优化,感觉问题不大,于是进一步查看具体的Sql,对sql进行优化,这里可以参考我前面写的sql优化场景。在进行了适当的冗余以及增加索引后,此时的Sql已经差不多接近6s,虽然有所进步,但还是远远不够,但详细看代码逻辑部分已经没有可以优化的部分了,此时就可以使用多线程了。
第二步:多线程查询
这个的大体思路是这样的,用一个线程进行查询慢,那我就使用多线程,每个线程处理一部分的数据,最后聚合起来返回,这样查询速率就可以显著提升。核心多线程代码如下:
List<List<Long>> subLists = new ArrayList<>();
// 数据大小
List<Long> ids = buSignatureMapper.selectSignatureCount(ro);
// 批次大小(每个线程要处理数据量)
int batchSize = 20000; // 每个子列表的大小为2万
// 分割List为多个子列表
for (int i = 0; i < ids.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, ids.size());
List<Long> subList = ids.subList(i, endIndex);
subLists.add(subList);
}
Queue<CureListVo> results = new ConcurrentLinkedQueue<>();
if (ObjectUtil.isNotEmpty(subLists)) {
// 使用定长线程池,大小根据实际情况调整,例如数据库连接数、CPU核心数等
int poolSize = Math.min(subLists.size(), Runtime.getRuntime().availableProcessors());
ExecutorService executor = Executors.newFixedThreadPool(poolSize);
CountDownLatch latch = new CountDownLatch(subLists.size());
for (List<Long> subList : subLists) {
// System.out.println(subList.get(0));
// System.out.println(subList.get(subList.size()-1));
CureListRo ro1 = new CureListRo();
BeanUtil.copyProperties(ro,ro1);
ro1.setStartIndex(subList.get(0));
ro1.setEndIndex(subList.get(subList.size()-1));
executor.submit(() -> {
try {
// 查询MySQL表操作
List<CureListVo> cureListVos1 = buSignatureMapper
.selectCureList(ro1);
// 直接添加到ConcurrentLinkedQueue中,它是线程安全的
System.out.println(ro1.getStartIndex()+"到"+ro1.getEndIndex()+"total"+cureListVos1.size());
results.addAll(cureListVos1);
} finally {
// 计数器减一,表示一个任务完成
latch.countDown();
}
});
}
// 阻塞等待所有查询任务完成
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Thread pool interrupted while awaiting termination: {}", e.getMessage());
} finally {
// 关闭线程池
executor.shutdown();
}
}
List<CureListVo> cureListVos = results.stream().sorted(Comparator.comparing(CureListVo::getCureEndTime).reversed()).collect(Collectors.toList());