前言
上一节被迫把线程池给整会了。但是仍然没有把实际问题给解决:向数据库插300万条数据(太慢啦)。
下面是我一步步加快执行速度的记录。
实现步骤
先来看下整会线程池之后的代码。
public class ThreadSaveDB {
public static void main(String[] args) {
int size = 3000000;
ThreadPoolExecutor executor = new ThreadPoolExecutor(30,30, 10L, TimeUnit.SECONDS,new LinkedBlockingQue);
for (int i=0;i<size;i++){
executor.execute(new Runnable() {
@Override
public void run() {
//存表
Person person = new Person("小王",20);
saveDB(person );
}
});
}
}
}
嗯怎么来形容呢,就是一小时插了30万条数据,那300万条就是一天啊,那肯定是不不能够啊,改!。
那改之前先思考一下,为什么会这么慢呢?
原因也是很明显。因为我是一条一条插入的,那每次插入都是一次连接数据库的操作,那必然是非常的耗费时间,那就是要改成批量操作了。
--思路
1. 之前我是一个一个对象进行存表的那我现在就一个是先把对象全部创建出来,然后塞到List。
2. 再进行批量插入,由于我使用的是Mybatis-plus那就是使用saveBatch(List<Person>)了。
3. 那我先用多线程进行塞数据,塞完之后再进行批量插入。
开干
public class ThreadSaveDB {
public static void main(String[] args) {
int size = 3000;
ThreadPoolExecutor executor = new ThreadPoolExecutor(30,30, 10L, TimeUnit.SECONDS,new LinkedBlockingQue);
List<Person> personList = new ArrayList<>();
CountDownLatch countDownLatch = new CountDownLatch(3000);
try{
for (int i=0;i<size;i++){
executor.execute(new Runnable() {
@Override
public void run() {
Person person = new Person("小王",20);
personList.add(person);
countDownLatch.countDown();
}
});
}
countDownLatch.await();
System.out.println(personList .size());
// 到这里数据已经都拼接完成了。
// 进行批量插入操作
personDao.saveBatch(personList);
}catch(Exception e){
e.printStackTrace();
}
}
}
我这里是先测了3000条数据的情况,花费是15秒。 3000000万条数据岂不是得5,6个小时?这怎么行,改!
这时候我同事笑眯眯的给我了一段代码:rewriteBatchedStatements=true
,并且让我加在数据库配置那。
spring.datasource:
url: jdbc:mysql://xxxxxxx&rewriteBatchedStatements=true
然后我重新执行了下代码,我靠硬生生从15秒变成了5秒。牛批,杰哥永远的神!
然后我赶紧去百度了下这段配置起什么作用。
-- 嗯查阅资料之后我的理解是这样的
1. 如果不加这配置MySQL JDBC驱动在默认情况下会无视批处理语句。也就是你即使调用了saveBatch,它还是给你一条一条插入。
2. 只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL 。这时候我们的saveBatch才真正的起作用了。
到这里其实已经可以了,但是我还想再快,之前我的存库操作是单线程的,我可以把他改成多线程。
思路
1. 我可以开启多条线程去执行saveBatch
2. 数组组装和saveBatch在同一个线程中执行
3. 一个线程插100000条数据,我开30个线程
4. 每次批量查10000条数据
代码
public class ThreadSaveDB {
public static void main(String[] args) {
int size = 30;
ThreadPoolExecutor executor = new ThreadPoolExecutor(30,30, 10L, TimeUnit.SECONDS,new LinkedBlockingQue);
CountDownLatch countDownLatch = new CountDownLatch(30);
try{
for (int i=0;i<size;i++){
executor.execute(new Runnable() {
@Override
public void run() {
int dataSize = 100000;
List<Person> personList = new ArrayList<>();
for(int j=0;j<dataSize ;j++){
Person person = new Person("小王",20);
personList.add(person);
}
personDao.saveBatch(personList,10000);
countDownLatch.countDown();
}
});
}
countDownLatch.await();
}catch(Exception e){
e.printStackTrace();
}
}
}
最会3000条数据执行时间为1秒多,那3000000万数据的话就是20分钟左右,又快了不少,不错不错。
有哪位大哥知道有没有更好、更快的办法,我表示馋哭了。