1、需求概述

用户在UI界面上点击全部导出按钮,就能导出所有商品数据,但导出的记录条数可能是200万。

面临问题:

1、如果同步导数据,接口很容易超时。

2、如果把所有数据一次性装载到内存,很容易引起OOM。

3、数据量太大sql语句必定很慢。

4、相同商品编号的数据要放到一起。

5、如果走异步,如何通知用户导出结果?

6、如果excel文件太大,目标用户打不开怎么办?

2、异步处理

一个MySQL百万数据级别的excel导出功能,如果接口同步导出,接口肯定会非常容易超时。
因此,我们在做系统设计的时候,第一选择应该是接口走异步处理。
说起异步处理,其实有很多种,比如:开启一个线程、使用线程池、使用job、使用mq等。
为了防止服务重启时数据的丢失问题,我们大多数情况下,会使用job或者mq来实现异步功能。

2.1、使用Job

1、增加一张执行任务表,记录每次的导出任务。
2、点击全部导出操作,向表中写入一条记录,记录的状态为待执行。
3、定时任务扫描一次执行任务表,查出所有状态是待执行的记录,然后遍历这些记录执行。
4、如果用job的话,要避免重复执行的情况。比如job每隔5分钟执行一次,但如果数据导出的功能所花费的时间超过了5分钟,在一个job周期内执行不完,就会被下一个job执行周期执行。所以使用job时可能会出现重复执行的情况。为了防止job重复执行的情况,该执行任务需要增加一个执行中的状态。状态变化如下:

执行任务被刚记录到执行任务表,是待执行状态。
当job第一次执行该执行任务时,该记录再数据库中的状态改为执行中。
当job跑完了,该记录的状态变成完成或失败。

这样导出数据的功能,在第一个job周期内执行不完,在第二次job执行时,查询待处理状态,并不会查询出执行中状态的数据,也就是说不会重复执行。此外,使用job还有一个硬伤即:它不是立马执行的,有一定的延迟。如果对时间不太敏感的业务场景,可以考虑使用该方案。

2.2、使用MQ

用户点击全部导出按钮,会调用一个后端接口,该接口会向mq服务端,发送一条mq消息。
有个专门的mq消费者,消费该消息,然后就可以实现excel的数据导出了。
相较于job方案,使用mq方案的话,实时性更好一些。
对于mq消费者处理失败的情况,可以增加补偿机制,自动发起重试。
RocketMQ自带了失败重试功能,如果失败次数超过了一定的阀值,则会将该消息自动放入死信队列。

3、使用Eesyexcel

我们知道在Java中解析和生成Excel,比较有名的框架有Apache POI和jxl。

但它们都存在一个严重的问题就是:非常耗内存,POI有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。

百万级别的excel数据导出功能,如果使用传统的Apache POI框架去处理,可能会消耗很大的内存,容易引发OOM问题。

而easyexcel重写了POI对07版Excel的解析,之前一个3M的excel用POI sax解析,需要100M左右内存,如果改用easyexcel可以降低到几M,并且再大的Excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。

需要在maven的pom.xml文件中引入easyexcel的jar包:

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.2</version>
</dependency>
  • 读excel数据非常方便
@Test
public void simpleRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
  • 写excel数据也非常方便
 @Test
public void simpleWrite() {String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}

easyexcel能大大减少占用内存的主要原因是:在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

关于Easyexcel | Easy Excel

4、分页查询

百万级别的数据,从数据库一次性查询出来,是一件非常耗时的工作。
即使我们可以从数据库中一次性查询出所有数据,没出现连接超时问题,这么多的数据全部加载到应用服务的内存中,也有可能会导致应用服务出现OOM问题。
因此,我们从数据库中查询数据时,有必要使用分页查询。比如:每页5000条记录,分为200页查询。

public Page<User> searchUser(SearchModel searchModel) {List<User> userList = userMapper.searchUser(searchModel);Page<User> pageResponse = Page.create(userList, searchModel);pageResponse.setTotal(userMapper.searchUserCount(searchModel));return pageResponse;
}

每页大小pageSize和页码pageNo,是SearchModel类中的成员变量,在创建searchModel对象时,可以设置设置这两个参数。

然后在Mybatis的sql文件中,通过limit语句实现分页功能:

limit #{pageStart}, #{pageSize}

其中的pagetStart参数,是通过pageNo和pageSize动态计算出来的,比如:

pageStart = (pageNo - 1) * pageSize;

5、多个Sheet

我们知道,excel对一个sheet存放的最大数据量,是有做限制的,一个sheet最多可以保存1048576行数据。否则在保存数据时会直接报错:

invalid row number (1048576) outside allowable range (0..1048575)

​6、计算limit的起始位置

limit语句实现分页功能,其中的pagetStart参数,是通过pageNo和pageSize动态计算出来的

limit #{pageStart}, #{pageSize}
pageStart = (pageNo - 1) * pageSize;

如果只有一个sheet可以这么玩,但如果有多个sheet就会有问题。因此,我们需要重新计算limit的起始位置。

