多线程应用:数据库百万级数据导入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工具类的封装:
