多线程应用:数据库百万级数据导入Excel表格
废话不说,先看疗效
数据库数据:4194294条
实现目标:DB ——> Excel表格
导出结果:
动手之前,先避坑
1. jdbc读取百万条数据出现内存溢出OOM。 因为mysql jdbc默认把select的所有结果全部取回,放到内存中,如果是要遍历很大的表,则可能把内存撑爆。这是查询后数据存到内存时,内存不足引起的。
通过分页查询来优化
2. 单线程执行一次性的查询上百万条数据JVM需要同时间创建上百万个对象也会造成OOM。这时JVM内存不足引起的。
通过使用多线程来优化。
思路:
线程池技术 + CountDownLatch + 分页查询 + 动态生成Excel工具类
工具类封装链接放在文章最下方,需要请点击传送门~
==================================核心==================================
代码详解
@Slf4j @RunWith(SpringRunner.class) @SpringBootTest(classes = NewsApplication.class) public class ImportTest { @Autowired private ThreadPoolTaskExecutor executor; @Autowired private INewsInfoService iNewsInfoService; /** * 测试百万条数据导入 */ @Test public void testMillionDataImport() throws Exception { //数据库总记录数,本机是400多万条 int count = iNewsInfoService.count(); //每次一万条 int limit = 10000; //分段次数 int cycles = count / limit; //使一个线程等待其他线程各自执行完毕后再执行 CountDownLatch countDownLatch = new CountDownLatch(cycles); for (int i = 0; i < cycles; i++) { //每一段的起始坐标 Integer idx = i * limit; log.info("idx: {}", idx); Runnable runnable = new Runnable() { @Override public void run() { try { //分页查询列表数据 List<NewsInfo> infos = iNewsInfoService.listData(idx, limit); //存入Excel PoiUtil.createDynamicListExcel(infos); //减少一个容量 countDownLatch.countDown(); } catch (Exception e) { e.printStackTrace(); } } }; //把任务丢给线程池调度执行 executor.execute(runnable); } try { //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } }
传送门
关于如何使用反射技术实现动态传入对象生成Excel工具类的封装: