此处我用的方案是先用多线程处理数据,处理完毕后将一共的数据分批次(每次500条,看数据结果每条的大小,mybatis使用foreach整体插入数据有上限,根据实际情况来定)向数据库里插入结果。
1.自己项目的话可以定义一个线程池的工具类,如果只想用在某个地方的话可以直接声明
此处我用的工具类,且只用的定长线程池,阿里文档不推荐,可以根据任务适当调整:
public class PoolUnit {
private static final ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
public static ExecutorService getInstance() {
return executorService;
}
}
解释一下为什么线程数要用:Runtime.getRuntime().availableProcessors() * 2
Runtime.getRuntime().availableProcessors()返回的是CPU的处理器数量
Runtime.getRuntime().availableProcessors()获取的是什么? - 知乎
2.处理作业
public class Test{
public void jobProcessingAndSaveResult(){
//处理数据的结果集
List<ResultEntity> future = new ArrayList<>();
//DataEntities是要处理的数据
for (DataEntity entity : DataEntities) {
Callable<List<ResultEntity>> result = new Callable<List<ResultEntity>>() {
@Override
public List<ResultEntity> call() throws Exception {
return jobProcessing();
}
};
//将每次循环产生的结果统一添加到future集合内
future.addAll(PoolUnit.getInstance().submit(result).get());
}
}
//处理业务代码,返回值就是想要插入数据库的结果实体类
public List<ResultEntity> jobProcessing(){
//此处是业务逻辑代码,想要怎样处理的,但不包含将结果插入数据库
}
}
3.插入结果
int runSize = 500;
int taskSize = (future.size() % runSize) == 0 ? (future.size() / runSize) : (future.size() / runSize) + 1;
int fromIndex, toIndex;
for (int n = 0; n < taskSize; n++) {
fromIndex = n * runSize;
//确保取最后数据正确
toIndex = Math.min(future.size(), fromIndex + runSize);
List<ResultEntity> subData = future.subList(fromIndex, toIndex);
ResultService.insertResultBatch(subData);
}
插入结果这部分是需要放在jobProcessingAndSaveResult方法内的,insertResultBatch是通过使用mybatis中的foreach插入的,因为500条数据插入只请求一次数据库,时间方面会节省很多。