# springboot-excel **Repository Path**: idbb98/springboot-excel ## Basic Information - **Project Name**: springboot-excel - **Description**: 百万级数据导入导出解决方案 Spring Boot 、EasyExcel 、MyBatis-Plus、 Disruptor 、MySQL - **Primary Language**: Java - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 3 - **Created**: 2025-04-02 - **Last Updated**: 2025-08-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: SpringBoot, Excel ## README ## 百万Excel数据,如何导入到数据库? [参考文章](https://www.cnblogs.com/idbb98/p/18795436) [高性能队列——Disruptor](https://tech.meituan.com/2016/11/18/disruptor.html) ### 流程图 ```mermaid graph TD A[开始导入数据] --> B[调用EasyExcel读取Excel文件] B --> C[创建DataRecordExcelListener监听器] C --> D[逐行读取Excel数据] D --> E{读取到一批数据?} E -- 是 --> F[缓存到batch列表] F --> G{batch达到10000条?} G -- 是 --> H[发布batch到Disruptor] H --> I[清空batch列表] G -- 否 --> D E -- 否 --> J[处理剩余数据] J --> K[发布最后一批数据到Disruptor] K --> L[Disruptor接收数据并放入RingBuffer] L --> M[DataRecordEventHandler处理数据] M --> N{数据达到1000条或批次结束?} N -- 是 --> O[批量插入数据库] N -- 否 --> M O --> P[清空已处理数据] P --> Q[导入完成] ``` 数据导入的完整过程: 1. 通过EasyExcel读取Excel文件,使用自定义的监听器处理数据 2. 监听器将读取的数据按批次(10000条)缓存 3. 当缓存达到批次大小时,将数据发布到Disruptor 4. Disruptor的EventHandler接收数据,并按更小的批次(1000条)批量插入数据库 5. 最后处理并插入剩余的数据 ### 项目上需要导入一个几百万数据 excel 文件到数据库中,有哪些注意点? 1. 技术选型 (EasyExcel 读取;Disruptor 队列; MyBatisPlus 插入数据库) 2. 性能优化 (流式逐行读取到内存;内存队列缓冲,多生产者,提升并发;数据库批量插入,索引优化) 3. 错误处理 (数据验证; 异常捕获; 日志记录) ## 导出数据 ### com.keepc.excel.service.DataExportService.exportData1 最简单的顺序处理方式,逐页查询并写入Excel,适用于数据量较小的情况。 单线程、分页查询、分sheet写入、有序 ```mermaid graph TD A[开始导出数据] --> B[初始化变量: page=0, sheetDataSize=0, sheetNo=1] B --> C[创建ExcelWriter和FileOutputStream] C --> D[创建WriteSheet] D --> E[page++] E --> F[查询第page页数据] F --> G{查询结果为空?} G -- 是 --> H[记录日志: 数据导出完成] H --> I[完成写入并关闭ExcelWriter] I --> J[结束] G -- 否 --> K[sheetDataSize += 查询数据量] K --> L{sheetDataSize > SHEET_SIZE?} L -- 是 --> M[sheetNo++] M --> N[重置sheetDataSize为当前页数据量] N --> O[创建新的WriteSheet] O --> P[写入数据到Excel] L -- 否 --> P P --> Q[记录日志: 第page页数据写入成功] Q --> E ``` ### com.keepc.excel.service.DataExportService.exportData2 使用多线程并行查询数据,但写入操作是同步的,避免并发写入问题,适用于中等数据量。 多线程(CountDownLatch和CompletableFuture)、分页查询、分sheet写入、无序 ```mermaid graph TD A[开始导出数据] --> B[获取数据总量] B --> C{数据总量为0?} C -- 是 --> D[记录日志: 没有数据需要导出] D --> E[结束] C -- 否 --> F[计算总页数] F --> G[创建ExcelWriter] G --> H[创建CountDownLatch和线程池] H --> I[for循环: 页数从1到总页数] I --> J[创建CompletableFuture任务] J --> K[异步查询当前页数据] K --> L[计算当前页应写入的Sheet] L --> M[创建WriteSheet] M --> N[同步写入数据到Excel] N --> O[CountDownLatch计数器减1] I --> P[等待所有任务完成] P --> Q[关闭线程池和ExcelWriter] Q --> R[记录日志: Excel文件导出完成] R --> S[结束] ``` ### com.keepc.excel.service.DataExportService.exportData3 使用生产者-消费者模式,多个线程负责查询数据,一个线程负责写入数据,通过队列缓冲实现高效的数据处理,适用于大数据量场景。 多线程、分页查询、缓存队列、生产者-消费者模式分sheet写入、无序 ```mermaid graph TD A[开始导出数据] --> B[获取数据总量和计算Sheet数量] B --> C[初始化线程安全队列Map] C --> D[创建查询和写入线程池] D --> E[创建ExcelWriter] E --> F[启动消费者线程写入数据] F --> G[生产者线程分页查询数据] G --> H[将查询到的数据分配到对应Sheet队列] H --> I{所有生产者完成?} I -- 否 --> G I -- 是 --> J[关闭查询线程池] J --> K[等待消费者线程完成] K --> L[关闭ExcelWriter和写入线程池] L --> M[记录日志: 导出完成] M --> N[结束] subgraph 消费者线程 F --> F1[从队列中批量获取数据] F1 --> F2[写入数据到Excel] F2 --> F3{队列为空且生产者结束?} F3 -- 否 --> F1 F3 -- 是 --> F4[退出消费者线程] end ``` ### 测试结果 2百万数据导出耗时 ![](./asserts/2million.png) 5百万数据导出耗时 ![](./asserts/5million.png)