java使用导出百万级别数据

news2024/11/15 7:07:21

用过POI的人都知道,在POI以前的版本中并不支持大数据量的处理,如果数据量过多还会常报OOM错误, 这时候调整JVM的配置参数也不是一个好对策(注:jdk在32位系统中支持的内存不能超过2个G,而在64位中没有限制,但是在64位的系统中,性能并不是太好),好在POI3.8版本新出来了一个SXSSFWorkbook对象,它就是用来解决大数据量以及超大数据量的导入导出操作的,但是SXSSFWorkbook只支持.xlsx格式,不支持.xls格式的Excel文件。

这里普及一下,在POI中使用HSSF对象时,excel 2003最多只允许存储65536条数据,一般用来处理较少的数据量,这时对于百万级别数据,Excel肯定容纳不了,而且在计算机性能稍低的机器上测试,就很容易导致堆溢出。而当我升级到XSSF对象时,它可以直接支持excel2007以上版本,因为它采用ooxml格式。这时excel可以支持1048576条数据,单个sheet表就支持近104万条数据了,虽然这时导出100万数据能满足要求,但使用XSSF测试后发现偶尔还是会发生堆溢出,所以也不适合百万数据的导出。

现在我们知道excel2007及以上版本可以轻松实现存储百万级别的数据,但是系统中的大量数据是如何能够快速准确的导入到excel中这好像是个难题,对于一般的web系统,我们为了解决成本,基本都是使用的入门级web服务器tomcat,既然我们不推荐调整JVM的大小,那我们就要针对我们的代码来解决我们要解决的问题。在POI3.8之后新增加了一个类,SXSSFWorkbook,采用当数据加工时不是类似前面版本的对象,它可以控制excel数据占用的内存,他通过控制在内存中的行数来实现资源管理,即当创建对象超过了设定的行数,它会自动刷新内存,将数据写入文件,这样导致打印时,占用的CPU,和内存很少。但有人会说了,我用过这个类啊,他好像并不能完全解决,当数据量超过一定量后还是会内存溢出的,而且时间还很长。对你只是用了这个类,但是你并没有针对你的需求进行相应的设计,仅仅是用了,所以接下来我要说的问题就是,如何通过SXSSFWorkbook以及相应的写入设计来实现百万级别的数据快速写入。

我先举个例子,以前我们数据库中存在大量的数据,我们要查询,怎么办?我们在没有经过设计的时候是这样来处理的,先写一个集合,然后执行jdbc,将返回的结果赋值给list,然后再返回到页面上,但是当数据量大的时候,就会出现数据无法返回,内存溢出的情况,于是我们在有限的时间和空间下,通过分页将数据一页一页的显示出来,这样可以避免了大数据量数据对内存的占用,也提高了用户的体验,在我们要导出的百万数据也是一个道理,内存突发性占用,我们可以限制导出数据所占用的内存,这里我先建立一个list容器,list中开辟10000行的存储空间,每次存储10000行,用完了将内容清空,然后重复利用,这样就可以有效控制内存,所以我们的设计思路就基本形成了,所以分页数据导出共有以下3个步骤:

  1. 求数据库中待导出数据的行数

  2. 根据行数求数据提取次数

  3. 按次数将数据写入文件

通过以上步骤在效率和用户体验性上都有了很高的提高,接下来上代码

