导出任务耗时怎么优化?

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS AI 助手,专业版
简介: 当处理大量数据的导入导出时,需避免长时间阻塞用户界面。推荐采用异步任务处理方式,提交任务后后台线程执行数据处理。对于导出功能,设计前端界面包括“导出”与“导出记录”按钮;导出记录包含批次号、时间、导出URL等字段。后端生成Excel文件并上传至服务器,记录URL以便下载。导入功能类似,记录批次号、总条数、成功条数等信息。为避免大量数据查询导致内存溢出或系统响应缓慢,应使用分批处理策略,例如分页查询来减轻MySQL内存负担。提供了Java工具类实现分页查询和处理逻辑

大量数据的导入导出时,请求一定非常耗时,页面一定会不停转圈圈,不可能让用户一直停留在这个页面转圈圈,这样并不友好。

比较好的方式就事通过异步的方式,先提交任务,然后通过线程的处理数据。一次性如果导出大量数据时,需要批量查询结果到处。

导出功能设计:

前端页面设计如下: 新增 导出按钮 和导出记录按钮 导出记录页面字段如下: 批次号  时间  导出URL  操作(导出) 后端表结构

sql

代码解读

复制代码

create table export_record(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
 `batch_no` varchar(32)  DEFAULT NULL COMMENT '导入批次号',
 `export_type` varchar(3) DEFAULT NULL COMMENT '类型(1:订单导出)',
  `export_url` varchar(300) DEFAULT NULL COMMENT 'url',
 `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
  `create_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人代码',
  `create_name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人名称',
  `last_update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
  `last_update_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后更新人',
  `last_update_name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后更新人',
   PRIMARY KEY (`id`),
   KEY `index_import_record` (`batch_no`) USING BTREE
)ENGINE=InnoDB  COMMENT='导出记录';

后端功能逻辑: 将导出的数据生成excel文件,并上传到服务器中,上传的文件生产的url保存记录,供前端页面下载excel文件

导入功能设计

前端页面设计如下:导入记录页面字段如下: 批次号  时间  总条数 成功条数 操作

sql

代码解读

复制代码

create table import_record (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
 `batch_no` varchar(32)  DEFAULT NULL COMMENT '导入批次号',
 `export_type` varchar(3) DEFAULT NULL COMMENT '类型(1:订单导出)',
  `total_num` int DEFAULT 0 COMMENT '总数量',
  `success_num` int DEFAULT 0 COMMENT '成功数量',
 `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
  `create_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人代码',
  `create_name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人名称',
  `last_update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
  `last_update_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后更新人',
  `last_update_name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后更新人',
   PRIMARY KEY (`id`),
   KEY `index_import_record` (`batch_no`) USING BTREE
)ENGINE=InnoDB  COMMENT='导入记录';


create table import_record (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
 `batch_no` varchar(32)  DEFAULT NULL COMMENT '导入批次号',
 `export_type` varchar(3) DEFAULT NULL COMMENT '类型(1:订单导出)',
  `total_num` int DEFAULT 0 COMMENT '总数量',
  `success_num` int DEFAULT 0 COMMENT '成功数量',
 `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
  `create_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人代码',
  `create_name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人名称',
  `last_update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
  `last_update_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后更新人',
  `last_update_name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后更新人',
   PRIMARY KEY (`id`),
   KEY `index_import_record` (`batch_no`) USING BTREE
)ENGINE=InnoDB  COMMENT='导入记录';

导入逻辑: 对导入的excel文件进行解析,并保存解析出来的数据。

大量数据查询拆分成批量任务查询

导出数据可能会导出大量数据,通常情况下,一次性查询大量数据导致负载压力的原因是在一次查询中同时检索了太多数据,并在内存中进行处理,这会占用大量系统资源,造成系统响应变慢和崩溃等问题。

mysql会将检索出来的数据都缓存到内存,一次性返回到服务端,这样会占用大量的内存资源,导致内存不足,从而影响系统运行稳定性。

解决的方式是批次处理,如分页查询数据,从而减少mysql查询占用的内存。

分页查询工具如下:

java

代码解读

复制代码

@CustomLog
public class PageBigDataUtil {

