大量数据的导入导出时,请求一定非常耗时,页面一定会不停转圈圈,不可能让用户一直停留在这个页面转圈圈,这样并不友好。
比较好的方式就事通过异步的方式,先提交任务,然后通过线程的处理数据。一次性如果导出大量数据时,需要批量查询结果到处。
导出功能设计:
前端页面设计如下: 新增 导出按钮 和导出记录按钮 导出记录页面字段如下: 批次号 时间 导出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());
}
}