ExcelWriter excelWriter = EasyExcelFactory.write(out).build();
int totalPage = searchUserTotalPage(searchModel);if(totalPage > 0) {Page<User> page = Page.create(searchModel);int sheet = (totalPage % maxSheetCount == 0) ? totalPage / maxSheetCount: (totalPage / maxSheetCount) + 1;for(int i=0;i<sheet;i++) {WriterSheet writeSheet = buildSheet(i,"sheet"+i);int startPageNo = i*(maxSheetCount/pageSize)+1;int endPageNo = (i+1)*(maxSheetCount/pageSize);while(page.getPageNo()>=startPageNo && page.getPageNo()<=endPageNo) {page = searchUser(searchModel);if(CollectionUtils.isEmpty(page.getList())) {break;}excelWriter.write(page.getList(),writeSheet);page.setPageNo(page.getPageNo()+1);}}
}

Excel导出百万数据相关推荐

  1. excel导出百万数据与进度条展示

    前言 需求:用户在UI界面上选择想要导出的列,然后点击导出按钮,就能导出用户想要的数据. 效果展示 可能会产生的问题 1.如果导出数据量较大,接口很容易造成超时. 2.如果把数据一次性装载到内存里,很 ...

  2. springboot easyexcel导出百万数据优化

    说明 由于某些原因系统jvm内存最大只能给到512,但是要导出百万数据该如何实现呢?传统的一次性导出肯定是不行的 优化 Excel导出基于 springboot , easyexcel 依赖: < ...

  3. 根据实体excel导入导出百万数据,可修改表头名称

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 表格导入导出实现效果展示 根据实体类导出模板 读取表格数据 导出数据为excel 进阶:修改表格导出的列头 controll ...

  4. Java,excel大量百万数据导出优化措施,SXSSFWorkbook流式、分批次导出示例

    在导出百万级的数据时,如果不采用适当的优化措施,确实可能会造成死机和内存崩溃等问题. 为避免这些问题,可以采用以下优化措施: 分批次读取数据:将需要导出的数据分成多个批次进行读取和写入,每次读取部分数 ...

  5. python导出百万数据到excel_使用python将大量数据导出到Excel中的小技巧分享

    使用python将大量数据导出到Excel中的小技巧分享 今天小编就为大家分享一篇使用python将大量数据导出到Excel中的小技巧心得,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看 ...

  6. jconsole 查看 SXSSFWorkbook导出百万数据excel内存使用情况

    模拟代码如下:计划使用SXSSFworkbook导出1000000行,230列数据的excel. import org.apache.poi.xssf.streaming.SXSSFCell; imp ...

  7. Java解决Excel导出大批量数据(附上测试代码)

    记录一次项目中使用POI导出Excel报错的修改方案. 参考了作者:happyljw的文章 JAVA使用POI如何导出百万级别数据,对代码进行封装扩展.由于项目时间太紧,并未对写的代码进行严格测试,遇 ...

  8. python导出百万数据到excel_[宜配屋]听图阁

    (1) 问题描述:为了更好地展示数据,Excel格式的数据文件往往比文本文件更具有优势,但是具体到python中,该如何导出数据到Excel呢?如果碰到需要导出大量数据又该如何操作呢? 本文主要解决以 ...

  9. php 数组导出csv_php导出百万数据到csv

    先看代码: <?phpset_time_limit(0); // 设置超时ini_set('memory_limit', '100M'); // 设置最大使用的内存header("Co ...

最新文章

  1. 微信小程序 文字换行
  2. 思科宣布NB-IoT平台实现商用
  3. c3p0 参数 模糊查询_MySQL模糊查询用法大全(正则、通配符、内置函数等)
  4. EditText控件的基本使用(点击Button按钮,Toast提示EditText中的内容)
  5. 一种简单快捷的 java 热部署方式
  6. python类和对象详解_Python公开课 - 详解面向对象
  7. 关于GNS3占用很大CPU的问题,很大可能对你有用
  8. DirectX API 编程起步 #01 项目设置
  9. NASM汇编语言与计算机系统09-8086实模式的内存分配图
  10. 机器学习案例系列教程——损失函数总结
  11. 解决NVIDIA显卡驱动“没有找到兼容的图形硬件”的问题 [转]
  12. C语言中static的使用
  13. 工具系列之邮件--浅谈工具如何改变你的工作效率
  14. 《缠中说禅108课》29:转折的力度与级别
  15. Win11dns解析状态异常怎么处理?Win11dns解析失败解决方法
  16. 数论复习之费马与欧拉
  17. What is pessimistic locking in Hibernate
  18. linux串口工具 kermit,ubuntu串口工具(minicom、kermit)的使用
  19. 实验三 面向对象(二)
  20. 传统企业转战电商必看(独家视角)

热门文章

  1. 精选2022年大厂高频Java面试真题集锦(含答案),面试一路开挂
  2. walden中的词频计算
  3. ASJ系列剩余电流继电器在农电安全管理中的应用-安科瑞耿敏花
  4. C语言简易班费管理系统
  5. python selenium定位元素方式
  6. 阿里云部署redis
  7. Cesium 四种雷达扫描效果
  8. 微信视频号运营技巧有哪些
  9. ai字体行间距怎么调整_ai字间距怎么调-调整ai字间距的方法 - 河东软件园
  10. Android系统分区简介