导出Excel数据量大时间过慢问题 您所在的位置:网站首页 awr报告怎么看出哪个语句产生的数据量大 导出Excel数据量大时间过慢问题

导出Excel数据量大时间过慢问题

2023-08-14 22:09| 来源: 网络整理| 查看: 265

导出Excel数据量大时间过慢问题

最近在工作中遇到一个问题,就是一个普通的导出Excel功能,响应时间居然需要一分多钟,导出的数据量为 10W-15W条

问题发现:

导出Excel无非就三个步骤 1: 查询数据 2: 数据处理 3: 导出数据 通过打日志的方式,发现查询和导出占用的时间很短,大量的时间都用来处理数据了。 查看代码发现,数据处理部分用的for循环处理,那就意味着需要遍历10W+遍,所以这个地方肯定是不可以这么写的

解决思路:

1: 结果集判断,如果结果集数据量较大,则将结果集进行分组 2: 分组后的N组数据 通过CountDownLatch用多线程去并行处理N组数据 3: 汇总N组数据,组装到一个List里 4: 调用导出工具类执行导出方法

代码:

导出Excel方法:

@Log(value = "导出结算明细") @RequestMapping("/export_detail") public void exportDoctorSettleDetail(SjAdsFinanceDoctorSettleDetailQuery detailQuery, HttpServletResponse response) { try { log.info("[BIZ Normal] MSG exportDoctorSettleDetail params:{}", JsonUtil.toString(detailQuery)); //根据筛选条件查询列表信息 List detailList = sjAdsFinanceDoctorSettleDetailService.findDetailListForExport(detailQuery, null); log.info("[BIZ Normal] MSG exportDoctorSettleDetail select end"); if (CollectionUtils.isEmpty(detailList)) { log.error("[BIZ Exception] Exception_MSG Class exportDoctorSettleDetail not find doctorSettleDetail, query={}", detailQuery); } List allDataList = new ArrayList(); //如果导出的数据量过大(目前是大于1000条),则将数据分组多线程进行处理 if (detailList.size() > Constant.EXPORT_DATA_LIMIT) { final CountDownLatch latch = new CountDownLatch(Constant.GROUP_SIZE); // 调用方法将集合分隔为多个小集合 List groupList = groupList(detailList, Constant.GROUP_SIZE); // 此处线程池最好不要每次都新new,用公用的线程池,此处省事我直接new了 ThreadPoolExecutor service = new ThreadPoolExecutor( Constant.GROUP_SIZE, Constant.GROUP_SIZE, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(), new ThreadPoolExecutor.DiscardOldestPolicy()); // 循环groupList,多线程处理每一个小集合数据 for (List list : groupList) { Runnable runnable = new Runnable() { @Override public void run() { try{ // 此处处理小集合的数据,做啥都可以,做你需要处理的事情 List doctorSettleDetailList = list.stream().map((t) -> { return exportDetailToVo(t); }).collect(Collectors.toList()); //将处理好的数据追加到allDataList中 allDataList.addAll(doctorSettleDetailList); }catch (RuntimeException e){ e.getMessage(); }finally { //不管上面执行结果如何,当前线程调用此方法,计数减一 latch.countDown(); } } }; // 嫌麻烦的可以改用lambda写法~ service.execute(runnable); } try { //阻塞当前线程,直到计数器的值为0 //上方的多个线程全部处理完数据前,此处阻塞,知道所有线程都处理完数据 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); }finally { service.shutdown(); } } else { List doctorSettleDetailList = detailList.stream().map(this::exportDetailToVo).collect(Collectors.toList()); allDataList.addAll(doctorSettleDetailList); } // 在这就可以拿到汇总的处理结果数据,如何做最后的数据导出动作了 String fileName = detailQuery.getReportDate() + "_" + detailQuery.getReportDate() + "_结算明细_" + DateUtil.format(new Date(), DateUtil.PATTERN_YEAR2MINUTE); List filedNames = handleManager.get(getCurrentUser().getId(), DrugstoreDoctorSettleDetailForListVO.class, allDataList); filedNames = filedNames.stream().filter(str -> !"doctorAccount".equals(str)).collect(Collectors.toList()); log.info("[BIZ Normal] MSG exportDoctorSettleDetail result:{}", JsonUtil.toString(allDataList)); EasyExcelUtil.exportExcel(response, DrugstoreDoctorSettleDetailForListVO.class, fileName).includeColumnFiledNames(filedNames).sheet(fileName).doWrite(allDataList); log.info("[BIZ Normal] MSG exportDoctorSettleDetail end"); } catch (Exception e) { log.error("[BIZ Exception] Exception_MSG = {}", e.toString()); throw new ServiceError("500","导出结算明细异常"); } }

List分组方法

/** * List分割操作 * * @param sourceList 需要分割的List * @return 分割的list集合 */ public static List groupList(List sourceList, Integer groupNum) { List groupList = new ArrayList(); int total = sourceList.size(); // 计算出余数 int remainder = total % groupNum; // 计算出商 int number = total / groupNum; // 偏移量 int offset = 0; for (int i = 0; i subList = sourceList.subList(i * number + offset, (i + 1) * number + offset + 1); remainder--; offset++; } else { subList = sourceList.subList(i * number + offset, (i + 1) * number + offset); } groupList.add(subList); } return groupList; }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有