SpringBoot框架下导入excel数据10W+处理

最近公司在做一个数据报表的功能,其中需要导入原来的手动操作的数据到现在的系统中,经过整理,数据以excel的格式导入。在处理的过程中发现,需要读取解析的数据大概10W+,以原有的处理方式,读取excel中的数据需要消耗时间2分钟,插入到数据库中需要消耗时间高达20分钟。结果就是直接系统卡死了。

后来想到的处理方式有2种:1.使用队列中间件  。 2.多线程

考虑中间件要调整的还挺多,就选择第二种方式了,解决思路是:解析excel时按sheet进行线程分解,每一次sheet数据解析完成后插入数据库中时都单独启动一个新的线程进行保存。于是着手干吧。

一开始是自己去写了一个实现线程池的类,如下:

public class AsyncConfiguration {

    /**
     * 组件计算
     */
    @Bean("excelExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数5:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);
        //最大线程数5:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(100);
        //缓冲队列500:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);
        //允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        //线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("DailyAsync-");
        executor.initialize();
        return executor;
    }
}

     线程调用类创建后,调用方式便是在需要调用的方法上使用注解@Async("excelExecutor")

 /**
     * 批量存储导入数据
     * @param list
     * @return
     * @throws Exception
     */
    @Async("excelExecutor")
    public void insertBigData(List<LclForeginFee> list)throws Exception{

        // 1、设置ExecutorType.BATCH批量模式:
        // (1.1)把SQL语句发给数据库,数据库预编译好,数据库等待需要运行的参数,接收到参数后一次运行;
        // (1.2)ExecutorType.BATCH只打印一次SQL语句,多次设置参数步骤,
        // 2、设置autoCommit=false,改为手动提交事务
        SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false);

        try {

            // 每次手动提交事务时,批次已插入数据量。如1000条一次提交。
            int batchSize = 5000;
            int size = list.size();
            LclForeginFeeMapper tMapper = sqlSession.getMapper(LclForeginFeeMapper.class);
            for (int i = 0; i < size; i++) {

                // 调用单条插入,而非batchInsert。
                // 原因:ExecutorType.BATCH+insert并非直接插入,而是数据库预编译,等待事务提交
                LclForeginFee model = list.get(i);
                tMapper.insert(model);

                // 批次提交事务
                if (i > 0 && i % batchSize == 0) {
                    // 预插入,代替commit()和closeCache()
                    sqlSession.flushStatements();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            sqlSession.rollback();
        } finally {
            // 关闭事务
            sqlSession.close();
        }
        System.out.println("执行线程任务。。。。。。。。。。。。。。。。。。。");
    }

       我的数据插入也做了处理,把数据的存储改成手动提交,数据的插入为预插入的处理方式,并且数据提交为5000条做一次事务提交。

整好了跑一次试试吧。额。。。。。好像没有实现多线程啊。

写了测试方法试了一下,的确还是单线程执行的。咋回事。搜索吧。

答案就是在同一个类中,一个方法调用另外一个有@Async注解的方法,注解不会生效.

@Async注解会在以下几个场景失效:

  • 异步方法使用static关键词修饰;
  • 异步类不是一个Spring容器的bean(一般使用注解@Component@Service,并且能被Spring扫描到);
  • SpringBoot应用中没有添加@EnableAsync注解;
  • 在同一个类中,一个方法调用另外一个有@Async注解的方法,注解不会生效。原因是@Async注解的方法,是在代理类中执行的。

赶紧把注解了@Async的方法移植到新的类中。再试一次。成功了。经过测试,10W+的数据处理完成后消耗的时间大概在3-8分钟。只要将前端调用处理成异步调用

让前端可以做出插入进度的提醒,后端进行插入。可以不影响用户进行其他的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值