使用线程池+分页查询大数据用来导出

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、使用步骤
    • 1.线程池配置
    • 2.实现方法
    • 3.获取数据的方法
  • 总结


前言

需求:最近需要做一个大数据的导入导出,数据量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核心数来决定每页查询多少数据,还有些非空逻辑判断什么的。

你可能感兴趣的:(java,jvm,spring,boot)