找回密码
 立即注册
首页 业界区 业界 Excel百万数据如何快速导入?

Excel百万数据如何快速导入?

庾芷秋 2025-6-2 00:36:54
前言

今天要讨论一个让无数人抓狂的话题:如何高效导入百万级Excel数据
去年有家公司找到我,他们的电商系统遇到一个致命问题:每天需要导入20万条商品数据,但一执行就卡死,最长耗时超过3小时。
更魔幻的是,重启服务器后前功尽弃。
经过半天的源码分析,我们发现了下面这些触目惊心的代码...
1 为什么传统导入方案会崩盘?

很多小伙伴在实现Excel导入时,往往直接写出这样的代码:
  1. // 错误示例:逐行读取+逐条插入
  2. public void importExcel(File file) {
  3.     List<Product> list = ExcelUtils.readAll(file); // 一次加载到内存
  4.     for (Product product : list) {
  5.         productMapper.insert(product); // 逐行插入
  6.     }
  7. }
复制代码
这种写法会引发三大致命问题:
1.1 内存熔断:堆区OOM惨案


  • 问题:POI的UserModel(如XSSFWorkbook)一次性加载整个Excel到内存
  • 实验:一个50MB的Excel(约20万行)直接耗尽默认的1GB堆内存
  • 症状:频繁Full GC ➔ CPU飙升 ➔ 服务无响应
1.2 同步阻塞:用户等到崩溃


  • 过程:用户上传文件 → 同步等待所有数据处理完毕 → 返回结果
  • 风险:连接超时(HTTP默认30秒断开)→ 任务丢失
1.3 效率黑洞:逐条操作事务


  • 实测数据:MySQL单线程逐条插入≈200条/秒 → 处理20万行≈16分钟
  • 幕后黑手:每次insert都涉及事务提交、索引维护、日志写入
2 性能优化四板斧

第一招:流式解析

使用POI的SAX模式替代DOM模式:
  1. // 正确写法:分段读取(以HSSF为例)
  2. OPCPackage pkg = OPCPackage.open(file);
  3. XSSFReader reader = new XSSFReader(pkg);
  4. SheetIterator sheets = (SheetIterator) reader.getSheetsData();
  5. while (sheets.hasNext()) {
  6.     try (InputStream stream = sheets.next()) {
  7.         Sheet sheet = new XSSFSheet(); // 流式解析
  8.         RowHandler rowHandler = new RowHandler();
  9.         sheet.onRow(row -> rowHandler.process(row));
  10.         sheet.process(stream); // 不加载全量数据
  11.     }
  12. }
复制代码
⚠️ 避坑指南

  • 不同Excel版本需适配(HSSF/XSSF/SXSSF)
  • 避免在解析过程中创建大量对象,需复用数据容器
第二招:分页批量插入

基于MyBatis的批量插入+连接池优化:
  1. // 分页批量插入(每1000条提交一次)
  2. public void batchInsert(List<Product> list) {
  3.     SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
  4.     ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
  5.    
  6.     int pageSize = 1000;
  7.     for (int i = 0; i < list.size(); i += pageSize) {
  8.         List<Product> subList = list.subList(i, Math.min(i + pageSize, list.size()));
  9.         mapper.batchInsert(subList);
  10.         sqlSession.commit();
  11.         sqlSession.clearCache(); // 清理缓存
  12.     }
  13. }
复制代码
关键参数调优
  1. # MyBatis配置
  2. mybatis.executor.batch.size=1000
  3. # 连接池(Druid)
  4. spring.datasource.druid.maxActive=50
  5. spring.datasource.druid.initialSize=10
复制代码
第三招:异步化处理

架构设计:
1.webp


  • 前端上传:客户端使用WebUploader等分片上传工具
  • 服务端

    • 生成唯一任务ID
    • 写入任务队列(Redis Stream/RabbitMQ)

  • 异步线程池

    • 多线程消费队列
    • 处理进度存储在Redis中

  • 结果通知:通过WebSocket或邮件推送完成状态
第四招:并行导入

对于千万级数据,可采用分治策略:
阶段操作耗时对比单线程逐条读取+逐条插入基准值100%批处理分页读取+批量插入时间降至5%多线程分片按Sheet分片,并行处理时间降至1%分布式分片多节点协同处理(如Spring Batch集群)时间降至0.5%3 代码之外的关键经验

3.1 数据校验必须前置

典型代码缺陷:
  1. // 错误:边插入边校验,可能污染数据库
  2. public void validateAndInsert(Product product) {
  3.     if (product.getPrice() < 0) {
  4.         throw new Exception("价格不能为负");
  5.     }
  6.     productMapper.insert(product);
  7. }
复制代码
正确实践

  • 在流式解析阶段完成基础校验(格式、必填项)
  • 入库前做业务校验(数据关联性、唯一性)
3.2 断点续传设计

解决方案:

  • 记录每个分片的处理状态
  • 失败时根据偏移量(offset)恢复
3.3 日志与监控

配置要点:
  1. // Spring Boot配置Prometheus指标
  2. @Bean
  3. public MeterRegistryCustomizer<PrometheusMeterRegistry> metrics() {
  4.     return registry -> registry.config().meterFilter(
  5.         new MeterFilter() {
  6.             @Override
  7.             public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
  8.                 return DistributionStatisticConfig.builder()
  9.                     .percentiles(0.5, 0.95) // 统计中位数和95分位
  10.                     .build().merge(config);
  11.             }
  12.         }
  13.     );
  14. }
复制代码
四、百万级导入性能实测对比

测试环境:

  • 服务器:4核8G,MySQL 8.0
  • 数据量:100万行x15列(约200MB Excel)
方案内存峰值耗时吞吐量传统逐条插入2.5GB96分钟173条/秒分页读取+批量插入500MB7分钟2381条/秒多线程分片+异步批量800MB86秒11627条/秒分布式分片(3节点)300MB/节点29秒34482条/秒总结

Excel高性能导入的11条军规:

  • 决不允许全量加载数据到内存 → 使用SAX流式解析
  • 避免逐行操作数据库 → 批量插入加持
  • 永远不要让用户等待 → 异步处理+进度查询
  • 横向扩展比纵向优化更有效 → 分片+分布式计算
  • 内存管理是生死线 → 对象池+避免临时大对象
  • 合理配置连接池参数 → 杜绝瓶颈在数据源
  • 前置校验绝不动摇 → 脏数据必须拦截在入口
  • 监控务必完善 → 掌握全链路指标
  • 设计必须支持容灾 → 断点续传+幂等处理
  • 抛弃单机思维 → 拥抱分布式系统设计
  • 测试要覆盖极端场景 → 百万数据压测不可少
如果你正在为Excel导入性能苦恼,希望这篇文章能为你的系统打开一扇新的大门。
如果你有其他想了解的技术难题,欢迎在评论区留言!
最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。
本文收录于我的技术网站:http://www.susan.net.cn

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册