百万级别数据的Excel如何快速导入到数据库中?

【Java】百万级别数据的Excel如何快速导入到数据库中 Mybatis 原生是不支持将查询到的结果直接写人一个 HashMap 中的,需要自定义 SessionMapperSessionMapper 中指定使用 MapResultHandler 处理 SQL 查询的结果集。 阅读详情

35.1 需求说明

项目中有一个 Excel 导入的需求:缴费记录导入

由实施 / 用户 将别的系统的数据填入我们系统中的 Excel 模板,应用将文件内容读取、校对、转换之后产生欠费数据、票据、票据详情并存储到数据库中。

在接手之前可能由于之前导入的数据量并不多没有对效率有过高的追求。但是到了 4.0 版本,预估导入时Excel 行数会是 10w+ 级别,而往数据库插入的数据量是大于 3n 的,也就是说 10w 行的 Excel,则至少向数据库插入 30w 行数据。因此优化原来的导入代码是势在必行的。逐步分析和优化了导入的代码,使之在百秒内完成(最终性能瓶颈在数据库的处理速度上,测试服务器 4g 内存不仅放了数据库,还放了很多微服务应用。处理能力不太行)。具体的过程如下,每一步都有列出影响性能的问题和解决的办法。

导入 Excel 的需求在系统中还是很常见的,优化办法可能不是最优的,欢迎读者在评论区留言交流提供更优的思路

一些细节

1.数据导入:导入使用的模板由系统提供,格式是 xlsx (支持 65535+行数据) ,用户按照表头在对应列写入相应的数据

2.数据校验:数据校验有两种:

2.1 字段长度、字段正则表达式校验等,内存内校验不存在外部数据交互。对性能影响较小

2.2 数据重复性校验,如票据号是否和系统已存在的票据号重复(需要查询数据库,十分影响性能)

3.数据插入:测试环境数据库使用 MySQL 5.7,未分库分表,连接池使用 Druid

迭代记录

由于篇幅限制,只能给大家展示小部分内容,

全套面试笔记及答案【点击此处】即可免费获取

35.2 A. 第一版:POI + 逐行查询校对 + 逐行插入

这个版本是最古老的版本,采用原生 POI,手动将 Excel 中的行映射成 ArrayList 对象,然后存储到 List,代码执行的步骤如下:

1.手动读取 Excel 成 List

2.循环遍历,在循环中进行以下步骤

.检验字段长度

.一些查询数据库的校验,比如校验当前行欠费对应的房屋是否在系统中存在,需要查询房屋表

3.写入当前行数据

4.返回执行结果,如果出错 / 校验不合格。则返回提示信息并回滚数据

显而易见的,这样实现一定是赶工赶出来的,后续可能用的少也没有察觉到性能问题,但是它最多适用于个位数/十位数级别的数据。存在以下明显的问题:

1.查询数据库的校验对每一行数据都要查询一次数据库,应用访问数据库来回的网络IO次数被放大了 n 倍,时间也就放大了 n 倍

2.写入数据也是逐行写入的,问题和上面的一样

3.数据读取使用原生 POI,代码十分冗余,可维护性差。

35.3 B. 第二版:EasyPOI + 缓存数据库查询操作 + 批量插入

针对第一版分析的三个问题,分别采用以下三个方法优化

1.缓存数据,以空间换时间

逐行查询数据库校验的时间成本主要在来回的网络IO中,优化方法也很简单。将参加校验的数据全部缓存到 HashMap 中。直接到 HashMap 去命中。

例如:校验行中的房屋是否存在,原本是要用 区域 + 楼宇 + 单元 + 房号 去查询房屋表匹配房屋ID,查到则校验通过,生成的欠单中存储房屋ID,校验不通过则返回错误信息给用户。而房屋信息在导入欠费的时候是不会更新的。并且一个小区的房屋信息也不会很多(5000以内)因此我采用一条SQL,将该小区下所有的房屋以 区域/楼宇/单元/房号 作为 key,以 房屋ID 作为 value,存储到 HashMap 中,后续校验只需要在 HashMap 中命中

自定义 SessionMapper

Mybatis 原生是不支持将查询到的结果直接写人一个 HashMap 中的,需要自定义 SessionMapper

SessionMapper 中指定使用 MapResultHandler 处理 SQL 查询的结果集

