文章目录

  • 前言
  • 源码
  • 技术栈和代码结构
  • 关键的导出监听器
  • Service 实现
  • 测试

前言

语雀社区写的 easyExcel 确实是目前市面excel 导入导出性能最好的框架,使用简便。社区指导清晰,不需要那么繁杂的代码。
前面写了一篇 EasyExcel3.0.5 解决大数据导入导出,防止OOM。后来,在工作中我让一个同事参照我的去实现,他请教了他们组长,给了点查询优化建议,确实值得改善,这给了我一些启发,今天分享一下。

影响速度很关键的是网络,其次是,数据表的索引优化,第三是我们自己写查询语句时做优化,话不多说,上干货。

源码

链接:https://pan.baidu.com/s/1Ixp1o-fjyO2zioB8efb9LQ?pwd=1234
提取码:1234

技术栈和代码结构

沿用上一篇的技术栈和代码结构,详情请移步 EasyExcel3.0.5 解决大数据导入导出,防止OOM 。

关键的导出监听器

ExportListener.java

package cn.com.easyExcel.excel.listener;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.util.CollectionUtils;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;public class ExportListener<T> {private BaseMapper<T> baseMapper;public ExportListener(BaseMapper<T> baseMapper) {this.baseMapper = baseMapper;}private static final String DATA_FORMAT = "yyyy-MM-dd-HH-mm-ss";private static final String CHARACTER_UTF_8 = "UTF-8";private static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";private static final String CONTENT_DISPOSITION = "Content-Disposition";private static final String ACCESS_CONTROL_EXPOSE = "Access-Control-Expose-Headers";private static final int PAGE_SIZE = 10000;public void exportExcel(HttpServletResponse response, String sheetName, Class<T> pojoClass,LambdaQueryWrapper<T> queryWrapper) throws IOException {ServletOutputStream out = getServletOutputStream(response, sheetName);// 这里 需要指定写用哪个class去写ExcelWriter excelWriter = EasyExcel.write(out, pojoClass).build();// 这里注意 如果同一个sheet只要创建一次WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();int totalCount = Math.toIntExact(baseMapper.selectCount(queryWrapper));int pageNumber = (int) Math.ceil((double) totalCount / (double) PAGE_SIZE);    //分页条数看情况// 去调用写入,根据数据库分页的总的页数来for (int i = 1; i <= pageNumber; i++) {//先定义一个空集合每次循环使他变成null减少内存的占用List<T> recordList = new ArrayList<>();Page<T> page = new Page<>(i, PAGE_SIZE);Page<T> pojoIPage = baseMapper.selectPage(page, queryWrapper);recordList = pojoIPage.getRecords();excelWriter.write(recordList , writeSheet);recordList.clear();}// 千万别忘记finish 会帮忙关闭流excelWriter.finish();out.flush();}/*** 查询优化的方法*/public void exportNoQueryCount(HttpServletResponse response, String sheetName, Class<T> pojoClass,LambdaQueryWrapper<T> queryWrapper) throws IOException {ServletOutputStream out = getServletOutputStream(response, sheetName);ExcelWriter excelWriter = EasyExcel.write(out, pojoClass).build();WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();int startIndex = 1;while (true){int startParam =(startIndex - 1) * PAGE_SIZE;int pageNumber = (int) Math.ceil((double) startParam / (double) PAGE_SIZE+1);Page<T> page = new Page<>(pageNumber, PAGE_SIZE, false);Page<T> pojoIPage = baseMapper.selectPage(page, queryWrapper);List<T> recordList = pojoIPage.getRecords();if (CollectionUtils.isEmpty(recordList)) {break;}startIndex++;excelWriter.write(recordList , writeSheet);}// 千万别忘记finish 会帮忙关闭流excelWriter.finish();}public static ServletOutputStream getServletOutputStream(HttpServletResponse response, String sheetName) throws IOException {DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATA_FORMAT);String nowTime = formatter.format(LocalDateTime.now());String fileName = sheetName.concat("_").concat(nowTime).concat(".xlsx");response.setContentType(CONTENT_TYPE);//设置字符集为utf-8response.setCharacterEncoding(CHARACTER_UTF_8);//用postman测正常,浏览器多了filename_=utf-8等字样
//        response.setHeader(CONTENT_DISPOSITION,
//                "attachment;filename=" + URLEncoder.encode(fileName, CHARACTER_UTF_8)
//                        + ";filename*=utf-8''" + URLEncoder.encode(fileName, CHARACTER_UTF_8));response.setHeader(ACCESS_CONTROL_EXPOSE, CONTENT_DISPOSITION); //使前端可以获取文件名解码转成中文//postman测会乱码,但浏览器下载就正常,真实开发是用这个,前端需要解码,文件名才能看到中文response.setHeader(CONTENT_DISPOSITION,"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));return response.getOutputStream();}
}