public void exportBigDataExcel(ValueDataDto valueDataDto, String path)
            throws IOException {
        // 最重要的就是使用SXSSFWorkbook,表示流的方式进行操作
        // 在内存中保持100行,超过100行将被刷新到磁盘
        SXSSFWorkbook wb = new SXSSFWorkbook(100);
        Sheet sh = wb.createSheet(); // 建立新的sheet对象
        Row row = sh.createRow(0);   // 创建第一行对象
        // -----------定义表头-----------
        Cell cel0 = row.createCell(0);
        cel0.setCellValue("1");
        Cell cel2 = row.createCell(1);
        cel2.setCellValue("2");
        Cell cel3 = row.createCell(2);
        cel3.setCellValue("3");
        Cell cel4 = row.createCell(3);
        // ---------------------------
        List<valuedatabean> list = new ArrayList<valuedatabean>();
        // 数据库中存储的数据行
        int page_size = 10000;
        // 求数据库中待导出数据的行数
        int list_count = this.daoUtils.queryListCount(this.valueDataDao
                .queryExportSQL(valueDataDto).get("count_sql"));
        // 根据行数求数据提取次数
        int export_times = list_count % page_size > 0 ? list_count / page_size
                + 1 : list_count / page_size;
        // 按次数将数据写入文件
        for (int j = 0; j < export_times; j++) {
            list = this.valueDataDao.queryPageList(this.valueDataDao
                            .queryExportSQL(valueDataDto).get("list_sql"), j + 1,
                    page_size);
            int len = list.size() < page_size ? list.size() : page_size;

            for (int i = 0; i < len; i++) {
                Row row_value = sh.createRow(j * page_size + i + 1);
                Cell cel0_value = row_value.createCell(0);
                cel0_value.setCellValue(list.get(i).getaa());
                Cell cel2_value = row_value.createCell(1);
                cel2_value.setCellValue(list.get(i).getaa());
                Cell cel3_value = row_value.createCell(2);
                cel3_value.setCellValue(list.get(i).getaa_person());
            }
            list.clear(); // 每次存储len行,用完了将内容清空,以便内存可重复利用
        }
        FileOutputStream fileOut = new FileOutputStream(path);
        wb.write(fileOut);
        fileOut.close();
        wb.dispose();
    }

到目前已经可以实现百万数据的导出了,但是当我们的业务数据超过200万,300万了呢?如何解决?

这时,直接打印数据到一个工作簿的一个工作表是实现不了的,必须拆分到多个工作表,或者多个工作簿中才能实现。因为一个sheet最多行数为1048576。

下面就以这种思路提供另外一种解决方案,直接上代码(后面会附上测试数据库,及案例需要的jar包)

public static void main(String[] args) throws Exception {
        TestExportData tm = new TestExportData();
        tm.jdbcex(true);
    }

    public void jdbcex(boolean isClose) throws InstantiationException, IllegalAccessException,
            ClassNotFoundException, SQLException, IOException, InterruptedException {

        String xlsFile = "f:/poiSXXFSBigData.xlsx";  //输出文件
        //内存中只创建100个对象,写临时文件,当超过100条,就将内存中不用的对象释放。
        Workbook wb = new SXSSFWorkbook(100);   //关键语句
        Sheet sheet = null;  //工作表对象
        Row nRow = null;  //行对象
        Cell nCell = null;  //列对象

        //使用jdbc链接数据库
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String url = "jdbc:mysql://localhost:3306/bigdata?characterEncoding=UTF-8";
        String user = "root";
        String password = "123456";
        //获取数据库连接
        Connection conn = DriverManager.getConnection(url, user, password);
        Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
        String sql = "select * from hpa_normal_tissue limit 1000000";   //100万测试数据
        ResultSet rs = stmt.executeQuery(sql);

        ResultSetMetaData rsmd = rs.getMetaData();
        long startTime = System.currentTimeMillis(); //开始时间
        System.out.println("strat execute time: " + startTime);

        int rowNo = 0;  //总行号
        int pageRowNo = 0; //页行号

        while (rs.next()) {
            //打印300000条后切换到下个工作表,可根据需要自行拓展,2百万,3百万...数据一样操作,只要不超过1048576就可以
            if (rowNo % 300000 == 0) {
                System.out.println("Current Sheet:" + rowNo / 300000);
                sheet = wb.createSheet("我的第" + (rowNo / 300000) + "个工作簿");//建立新的sheet对象
                sheet = wb.getSheetAt(rowNo / 300000);  //动态指定当前的工作表
                pageRowNo = 0;  //每当新建了工作表就将当前工作表的行号重置为0
            }
            rowNo++;
            nRow = sheet.createRow(pageRowNo++); //新建行对象

            // 打印每行,每行有6列数据   rsmd.getColumnCount()==6 --- 列属性的个数
            for (int j = 0; j < rsmd.getColumnCount(); j++) {
                nCell = nRow.createCell(j);
                nCell.setCellValue(rs.getString(j + 1));
            }

            if (rowNo % 10000 == 0) {
                System.out.println("row no: " + rowNo);
            }
//  Thread.sleep(1); //休息一下,防止对CPU占用,其实影响不大
        }

        long finishedTime = System.currentTimeMillis(); //处理完成时间
        System.out.println("finished execute  time: " + (finishedTime - startTime) / 1000 + "m");

        FileOutputStream fOut = new FileOutputStream(xlsFile);
        wb.write(fOut);
        fOut.flush();  //刷新缓冲区
        fOut.close();

        long stopTime = System.currentTimeMillis();  //写文件时间
        System.out.println("write xlsx file time: " + (stopTime - startTime) / 1000 + "m");

        if (isClose) {
            this.close(rs, stmt, conn);
        }
    }

    //执行关闭流的操作
    private void close(ResultSet rs, Statement stmt, Connection conn) throws SQLException {
        rs.close();
        stmt.close();
        conn.close();
    }