@Repository
public class SessionMapper extends SqlSessionDaoSupport {
    @Resource
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }
    // 堆代码 duidaima.com
    // 区域楼宇单元房号 - 房屋ID
    @SuppressWarnings("unchecked")
    public Map<String, Long> getHouseMapByAreaId(Long areaId) {
        MapResultHandler handler = new MapResultHandler();
 this.getSqlSession().select(BaseUnitMapper.class.getName()+".getHouseMapByAreaId", areaId, handler);
        Map<String, Long> map = handler.getMappedResults();
        return map;
    }
}

MapResultHandler 处理程序,将结果集放入 HashMap

public class MapResultHandler implements ResultHandler {
    private final Map mappedResults = new HashMap();
    @Override
    public void handleResult(ResultContext context) {
        @SuppressWarnings("rawtypes")
        Map map = (Map)context.getResultObject();
        mappedResults.put(map.get("key"), map.get("value"));
    }
    public Map getMappedResults() {
        return mappedResults;
    }
}

示例 Mapper

@Mapper
@Repository 
public interface BaseUnitMapper {
    // 收费标准绑定 区域楼宇单元房号 - 房屋ID
    Map<String, Long> getHouseMapByAreaId(@Param("areaId") Long areaId);
}

示例 Mapper.xml

<select id="getHouseMapByAreaId" resultMap="mapResultLong">
    SELECT
        CONCAT( h.bulid_area_name, h.build_name, h.unit_name, h.house_num ) k,
        h.house_id v
    FROM
        base_house h
    WHERE
        h.area_id = ##{areaId}
    GROUP BY
        h.house_id
</select>
            
<resultMap id="mapResultLong" type="java.util.HashMap">
    <result property="key" column="k" javaType="string" jdbcType="VARCHAR"/>
    <result property="value" column="v" javaType="long" jdbcType="INTEGER"/>
</resultMap>

之后在代码中调用 SessionMapper 类对应的方法即可。

2.使用 values 批量插入

MySQL insert 语句支持使用 values (),(),() 的方式一次插入多行数据,通过 mybatis foreach 结合 java 集合可以实现批量插入,代码写法如下:

<insert id="insertList">
    insert into table(colom1, colom2)
    values
    <foreach collection="list" item="item" index="index" separator=",">
     ( ##{item.colom1}, ##{item.colom2})
    </foreach>
</insert>

35.4 C. 第三版:优化数据插入速度

在第二版插入的时候,我使用了 values 批量插入代替逐行插入。每 30000 行拼接一个长 SQL、顺序插入。整个导入方法这块耗时最多,非常拉跨。后来我将每次拼接的行数减少到 10000、5000、3000、1000、500 发现执行最快的是 1000。结合网上一些对 innodb_buffer_pool_size 描述我猜是因为过长的 SQL 在写操作的时候由于超过内存阈值,发生了磁盘交换。关注公号:码猿技术专栏,回复关键词:1111 获取阿里内部Java调优手册;限制了速度,另外测试服务器的数据库性能也不怎么样,过多的插入他也处理不过来。所以最终采用每次 1000 条插入。

每次 1000 条插入后,为了榨干数据库的 CPU,那么网络IO的等待时间就需要利用起来,这个需要多线程来解决,而最简单的多线程可以使用 并行流 来实现,接着我将代码用并行流来测试了一下:

10w行的 excel、42w 欠单、42w记录详情、2w记录、16 线程并行插入数据库、每次 1000 行。插入时间 72s,导入总时间 95 s。

并行插入工具类

并行插入的代码我封装了一个函数式编程的工具类,也提供给大家

/**
 * 功能:利用并行流快速插入数据
 */
public class InsertConsumer {
    /**
     * 每个长 SQL 插入的行数,可以根据数据库性能调整
     */
    private final static int SIZE = 1000;
    /**
     * 如果需要调整并发数目,修改下面方法的第二个参数即可
     */
    static {
        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "4");
    }
    /**
     * 插入方法
     *
     * @param list     插入数据集合
     * @param consumer 消费型方法,直接使用 mapper::method 方法引用的方式
     * @param <T>      插入的数据类型
     */
    public static <T> void insertData(List<T> list, Consumer<List<T>> consumer) {
        if (list == null || list.size() < 1) {
            return;
        }
        List<List<T>> streamList = new ArrayList<>();
        for (int i = 0; i < list.size(); i += SIZE) {
            int j = Math.min((i + SIZE), list.size());
            List<T> subList = list.subList(i, j);
            streamList.add(subList);
        }
        // 并行流使用的并发数是 CPU 核心数,不能局部更改。全局更改影响较大,斟酌
        streamList.parallelStream().forEach(consumer);
    }
}

这里多数使用到很多 Java8 的API,不了解的朋友可以翻看我之前关于 Java 的博客。方法使用起来很简单

InsertConsumer.insertData(feeList, arrearageMapper::insertList);

35.5 其他影响性能的内容

日志

避免在 for 循环中打印过多的 info 日志

在优化的过程中,我还发现了一个特别影响性能的东西:info 日志,还是使用 41w行、25列、45.5m 数据,在 开始-数据读取完毕 之间每 1000 行打印一条 info 日志,缓存校验数据-校验完毕 之间每行打印 3+ 条 info 日志,日志框架使用 Slf4j 。打印并持久化到磁盘。下面是打印日志和不打印日志效率的差别

打印日志

不打印日志

我以为是我选错 Excel 文件了,又重新选了一次,结果依旧

缓存校验数据-校验完毕 不打印日志耗时仅仅是打印日志耗时的 1/10 !

…… 

Java实现百万级数据从Excel导入到数据库 在一个后台管理功能中,Excel导入数据是不可缺少,但是当处理大数据量的Excel文件导入时候就可能会带来一些列的问题,本文我们会分析问题产生的原因。并提供解决方案以及完整的实现代码。 阅读详情

相关推荐

【Java】百万级别数据的Excel如何快速导入到数据库中?

Mybatis 原生是不支持将查询到的结果直接写人一个 HashMap 中的,需要自定义 SessionMapperSessionMapper 中指定使用 MapResultHandler 处理 SQL 查询的结果集。

alen101的博客 1619

实现百万级数据从Excel导入到数据库的方式

原创 派大星辽宁身处外企,是一名技术工作者。作为InfoQ、阿里云等技术平台签约作者,致力于打造您专属的编程个人笔记(无广告)。216篇原创内容公众号。

竹林幽深 1969

如何实现在4秒内将百万级数据导入SQL Server

上述代码将从指定的CSV文件路径导入数据到名为"MyTable"的表中。本文将详细介绍如何使用一些有效的技术和最佳实践,以在4秒内将百万级数据导入SQL Server数据库。首先,我们需要准备好要导入的百万级数据。确保数据源的格式正确,并根据需要进行必要的数据清洗和转换。通过遵循上述步骤和最佳实践,您可以实现在4秒内将百万级数据导入SQL Server数据库。根据您的具体需求和环境,可能需要进行一些调整和优化。在SQL Server中,我们需要创建一个表来存储导入的数据。根据数据的结构,创建相应的表和列。

ElServer的博客 403

如何实现百万级数据从Excel导入到数据库

我们要知道 导入功能数据量一旦大起来的话, 批量插入数据库, 就会出现很多问题,1. 比如说 excel文件会很大, 如果一次加载到内存中, 如果jvm设置的内存不足以支撑, 就会导致内存溢出2.百万级数据读取时, 我们进行业务操作, 导入到数据库这一操作, 插入的会导致很慢, 同时也需要考虑性能问题。

m0_68723562的博客 2348

用AI免费制作30分钟电影:完整工作流与实操解析

AI视频生成技术正大幅降低影视创作门槛,但真正制作一部完整电影并非一键生成,而是涉及剧本、分镜、画面、配音、剪辑的复杂流水线。理解其基本原理,是将文本转化为连续视觉叙事的关键。对于创作者而言,掌握角色一致性控制、素材批量管理、免费额度合理分配等技术价值,能有效避免生成结果崩坏与资源浪费。该能力可广泛应用于短视频创作、独立电影预演、教学演示等场景。本文以30分钟AI电影制作为例,拆解从工具选择到成片输出的全流程,重点分析免费方案下的时间与积分成本,以及最容易翻车的环节,并给出基于实践验证的排查与优化策略。

weixin_29056865的博客 321

Python+Selenium实战:5分钟搞定快手评论区数据采集(附完整代码)

本文详细介绍了如何使用Python和Selenium快速采集快手评论区数据,提供完整的代码实现和优化方案。通过动态加载处理和智能等待策略,5分钟内即可构建高效的评论采集工具,适用于用户行为分析和内容挖掘等场景。

weixin_30415113的博客 342

