记一次百万数据excel导入数据库的处理过程

场景:现在需要导入账号角色关系数据,数据量:10万----百万。之前代码处理逻辑复杂,运用poi导入技术,业务处理中操作数据库为单次操作,访问数据库次数多,最后批量导入为同步导入,导致数据量过大时,接口响应时间很长。

分析:

首先,excel数据量过大,导入数据也需要验证操作,此处为了省事,采用easyexcel进行大数据量的导入;

第二:将业务进行拆分梳理,业务中会有删除操作,对于excel中账号关联的角色关系需要进行删除,我们这里将所有账号梳理出来进行批量删除,使操做数据库的次数减少;删除时,数据量没有超过10万,操作一次数据路;超过10万,每10万分批删除。

第三:向数据库批量新增数据时,采用多线程处理。2万条数一个线程,每个线程里2000条数据插入一次。

改进结果:10万数据处理完不到1分钟,100万数据不到4分钟。

注意:多线程的参数要根据数据量进行合理的配置,否则线程无用。

以下是部分代码片段:

list拆分方法:

 /**
     *         把list拆分成指定大小的list
     * @param resList
     * @param count
     * @param 
     * @return
     */
    public  List> splitList(List resList, int count) {
        if (resList == null || count < 1)
            return null;
        List> ret = new ArrayList>();
        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 itemList = new ArrayList();
                for (int j = 0; j < count; j++) {
                    itemList.add(resList.get(i * count + j));
                }
                ret.add(itemList);
            }
            // last的进行处理  
            if (last > 0) {
                List itemList = new ArrayList();
                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 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> splitList = splitList(list, 20000);
        // 记录单个任务的执行次数
        CountDownLatch countDownLatch = new CountDownLatch(splitList.size());

        for (List userAppRoles : splitList) {
            // 线程池执行
            threadPool.execute(()->{
                log.info("current thread:"+Thread.currentThread()+" is running");
                    try {
                        Integer addNum = 0;
                        if (userAppRoles != null && userAppRoles.size() > 0) {//新增
                            List> splitRoleList = splitList(userAppRoles, 2000);
                            for (List 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));
        }

    }

你可能感兴趣的:(excel)