近期遇到一个需要将大量数据写入数据库的事情,但是没有正版Navicate,别的软件也不好用,就想着写个接口来实现,(如果不考虑安全问题,我建议使用navicate->网上pojie版资源很多)
回归正题因为是周五下午快下班告诉我的,所以当时思考了一下,决定采用POI来做(因为之前没有用过这个功能)后来写了一下,实现了这个功能(第一个版本),但是非常简陋,面对2000条数据都很耗时,更不要说30w条数据。
周五下班在地铁上在想怎么优化代码,坐上回学校的校车时候突然想到用多线程,回来之后立马用多线程实现了,最终测试了一下面对2017条数据耗时大概14.55秒(第二个版本),但是面对29w条数据时候出现电脑性能被吃满,非常卡,cpu和内存都被吃满了,这还仅仅是一个存入数据库的功能而已,如果真的上线,对其他功能以及整个程序会造成无法挽回的后果,29w条数据最终耗时大概在24分钟左右,相比第一个版本耗时64分钟提升太多了,但是最终还是决定继续优化,碰巧周五坐大巴时候看到一篇博客讲的是阿里的EasyExcel性能非常好,周日也没啥事情,就决定采用EasyExcel来实现一下,万万没想到只是比第一个版本在导入2017条数据时候快了1秒在导入29w条数据时候快了大概有2分钟吧,当时就蒙了,还以为是用错了,又去官网看了一下,结果还是一样,最后想到了用MybatisPlus的批量导入功能,可是依旧改变不大(第三个版本)
最终想着让Chatgpt帮我实现,可是弄了一下午,最后发现它根本不会(只能锦上添花,当个搜索引擎来用),我就尝试了很多办法,看了很多人的文档,都以失败告终。然后就决定自己来想怎么做,经过思考决定在第二个版本多线程的基础上来实现,我采用EasyExcel来读取数据,然后对数据进行分片操作,每一片对应一个线程,然后采用MybatisPlus的批量插入,一次插入一片数据,我定义的一片数据为500条。最终结果是非常快2017条一共耗时6秒,比一般的多线程快了一半多时间,30w+条数据耗时6分44秒。并且全程电脑性能正常,没有吃CPU和内存,其他指数也都正常,真正达到了我的预期。
最后我也去网上又找了其他的方法,发现有的比我的快得多只用了24秒,能力有限我确实想不出是怎么实现的,代码还有许多要调优的地方,大家可以自己再优化一下。
@Test
public void testExcel04(){
String excelFilePath = "D:\\data\\0406-0410.xlsx";
int numThreads = 8; // 线程数
int pagesize=500;
// 创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
List<Exceltest> list = EasyExcel.read(new File(excelFilePath))
.head(Exceltest.class)
.sheet(0)
.doReadSync();
int size = list.size();
//对list进行切分然后对分片进行插入操作
for (int i = 0; i < size; i+=pagesize) {
int startindex= i;
int endindex=Math.min(i+pagesize,size);
List<Exceltest> sublist = list.subList(startindex, endindex);
// new work04(list,pagesize)
threadPool.submit(new Runnable() {
@Override
public void run() {
savedata(sublist,pagesize);
}
});
}
// 关闭线程池
threadPool.shutdown();
//等待执行完毕
try {
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void savedata(List<Exceltest> list,int pagesize){
System.out.println(LocalDateTime.now()+"线程"+Thread.currentThread().getName()+"正在执行分片"+(list.get(0).getObjectid()/pagesize+1)+"保存数据的任务.....");
exceltestService.saveBatch(list);
System.out.println(LocalDateTime.now()+"线程"+Thread.currentThread().getName() + "执行分片"+(list.get(0).getObjectid() / pagesize + 1) + "保存数据的任务完成.....");
}