场景:
4w单的EXECL表格,导入到订单表,校验数据后落库,响应速度超过2min,大约在8min左右,这是单线程的情况下,在使用批量插入后,仍超时2min。
于是使用多线程去处理,可能出现的问题是,如果一个线程出现失败,那么应该返回响应处理失败,已插入就绪的线程任务需要回滚,但是每个线程执行的是自己的事务,那么该如何处理呢。
原因:spring中对数据库连接是放在threadLocal里面,多线程场景下,拿到的数据库连接是不一样的,即是属于不同事务。
这里提供两种解决方案:
- 使用completeFuture,获取到每个执行线程的结果,然后等待所有线程处理完,for循环对结果进行判断,如果存在FALSE则全部线程回滚。需要注意的是,这里的回滚,需要将sql自动提交关闭,并且获取插入的mapper。
- 使用切面注解,基于springboot的
@Async
注解,避免繁琐的手动提交/回滚事务
工具类:
将大批量的数据,分为多个集合,每个线程处理一个。
/**
* 拆分集合
* @param <T>
* @param resList 要拆分的集合
* @param count 每个集合的元素个数
* @return 返回拆分后的各个集合
*/
public static <T> List<List<T>> split(List<T> resList, int count) {
if (resList == null || count < 1) {
return null;
}
List<List<T>> ret = new ArrayList<List<T>>();
int size = resList.size();
if (size <= count) {
ret.add(resList);
return ret;
}
int pre = size / count;
int last = size % count;
for (int i = 0; i < pre; i++) {
List<T> itemList = new ArrayList<T>();
for (int j = 0; j < count; j++) {
itemList.add(resList.get(i * count + j));
}
ret.add(itemList);
}
if (last > 0) {
List<T> itemList = new ArrayList<T>();
for (int i = 0; i < last; i++) {
itemList.add(resList.get(pre * count + i));
}
ret.add(itemList);
}
return ret;
}
第一种:
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
//自定义线程池
private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors() * 2,
Runtime.getRuntime().availableProcessors() * 4,
60L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(256),
new ThreadPoolExecutor.CallerRunsPolicy()
);
@Override
@Transactional
public Result importExcel(ArrayList<StudentDto> studentDtoArrayList) throws