系统项目报表导出功能开发

针对大数据导出超时问题,本文介绍了两种优化策略:数据库调优和导出方式改进。数据库调优通过优化提升了查询效率,而导出方式优化采用了任务队列,用户点击导出后,任务被加入队列并在后台多线程处理,最后提供下载链接。这种方式避免了前端等待,提高了系统响应速度。
摘要由CSDN通过智能技术生成

与正常的导出功能不同,应局方要求需要导出一些截至当前的工程采集状态、提交审核状态等字段。该一类字段需要在导出时进行实时统计并且比较耗时。

1. 前期统计方式:单线程全量统计并返回前端

问题:根据当时统计,2000条数据就会超过1min,并且当时没有做数据库调优操作,随着交维系统同步过来的项目数量越来越多,造成

了导出超时的问题

2. 优化

2.1 数据库调优

详见:MySQL数据库优化_Lionel_SSL的博客-CSDN博客_mysql 数据库优化

2.2 导出方式调优

1.改变实时导出并实时返回给前端的方式。采用“任务队列”的形式来实现导出,即用户在前端点击【导出】后,系统提示【任务已加入任务队列,请稍后刷新页面查看】,此时用户可去其他页面进行操作。待后端导出完成后会给出下载连接,用户点击链接下载文件。

(1)前端:提供文件下载列表,实时展示用户的文件导出状态,导出完成后可点击下载
在这里插入图片描述

(2)后端:导出任务队列表 + 定时扫描 + 多线程

​ 导出任务队列表:用户点击导出后,会将用户的导出任务入表,并标识”未开始“

​ 定时扫描:利用Spring Boot提供的注解@Scheduled来开启一个定时任务,每分钟都去扫描任务表,是否有未开始导出的任务,有则进行导出

​ 解决@Scheduled()注解造成的多任务冲突问题解决: 关于使用@Scheduled()注解造成的多任务冲突问题解决_Lionel_SSL的博客-CSDN博客

​ 多线程:将所有的数据交给不同的线程来进行导出,最后再将结果汇总

Java 1.5之后提供了Callable和Future接口,通过它们就可以在任务执行完毕之后得到任务的执行结果

// 多线程处理导出时的数据查询
int threadNum = 4;
int singleSize = idList.size() / threadNum;// 一个线程处理的数据

// 创建线程池,拥有固定数量(此处可继续优化,按照阿里规约,线程池应自己定义)
ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
List<Callable<List<ProjectDetailOutBO>>> tasks = new ArrayList<>();
Callable<List<ProjectDetailOutBO>> task = null;

// 设置每个任务
for (int i = 0; i < 4; i++) {

    int loop = i;
    task = () -> {

        // 分割数组
        List<BigDecimal> subIdList;
        if (loop == 0) {
            subIdList = idList.subList(0, singleSize);
        } else if (loop == 3) {
            subIdList = idList.subList(singleSize * 3, idList.size());
        } else {
            subIdList = idList.subList(singleSize * loop, singleSize * (loop + 1));
        }

        // 定义接收每个线程返回的业务结果集
        List<ProjectDetailOutBO> resList = new ArrayList<>();
        for (BigDecimal id : subIdList) {
            ProjectDetailOutBO tmpBO = getProjectDetail(id);
            resList.add(tmpBO);
        }
        return resList;
    };
    // 添加任务
    tasks.add(task);
}

try {
    // 执行所有的任务并获取结果
    List<Future<List<ProjectDetailOutBO>>> futures = executorService.invokeAll(tasks);
    for (Future<List<ProjectDetailOutBO>> future : futures) {
        List<ProjectDetailOutBO> detail = future.get();
        details.addAll(detail);
    }
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

// 关闭线程池
executorService.shutdown();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值