【Java】Excel百万级别数据的导入和导出【详细代码】

Excel百万级别的数据导入和导出,包含代码和sql文件和相关视频链接,多多学习,百炼成钢。

m0_64210833的博客 1万+

从打印攻击到视频重放:手把手用OULU-NPU训练你的第一个活体检测模型

本文详细介绍了如何使用OULU-NPU数据集训练活体检测模型,以应对打印攻击和视频重放等安全威胁。通过PyTorch框架,从环境准备、数据加载、预处理到模型架构设计,手把手教你构建一个高效的双流网络,结合CNN和LSTM技术,提升模型在多种攻击场景下的检测准确率。

weixin_29092031的博客 384

百万级别数据的Excel如何快速导入到数据库中

Mybatis 原生是不支持将查询到的结果直接写人一个 HashMap 中的,需要自定义 SessionMapperSessionMapper 中指定使用 MapResultHandler 处理 SQL 查询的结果集。

alen101的博客 1847

构建基于知识图谱的金融监管合规系统

金融行业是一个高度监管的领域,金融机构需要遵守众多复杂的法规和政策。传统的金融监管合规方式往往依赖于人工审查和简单的规则引擎,效率低下且容易出错。构建基于知识图谱的金融监管合规系统的目的在于利用知识图谱强大的知识表示和推理能力,实现对金融业务数据的自动化分析和合规检查,提高监管合规的效率和准确性。本系统的范围涵盖了金融机构的各类业务,如银行业务、证券业务、保险业务等。

AI天才研究院 1165

手把手教你学Simulink——基于Simulink的储能PCS(功率转换系统)离网V/f控制仿真示例

本文带你从零开始,在Simulink中硬核还原了一套完整的100kW 储能PCS离网V/f控制系统。通过引入:✅无PLL的自主角度生成机制(实现真正的“构网”能力)✅电压外环与电流内环的双闭环解耦架构(兼顾稳态精度与动态响应)✅黑启动软充电与有源阻尼等工程化手段(确保系统平滑建压与抗谐振)核心收获透彻理解了储能系统在无大电网支撑下的自主运行机理。掌握了从“跟网(Grid-Following)”向“构网(Grid-Forming)”跨越的控制算法核心差异。

MHD0815的博客 77

百万级别数据的 Excel 如何快速导入到数据库中

这个版本是最古老的版本,采用原生 POI,手动将 Excel 中的行映射成 ArrayList 对。10w 行的 excel、42w 欠单、42w 记录详情、2w 记录、16 线程并行插入数据库、每。1.数据导入:导入使用的模板由系统提供,格式是 xlsx (支持 65535+行数据) ,用户。的,也就是说 10w 行的 Excel,则至少向数据库插入 30w 行数据。每 30000 行拼接一。导入 Excel 的需求在系统中还是很常见的,优化办法可能不是最优的,欢迎读者在评。

weixin_46300935的博客 1043

在 Linux 64 位上遭遇 Signal 7 与 ST22 SYSTEM_CORE_DUMPED:从 es/implementation=std 到 /dev/shm 的一套落地排查法

摘要: Linux 64位SAP系统出现SYSTEM_CORE_DUMPED和Signal 7(对应SIGBUS)错误时,核心问题是进程因共享内存资源异常被操作系统强制终止。主要诱因包括:1)/dev/shm的tmpfs默认容量不足(通常仅占物理内存50%);2)SAP内存管理参数es/implementation=map在64位场景下的敏感性。解决方案应双管齐下:在实例profile设置es/implementation=std以降低对tmpfs的依赖,同时按(RAM+Swap)*75%重新配置/dev/

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 163

【Java面试场景题】百万级别数据的Excel如何快速导入到数据库中

Mybatis 原生是不支持将查询到的结果直接写人一个 HashMap 中的,需要自定义 SessionMapperSessionMapper 中指定使用 MapResultHandler 处理 SQL 查询的结果集。

JAVA_aik的博客 1429

高比例风电电力系统储能运行及配置研究(python&Matlab实现)

目录1 概述2 案例3 Matlab实现 3.1 题目及分析3.2 Matlab代码 4 七个题全部代码(值得学习) 下面我们先以第二题为例,然后给出Matlab代码:先考虑风电装机300MW直接替代机组3的发电功率曲线,观察系统功率平衡发生的变化情况。然后在此场景下,以最小发电成本为目标函数,将负荷分配给两台火电机组1、2和风电机组,绘制机组日发电计划图。再将风电装机容量作为一个决策变量,以最小发电成本为目标函数,求取最小发电成本对应的风电装机容量,绘制机组日发电计划图。1. 风电装机300MW、直接

