提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
需求:最近需要做一个大数据的导入导出,数据量21w条30个字段。下面主要记录一次做导出时大数据的查询。
思路:如果一次数据库查询太多数据,jvm就会内存溢出,所以我使用分页每次查询1万条数据,使用for循环,再把结果合并到一起。但是这种串行方式是阻塞的,查询21w数据大约需要15秒,然后就通过线程池用多线程查询,21w条数据用时2秒多。
提示:以下是本篇文章正文内容,下面案例可供参考
一、使用步骤
1.线程池配置
代码如下(示例):
@Configuration(proxyBeanMethods = false)
@EnableAsync
public class AsyncTaskPoolConfig {
private static Logger LOGGER = LoggerFactory.getLogger(AsyncTaskPoolConfig.class);
@Bean(name = "taskExecuter")
public Executor taskExecutor(){
int i = Runtime.getRuntime().availableProcessors();
LOGGER.info("系统最大线程数:" + i);
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(i);
taskExecutor.setMaxPoolSize(i*3);
taskExecutor.setQueueCapacity(99999);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("taskExecutor--");
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
return taskExecutor;
}
}
2.实现方法
代码如下(示例):
@Override
public ServiceResponse queryExcel(ZdXyDictNew test){
long startTime = System.currentTimeMillis();
System.out.println("开始获取数据...");
//先查询出数据总条数
Integer sunNum = zdXyDictNewMapper.queryDataCount(new ZdXyDictNew());
if(sunNum > 0){
int countNum = 0;
//每次查询条数
int maxNum = 10000;
//用于最终合并Future.get()数据
List<List<ZdXyDictNew>> sumList = new ArrayList<>();
//如果数据总条数正好是1w的倍数,直接除获取要循环的次数,否则就+1次
if(sunNum%maxNum == 0){
countNum = sunNum/maxNum;
}else {
countNum = (sunNum/maxNum) + 1;
}
//用于暂时存储线程运行结果
List<Future<List<ZdXyDictNew>>> tasks = new ArrayList<>();
for (int i = 0; i < countNum; i++) {
ZdXyDictNew zdXyDictNew = new ZdXyDictNew();
zdXyDictNew.setStartnum(i*maxNum);
zdXyDictNew.setEndnum((i+1)*maxNum);
//调用加上@Async注解的异步方法(获取数据的方法)
Future<List<ZdXyDictNew>> zdXyDictNews = testService.queryData(zdXyDictNew);
//先存入list,切记不要再此处直接future.get(),因为此方法是阻塞的
tasks.add(zdXyDictNews);
}
try {
//等for循环结束再future.get()取出数据并合并
if (tasks.size() > 0) {
for (Future<List<ZdXyDictNew>> future : tasks) {
sumList.add(future.get());
}
}
}catch (Exception e){
return ServiceResponse.error(e.getMessage());
}
long endTime = System.currentTimeMillis();
System.out.println("获取数据耗时:"+(endTime-startTime)+"ms");
return ServiceResponse.ok(sumList);
}
return ServiceResponse.error("无数据!");
}
3.获取数据的方法
代码如下(示例):
@Override
@Async(value = "taskExecuter")
public Future<List<ZdXyDictNew>> queryData(ZdXyDictNew test){
LOGGER.info("线程:"+Thread.currentThread().getName()+"开始读取数据");
List<ZdXyDictNew> zdXyDictNews = zdXyDictNewMapper.queryDataPage(test);
return new AsyncResult<>(zdXyDictNews);
}
总结
如果要正式使用,可以对上面代码进行优化,比如可以把获取分页次数,每页数据什么的封装成工具类,或者是根据jvm核心数来决定每页查询多少数据,还有些非空逻辑判断什么的。