场景:现在需要导入账号角色关系数据,数据量:10万----百万。之前代码处理逻辑复杂,运用poi导入技术,业务处理中操作数据库为单次操作,访问数据库次数多,最后批量导入为同步导入,导致数据量过大时,接口响应时间很长。
分析:
首先,excel数据量过大,导入数据也需要验证操作,此处为了省事,采用easyexcel进行大数据量的导入;
第二:将业务进行拆分梳理,业务中会有删除操作,对于excel中账号关联的角色关系需要进行删除,我们这里将所有账号梳理出来进行批量删除,使操做数据库的次数减少;删除时,数据量没有超过10万,操作一次数据路;超过10万,每10万分批删除。
第三:向数据库批量新增数据时,采用多线程处理。2万条数一个线程,每个线程里2000条数据插入一次。
改进结果:10万数据处理完不到1分钟,100万数据不到4分钟。
注意:多线程的参数要根据数据量进行合理的配置,否则线程无用。
以下是部分代码片段:
list拆分方法:
/**
* 把list拆分成指定大小的list
* @param resList
* @param count
* @param <T>
* @return
*/
public <T> List<List<T>> splitList(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) {
// 数据量不足count指定的大小
ret.add(resList);
} else {
int pre = size / count;
int last = size % count;
// 前面pre个集合,每个大小都是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);
}
// last的进行处理
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;
}
/**
* 多线程处理入库操作
* @param list
* @param ipAddress
* @param userName
* @param appId
*/
public void MultithreadingInsert(List<UserAppRole> list,String ipAddress ,String userName,String appId,String toBimStatus) {
// 初始化线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(20, 50,
4, TimeUnit.SECONDS, new ArrayBlockingQueue(50), new ThreadPoolExecutor.AbortPolicy());
// 对拆分的集合进行批量处理, 先拆分的集合, 再多线程执行
List<List<UserAppRole>> splitList = splitList(list, 20000);
// 记录单个任务的执行次数
CountDownLatch countDownLatch = new CountDownLatch(splitList.size());
for (List<UserAppRole> userAppRoles : splitList) {
// 线程池执行
threadPool.execute(()->{
log.info("current thread:"+Thread.currentThread()+" is running");
try {
Integer addNum = 0;
if (userAppRoles != null && userAppRoles.size() > 0) {//新增
List<List<UserAppRole>> splitRoleList = splitList(userAppRoles, 2000);
for (List<UserAppRole> addUerAppRoles : splitRoleList) {
addNum += userAppRoleDao.insertByBatch(addUerAppRoles);
}
}
log.info("current thread:"+Thread.currentThread()+" is running over , data size:"+addNum);
}catch (Exception e){
log.error(e.getMessage());
}finally {
countDownLatch.countDown();
}
});
}
try {
// 让当前线程处于阻塞状态,直到锁存器计数为零
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException("线程执行失败");
}finally {
//删除缓存
cacheService.remove(RedisKeyUtils.getUserAppRoleKey(appId));
}
}