weixin_46039719的博客 1738

HQChart实战教程49-点击修改K线颜色及背景色

HQChart实战教程49-点击修改K线颜色及背景色需求效果图步骤1. 注册点击监听事件2. 修改K颜色添加一个自定义颜色的K线klineoption清空所有自定义颜色的K线option交流QQ群: 950092318HQChart代码地址完整例子 需求 对点击的K线修改颜色。 效果图 步骤 1. 注册点击监听事件 事件ID JSCHART_EVENT_ID.ON_CLICK_CHART_PAINT, 注册监听事件见教程 HQChart使用教程5- K线图控件操作函数说明 this.Create=func

小银 2911

招行面试:100万级别数据的Excel,如何秒级导入到数据库?

在日常的开发中,用的比较多的方式就是 Apache 下的 POI 框架了,但在目前数据量大的时代下,这种方式 已经不适合了, 当数据量过大时, POI 框架会出现 OOM 异常,但是作为数据量小场景下的操作框架,还是OK的。百万级数据量的场景,这个就不行了。这里,尼恩先是介绍原始 Apache POI ,然后介绍阿里巴巴开源框架,做对比介绍。这个示例实现了一个完整的数据导入架构,使用 EasyExcel 进行高性能分片读取,避免了内存溢出问题。

架构师尼恩 2001

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

基于easyexcel的MySQL百万级别数据的excel导出功能

重写了POI对07版Excel的解析,之前一个3M的excel用POI sax解析,需要100M左右内存,如果改用easyexcel可以降低到几M,并且再大的Excel也不会出现内存溢出;需要注意的是:如果用job的话,要避免重复执行的情况。用户拿到这个百万级别的excel文件,到底有什么用途,在他们的电脑上能否打开该excel文件,电脑是否会出现太大的卡顿了,导致文件使用不了。,增加更多的查询条件,用户通过修改查询条件,多次导数据,可以实现将所有数据都导出的功能,这样可能更合理一些。

qq_42003636的博客 1393

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

JAVA使用POI如何导出百万级别数据

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

happyljw的博客 6万+

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

百万级数据excel导出功能如何实现?

最近我做过一个MySQL百万级别数据的excel导出功能,已经正常上线使用了。这个功能挺有意思的,里面需要注意的细节还真不少,现在拿出来跟大家分享一下,希望对你会有所帮助。原始需求:用户在UI界面上点击全部导出按钮,就能导出所有商品数据。咋一看,这个需求挺简单的。但如果我告诉你,导出的记录条数,可能有一百多万,甚至两百万呢?这时你可能会倒吸一口气。如果同步导数据,接口很容易超时。如果把所有数据一次性装载到内存,很容易引起OOM。数据量太大sql语句必定很慢。

5524

百万数据excel导出功能如何实现?(荣耀典藏版)

大家好,我是月夜枫,最近我做过一个MySQL百万级别数据的excel导出功能,已经正常上线使用了。 这个功能挺有意思的,里面需要注意的细节还真不少,现在拿出来跟大家分享一下,希望对你会有所帮助。

人生若只初相见@的博客 4万+

【Java】百万数据excel导出功能如何实现

最近我做过一个MySQL百万级别数据的excel导出功能,已经正常上线使用了。这个功能挺有意思的,里面需要注意的细节还真不少,现在拿出来跟大家分享一下,希望对你会有所帮助。原始需求:用户在UI界面上点击全部导出按钮,就能导出所有商品数据。乍一看,这个需求挺简单的。但如果我告诉你,导出的记录条数,可能有一百多万,甚至两百万呢?这时你可能会倒吸一口气。如果同步导数据,接口很容易超时。如果把所有数据一次性装载到内存,很容易引起OOM。数据量太大sql语句必定很慢。

JavaGPT的博客 1925

java使用导出百万级别数据

java 导出百万数据

LuckyTHP 2630
上一篇: 设计一个支持10W QPS的评论中台,你会怎么设计?
下一篇: 如何设计RPC框架在10万QPS下实现毫秒级的服务调用?
Java阿福
博客等级 码龄2年 61粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值