数据库截图:

java使用导出百万级别数据?_OOM错误

java使用导出百万级别数据?_大数据量_02

案例执行结果截图:

在这里插入图片描述

转载至:https://blog.51cto.com/u_9177933/3004953

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/196756.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

栈、堆、全局区/静态存储区、常量区、代码段、到底是什么?

一、程序运行内存分布图 我们知道一个由我们编写好的程序&#xff0c;运行时&#xff0c;我们的程序中写的代码&#xff0c;定义的变量&#xff0c;写的函数、for 循环等等&#xff0c;这些运行时都分布在内存中的哪里吗&#xff1f; 一下是一个程序运行时 内存的各个区域的分…

C语言字符串指针(指向字符串的指针)详解

C语言中没有特定的字符串类型&#xff0c;我们通常是将字符串放在一个字符数组中&#xff0c;这里演示一下&#xff1a;#include<stdio.h> #include<string.h>intmain(){ char str[]"http://csdn.net"; int len strlen(str), i; //直接输出字符串 printf…

一套完全开源,支持多租户,界面配置单点的后端框架JVS,赶紧收藏

今天推荐的这个项目是「JVS数据全家桶中的 JVS微服务框架」—— 是一个免费开源的中后台模版&#xff0c;使用了最新的 vuespring cloud 主流技术开发&#xff0c;开箱即用的中后台前端解决方案&#xff0c;可以直接商用&#xff0c;并且这个脚手架上做了很多商业上的扩展&…

day19 二叉树 | 654、最大二叉树 617、合并二叉树 700、二叉搜索树中的搜索 98、验证二叉搜索树

题目 654、最大二叉树 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下&#xff1a; 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构…

数据中心转型利润中心:数据如何赋能零售行业营销升级?

通过大数据来提升营销效果已经是零售行业不同业态都绕不过去的话题。以往&#xff0c;市场团队在进行投放时需要依靠营销团队和销售团队的经验来判断&#xff0c;这样的营销策略较为粗放&#xff0c;对用户的感知也不精准&#xff0c;一般都是在海量投放后被动的等待营销反馈。…

C++代码优化(2):条款5~12

"然后自然老去吧&#xff0c;别再依依惜别了"条款5:了解C默默编写并调用了哪些函数(1)小试牛刀地回顾C编译器为类提供的默认函数很多人知道是有6个的。默认构造、拷贝构造、赋值重载、析构函数、重载取地址运算符&#xff0c;但是在C11更新后&#xff0c;又为类对象增…

软件测试项目实战,我们拿到项目第一步应该怎么做【附过程文档】

对于从事软件研发的组织来说&#xff0c;工作类型至少包括项目管理、产品设计、编码、测试、质量保证和软件配置管理&#xff0c;以及其它人员&#xff0c;如文档编制人员和美工人员/系统硬件管理人员等。根据职能需要&#xff0c;可以以半独立方式进行部门和项目的矩阵管理&am…

Let’s Encrypt共建安全的互联网

导读最近关于沃通和 StartCom 这两家 CA 公司的消息让人们再次关注到了网络隐私和安全的问题。随着 Mozilla、苹果和谷歌对这两家 CA 公司处罚落定&#xff0c;很多使用这两家 CA 所签发证书的网站纷纷寻求新的证书签发商。这里面固然有不少可信赖的 CA 公司可以提供服务&#…

UniRx之基本语法格式

前言 想要更好的去学习UniRx&#xff0c;我们最好是先理解UniRx 的语法格式。就像我们去学习英语或者汉语一样&#xff0c;理解了基本的语法规则后&#xff0c;再学其他的就信手拈来了。 语法示例 下面我们来看一个最简单的示例&#xff0c; Observable.EveryUpdate().Where…