Service 实现

package cn.com.easyExcel.service.impl;import cn.com.easyExcel.excel.listener.ExportListener;
import cn.com.easyExcel.excel.listener.ImportListener;
import cn.com.easyExcel.mapper.EmployeeMapper;
import cn.com.easyExcel.pojo.EmployeeExporter;
import cn.com.easyExcel.service.EmployeeService;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;@Slf4j
@Service
public class EmployeeServiceImpl implements EmployeeService {@Autowiredprivate EmployeeMapper employeeMapper;@Overridepublic void initData() {long beforeTime = System.currentTimeMillis();List<EmployeeExporter> employees = new ArrayList<EmployeeExporter>();for (int i = 0; i < 200000; i++) {EmployeeExporter employee = new EmployeeExporter();employee.setUserName(getRandomName());employee.setGender(getRandomGender());employee.setAge(getRandomAge());employee.setMaritalStatus(getRandomGender());employee.setEducation(getRandomEducation());employee.setMobile("18866998888");employee.setDepartmentName(getRandomDP());employee.setNationalArea("中国");employee.setCity("深圳");employees.add(employee);if(employees.size() % 1000 == 0){employeeMapper.batchInsert(employees);employees.clear();}}long afterTime = System.currentTimeMillis();log.info("耗时:{}", afterTime - beforeTime);}@Overridepublic void importExcel(MultipartFile file) throws IOException {long beforeTime = System.currentTimeMillis();EasyExcel.read(file.getInputStream(),EmployeeExporter.class,new ImportListener(employeeMapper)).sheet().headRowNumber(1).doRead();long afterTime = System.currentTimeMillis();log.info("耗时:{}", afterTime - beforeTime);}@Overridepublic void exportExcel(HttpServletResponse response) throws IOException {long beforeTime = System.currentTimeMillis();LambdaQueryWrapper<EmployeeExporter> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.gt(EmployeeExporter::getAge, 22);queryWrapper.between(EmployeeExporter::getEducation, "1", "3");new ExportListener<>(employeeMapper).exportExcel(response, "员工信息", EmployeeExporter.class,queryWrapper);long afterTime = System.currentTimeMillis();log.info("耗时:{}", afterTime - beforeTime);}@Overridepublic void exportExcelNoQueryCount(HttpServletResponse response) throws IOException {long beforeTime = System.currentTimeMillis();LambdaQueryWrapper<EmployeeExporter> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.between(EmployeeExporter::getAge, "25", "30");new ExportListener<>(employeeMapper).exportNoQueryCount(response, "员工信息", EmployeeExporter.class,queryWrapper);long afterTime = System.currentTimeMillis();log.info("导出查询耗时33:{}", afterTime - beforeTime);}/*** 随机取名字* @return*/public String getRandomName(){String[] doc = {"朝歌晚酒", "都怪时光太动听", "笑我孤陋", "水墨青花","时光清浅", "草帽撸夫", "江山如画","热度不够", "盏茶浅抿", "把酒临风", "且听风吟", "梦忆笙歌", "倾城月下", "清风墨竹", "自愈心暖", "几许轻唱","平凡之路", "半夏倾城", "南栀倾寒", "孤君独战", "温酒杯暖", "眉目亦如画", "旧雪烹茶", "律断华章", "清酒暖风","清羽墨安", "一夕夙愿", "南顾春衫", "和云相伴", "夕颜若雪", "时城旧巷", "梦屿千寻", "故港笑别", "水袖萦香","秋水墨凉", "海棠花瘦", "千城暮雪", "华灯初上", "一纸枕书", "剑断青丝", "风烟影月", "日月星辰", "浅喜深爱"};int index = (int) (Math.random() * doc.length);return doc[index];}/*** 性别随机* @return*/public String getRandomGender(){String[] doc = {"0", "1"};int index = (int) (Math.random() * doc.length);return doc[index];}/*** 年龄随机* @return*/public int getRandomAge(){int[] doc = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};int index = (int) (Math.random() * doc.length);return doc[index];}public String getRandomEducation(){String[] doc = {"0", "1", "2", "3"};int index = (int) (Math.random() * doc.length);return doc[index];}public String getRandomDP(){String[] doc = {"行政部", "财务部", "技术部", "市场部", "公关部"};int index = (int) (Math.random() * doc.length);return doc[index];}
}

测试

我用 20万数据区测试,摈除家庭网速一般的因素,用第一个没有改造的方法去导出,平均耗时70+ 秒,用第二种改造的方法平均耗时 30秒左右。 这还是在我的网速一般的情况,而且我没有做什么索引优化。
exportExcel 方法测试的结果:

exportNoQueryCount 方法测试的结果:

好了,分享结束。 不整噱头,不标题党(什么史上最强/最全/最好,看这篇足够了……,标题党的内容八成都是很水的抄袭),认真分享,认真探索,觉得有用,欢迎留言。

EasyExcel3.0.5 加快大数据查询速度,查询性能优化相关推荐

  1. EasyExcel3.0.5 解决大数据导入导出,防止OOM

    文章目录 前言 源码 代码实现 POM 依赖 application.yaml Application 启动类 Config 相关配置类 创建员工信息表 导入和导出实体 Controller 层 Se ...

  2. 大数据存储系统I/O性能优化技术研究进展

    大数据存储系统I/O性能优化技术研究进展 肖利民,霍志胜 北京航空航天大学计算机学院,北京 100191 摘要:大数据存储系统的I/O性能是影响大数据应用整体性能的关键因素之一,总结了当前在存储系统架 ...

  3. AntDB 落地某省电信大数据中心项目的性能优化案例分享

    亚信科技AntDB 落地某省电信大数据中心项目的性能优化案例分享 某省电信大数据中心项目采购了一套亚信科技AntDB 3.1分布式数据库,2018年8月初开始建设,建设周期一个月.9月份投入运行后,至 ...

  4. redis 亿级查询速度_Redis 性能优化的 13 条军规!史上最全

    Redis性能优化实战方案 Redis 是基于单线程模型实现的,也就是 Redis 是使用一个线程来处理所有的客户端请求的,尽管 Redis 使用了非阻塞式 IO,并且对各种命令都做了优化(大部分命令 ...

  5. eBay 大数据平台的 HDFS 性能优化实践

    导读 HDFS作为大数据的底层存储系统,其性能处理效率关乎着大量与集群数据相关的计算任务的运行.HDFS的性能效率主要由其内部的核心服务NameNode所决定.此次eBay Hadoop team将分 ...

  6. iMobile中加载大数据量的矢量数据性能优化方法有哪些

    作者:xinxin 随着移动技术的发展,GIS行业中移动项目越来越多.在移动应用中不仅要对接在线的服务数据,还要加载各种本地的业务数据,GIS数据的量一般很大,而移动设备的内存有限,加载本地大数据量的 ...

  7. kettle大数据量读写mysql性能优化

     修改kettleDB连接设置 1. 增加批量写的速度: useServerPrepStmts=false   rewriteBatchedStatements=true   useCompressi ...

  8. 千万级别数据查询优化_MySQL大数据量分页查询方法及其优化

    MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适 ...

  9. mysql一样的查询在我本地很快但是线上很慢_MySQL大数据量分页查询方法及其优化...

    MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适 ...

最新文章

  1. android opengl es 绘制余弦曲线,Android OpenGL ES - 绘制线、面
  2. rest_framework之解析器详解 05
  3. 省选专练(学习)AC自动机
  4. 运营商视角的物联网商业模式-物联网操作系统篇
  5. win8系统关闭共享服务器,Windows8系统关闭Windows Media Player网络共享服务的方法
  6. javase学习第10天(形式参数和返回值类型、包、内部类、匿名内部类)
  7. python scrapy教程实例_Python之scrapy实例1
  8. Linux的实际操作:时间日期类的实用指令(date cal)
  9. java 方法 示例_Java语言环境getDisplayCountry()方法与示例
  10. 数博会重磅活动:第三届大数据科学与工程国际会议日程
  11. USACO-Section1.4 Wormholes(枚举法)
  12. Matlab多个Figure图合成一个Fig
  13. pytorch 入门学习处理多维特征输入-7
  14. TOGAF9.2 第I部分 第1章简介
  15. imei服务器清除id_苹果绕过ID解锁
  16. 微信小程序弹窗有输入框且可以使用名文和密文输入
  17. nginx php 无法输出图片大小,Nginx+PHP实时生成不同尺寸图片
  18. .net 基于wkhtmltopdf插件,Rotativa包在后端根据html代码生成pdf文件
  19. DOS里的NET命令用法
  20. swf文件转换其他视频格式工具(例:swf to mp4) ,转换后的视频无水印

热门文章

  1. picsart下载_PicsArt美易绘画
  2. python3打包成apk_ionic3 从创建到打包成apk
  3. TX9118 同步升压IC
  4. A类、B类、AB类、C类、D类五种功率…
  5. 浅谈Joomla网站优化
  6. 医院信息管理云平台源码 云HIS系统源码 4级电子病历系统
  7. SPEC2006详细参数和测试过程常见问题处理总结(附实例操作)
  8. 怎样用ASP代码获取网页源代码
  9. u盘工作表在计算机上打不开,U盘中无法开启的excel文件的5种修复方法
  10. linux进程控制命令行,linux命令行学习(37):控制进程的方法