    /**
     * @param queryParam 查询条件
     * @param function 分页查询
     * @return
     */
    public static <T> List<T> pageBigData(T queryParam, Function<PageQueryBean<T>, PageQueryBean<T>> function) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        List<T> results = new ArrayList<>();
        int size = 500;
        int current = 1;
        for (; ; ) {
            PageQueryBean<T> pageParam = new PageQueryBean<>();
            SimplePage<T> simplePage = new SimplePage<>();
            simplePage.setPageSize(size);
            simplePage.setPageNum(current);
            pageParam.setPage(simplePage);
            pageParam.setParameter(queryParam);
            PageQueryBean<T> result = function.apply(pageParam);
            logger.keyword("分页批次任务").info("--------导入开始,本批次共:{} 轮,当前第{}轮", result.getPage().getPages(), current);
            if (DataUtil.isEmpty(result)) {
                break;
            }
            results.addAll(result.getPage().getList());
            if ((long) size * current >= result.getPage().getTotal()) {
                break;
            }
            current++;
        }
        stopWatch.stop();
        logger.info("批次任务已结束,分页批次任务:{},获取总记录数:{},总耗时:{}秒", current, results.size(), stopWatch.getTotalTimeSeconds());
        return results;
    }

    /**
     *
     * @param queryParam 查询条件
     * @param function 分页查询
     * @param consumer 批次消费
     * @param <T>
     */
    public static <T> void handleBigData(T queryParam, Function<PageQueryBean<T>, PageQueryBean<T>> function, Consumer<List<T>> consumer) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        int size = 500;
        int current = 1;
        for (; ; ) {
            PageQueryBean<T> pageParam = new PageQueryBean<>();
            SimplePage<T> simplePage = new SimplePage<>();
            simplePage.setPageSize(size);
            simplePage.setPageNum(current);
            pageParam.setPage(simplePage);
            pageParam.setParameter(queryParam);
            PageQueryBean<T> result = function.apply(pageParam);
            if (DataUtil.isEmpty(result)) {
                break;
            }
            // 业务处理
            consumer.accept(result.getPage().getList());
            logger.keyword("分页批次任务").info("--------导入开始,本批次共:{} 轮,当前第{}轮", result.getPage().getPages(), current);
            if ((long) size * current >= result.getPage().getTotal()) {
                break;
            }
            current++;
        }
        stopWatch.stop();
        logger.info("批次任务已结束,总耗时:{}秒", stopWatch.getTotalTimeSeconds());
    }
}


转载来源:https://juejin.cn/post/7367975773519200307

相关文章
|
JavaScript 前端开发 编译器
看完这篇文章,不再害怕Vue3的源码(一)
看完这篇文章,不再害怕Vue3的源码
|
存储 Unix Linux
CentOS7下日志轮转logrotate简单入门与实践
CentOS7下日志轮转logrotate简单入门与实践
852 0
CentOS7下日志轮转logrotate简单入门与实践
|
7月前
|
人工智能 安全 数据可视化
深度解析三大AI协议:MCP、ACP与A2A,看懂智能代理的通信法则
在AI代理技术快速发展的背景下,MCP、ACP和A2A三大协议成为推动AI生态协作的关键标准。MCP(模型上下文协议)为大模型提供标准化信息接口,提升AI处理外部数据的效率;ACP(代理通信协议)专注于边缘设备间的低延迟通信,实现本地系统的高效协同;A2A(代理对代理协议)则构建跨平台通信标准,打通不同AI系统的协作壁垒。三者各司其职,共同推动AI从独立工具向智能协作团队演进,提升整体智能化水平与应用灵活性。
3275 2
深度解析三大AI协议:MCP、ACP与A2A,看懂智能代理的通信法则
|
8月前
|
SQL XML Java
通过MyBatis的XML配置实现灵活的动态SQL查询
总结而言,通过MyBatis的XML配置实现灵活的动态SQL查询,可以让开发者以声明式的方式构建SQL语句,既保证了SQL操作的灵活性,又简化了代码的复杂度。这种方式可以显著提高数据库操作的效率和代码的可维护性。
474 18
|
监控 Java easyexcel
面试官:POI大量数据读取内存溢出?如何解决?
【10月更文挑战第14天】 在处理大量数据时,使用Apache POI库读取Excel文件可能会导致内存溢出的问题。这是因为POI在读取Excel文件时,会将整个文档加载到内存中,如果文件过大,就会消耗大量内存。以下是一些解决这一问题的策略:
2085 1
|
自然语言处理 关系型数据库 MySQL
MySQL MATCH 匹配中文 无法查询的问题如何处理?
【8月更文挑战第29天】MySQL MATCH 匹配中文 无法查询的问题如何处理?
735 7
|
Java API Spring
集成EasyPoi(一个基于POI的Excel导入导出工具)到Spring Boot项目中
集成EasyPoi(一个基于POI的Excel导入导出工具)到Spring Boot项目中
1505 1
|
缓存 Java 测试技术
Java性能优化(八)-多线程调优-线程池大小设置
Java性能优化(八)-多线程调优-线程池大小设置
602 0
|
Java Spring 容器
同一接口有多个实现类,怎么来注入一个指定的实现?@Resource、@Autowired、@Qualifier
同一接口有多个实现类,怎么来注入一个指定的实现?@Resource、@Autowired、@Qualifier
1205 0
|
数据采集 Web App开发 前端开发
前端自动化UI测试的完整方案
前端自动化UI测试的完整方案
1680 0