Git安装,配置及Gitee项目代码pull到本地

一、Git安装从git官网下载&#xff1a;https://git-scm.com/downloads选择适合的版本进入下载&#xff0c;然后傻瓜式安装直到结束。检查安装是否成功&#xff1a;进入任意文件夹点击右键选择Git Bash Here进行基本配置输入如下命令&#xff1a;git --version就会显示当前安装的…

【Windows】ip地址修改器v5.0.5.4

简介 IP地址修改器&#xff0c;一款能够快速的切换IP地址&#xff0c;在几个不同的固定IP之间进行切换&#xff0c;手动输太麻烦&#xff0c;所以可以用到这款IP地址修改器&#xff01; 下载 ip地址修改器v5.0.5.4 软件介绍 程序主要原理还是利用了WMI的Win32_NetworkAda…

二叉搜索树,平衡二叉树,红黑树,B树,B+树

文章目录二叉树&#xff08;BT&#xff09;1. 满二叉树2. 完全二叉树二叉搜索树&#xff08;BST&#xff09;平衡二叉搜索树&#xff08;AVL&#xff09;1. 定义2. 如何保持平衡——旋转红黑树&#xff08;RBTree&#xff09;1.定义2.红黑规则3.插入规则B树1.定义2.在磁盘系统中…

云存储、云计算与分布式存储、分布式计算是一回事吗?

随着互联网的蓬勃兴起&#xff0c;大数据、人工智能、物联网、云计算与云存储等这些专业词汇在大众视野内出现的频率越来越高&#xff0c;再加上近几年分布式技术异军突起&#xff0c;更使得分布式存储、分布式计算等成为热词。然而&#xff0c;很多人对这些名词都一知半解&…

HTML5+CSS3(七)-全面详解(学习总结---从入门到深化)

目录 字体属性 color font-size font-weight font-style font-family 学习效果反馈 背景属性一 background-color属性 background-image属性 background-repeat属性 学习效果反馈 背景属性二 background-size属性 background-position属性 background-attachment属性…

Utools的安装与使用

Utools的安装与使用 新一代效率工具平台. 自由组合插件应用&#xff0c;打造专属你的趁手工具集&#xff0c;我们可以根据自己需求下载对应插件&#xff0c;然后通过Utools启动插件&#xff0c;不用向以前那样不同的插件需要找到对应插件地址。我们也可以通过设置全局快捷键快速…

ESP-IDF:字符串中字符转为链表结点入栈(使用STL stack),然后打印栈中内容

ESP-IDF:字符串中字符转为链表结点入栈(使用STL stack),然后打印栈中内容 /字符串中字符转为链表结点入栈(使用STL stack),然后打印栈中内容/ #include typedef struct LINKNODE20 { struct LINKNODE20 * next; }linknode20; typedef struct MYCHAR20 { linknode20 node; ch…

C++ STL源码剖析 笔记

写在前面 记录一下《C STL源码剖析》中的要点。 一、STL六大组件 容器&#xff08;container&#xff09;&#xff1a; 各种数据结构&#xff0c;用于存放数据&#xff1b;class template 类泛型&#xff1b;如vector, list, deque, set, map&#xff1b; 算法&#xff08;a…

LeetCode刷题系列 -- 113. 路径总和 II

给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。叶子节点 是指没有子节点的节点。示例 1&#xff1a;输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22输出&#x…

Java:函数与数组的相关代码题目

引言&#xff1a; 放松一段时间&#xff0c;今天又重新开始与大家来学习&#xff0c;假期正是我们反超别人的最佳时间&#xff0c;大家要抑制自己的情绪&#xff0c;低头学习。没有任何一种成功是短时间可以得到的&#xff0c;我们要做的就是&#xff0c;沉下心来&#xf…

偶数科技入选 IDC 中国分布式数据库报告,获 Innovator 殊荣

C Innovator 近日&#xff0c;全球知名咨询研究机构 IDC 发布了《中国分布式关系型数据库》研究报告&#xff0c;通过调研 CIO、IT 负责人、投资机构和众多厂商&#xff0c;评选出该领域综合表现突出的创新型厂商。偶数科技凭借其新一代云原生数据库 OushuDB 和实时湖仓一体创新…