导出excel方案-续篇

作者分享了一年半前从互联网公司离职后的工作经历,重点在于如何通过微服务架构优化300万记录的Excel导出,从技术挑战到解决方案。21-22年工作中,作者感叹公司文化与个人成长,并展示了提升Excel导出性能的过程和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:较上一篇文章简单使用easyexcel已经过去一年半了,https://blog.csdn.net/qq_32447321/article/details/103793475 回想去年今天,完成了首次从老东家(就不说名字了,也的确不是BAT或TMD 咋好意思说自己在互联网公司待过)交接辞职,进入一个小创业团队,之后几个月 做了一个景区小火车购票系统直到11月份验收完,之后公司经营不行,收缩至合肥,11月开始到合肥,各种原因21年3月份回上海找工作,这一年下来实际的产出也就一套系统,一套方案设计,从需求-》设计-》原型-》开发-》交付-》验收-》运维 ,套路是全了深度就呵呵,就这;对比这20-》21年的收获,一是年龄涨了岁,二:熟悉了些微服务架构设计;就这; 


展望:21-》22年,现公司每天早间新闻晨会贯彻“一切为了打仗”,警务自己“时刻准备着”,同事都百万年薪了,自己还一人吃饱就好状态,可不敢要,今天午休聊天同事修产假四个月,每天按公司平均工资发薪,四个月发了12w,卧槽,人均工资3w,太特么羡慕人了,不由得嘲讽自己,另同事说现在工资少点没关系,下家补过来,害,听起来也不是很开心,说这个呢就是说大家好好努力,思想是有边界的,你未知的幸福,不代表没有,提升自己不是负担,“大概是认知不同”阿巴阿巴


正事给忘了:这边是excel导出升级篇 ,背景:导出excel(一次 >3百万记录,十八字段,文件size: >100M),数据存储在mongodb,数据容量:< 100G

演变  导不出来 -》2min -> 30s;

talk is cheap ,show you code;具体

    // 导出订单生成excel处理池
    private ExecutorService processExportOrderExcelPool = new ThreadPoolExecutor(10, 20,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(30),
            Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());

public BaseResultVo exportOrderInfo(BaseDto dto, StationInfoQueryBO bo, HttpServletResponse response) {
        BaseResultVo vo = new BaseResultVo();
        long s1 = System.currentTimeMillis();
        try {
            int ndrcNum = SyuserSyroleMapper.countUserIsNDRC(dto.getSyuserId());
            DBObject sql = getMatchFields(dto, bo, ndrcNum);
            long totalRowCount =mongoTemplate.getCollection("notificationChargeOrderInfo").count(sql);
            //计算总数量,进行分批
            System.out.println("导出总数totalRowCount|" + totalRowCount+"|count耗时:"+(System.currentTimeMillis()-s1));
            logger.info("导出总数totalRowCount|" + totalRowCount+"|count耗时:"+(System.currentTimeMillis()-s1));
            response.setHeader("Content-disposition", "attachment;filename=exportOrder.xlsx");
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExportOrder2Bo.class).build();
            List<Future> headerflist = new ArrayList<>();
            Long toIndex = Consts.PER_SHEET_ROW_COUNT_10W.longValue(); //数据100000条处理存一次
            int sheetIndex = 0;//根据分批数创建对用数sheet
            // 这里 指定文件
            List<BlockingQueue> syncOrderLists =  Collections.synchronizedList(new LinkedList<BlockingQueue>());
            for (int i = 0; i < totalRowCount; i += Consts.PER_SHEET_ROW_COUNT_10W) {
                if (i + Consts.PER_SHEET_ROW_COUNT_10W > totalRowCount) {        //作用为toIndex最后没有100条数据则剩余几条newList中就装几条
                    toIndex = totalRowCount - i;
                }
                System.out.println("fori|"+Thread.currentThread().getName() +"|i:"+i);
                // 并发执行
                DBCollectionFindOptions copiedOptions = new DBCollectionFindOptions();
                copiedOptions.limit(toIndex.intValue());//分页条数
                copiedOptions.skip(i);//开始位置
                copiedOptions.sort(new BasicDBObject("EndTime", -1));
                headerflist.add(processExportOrderExcelPool.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
////                        //根据条件查询订单(并行处理)
//                            long c1 = System.currentTimeMillis();
                            Iterable<DBObject> dbObjects = mongoTemplate.getCollection("notificationChargeOrderInfo").find(sql,copiedOptions);
//                            long c2 = System.currentTimeMillis();
//                            System.out.println(Thread.currentThread().getName()+"|执行sql耗时:"+(c2-c1));
                            BlockingQueue queue = new LinkedBlockingQueue(new LinkedList<ExportOrder2Bo>());
//                            List<ExportOrder2Bo> synchronizedList = new LinkedBlockingQueue(new LinkedList<ExportOrder2Bo>());//Collections.synchronizedList(new LinkedList<ExportOrder2Bo>());
//                            int i=0;
                            for (DBObject next:dbObjects) {
//                                i++;
                                asyncSetObjectCallback.submit(new Runnable() {
                                    @Override
                                    public void run() {
                                        ExportOrder2Bo res = new ExportOrder2Bo();
                                        res.setStartChargeSeq((String) next.get("StartChargeSeq"));
                                        res.setCreatedTime((Date) next.get("CreatedTime"));
                                        res.setEquipmentId((String) next.get("equipmentId"));
                                        res.setConnectorID((String) next.get("ConnectorID"));
                                        res.setStationName((String) next.get("stationName"));
                                        res.setAreaName((String) next.get("areaName"));
                                        res.setOrgName((String) next.get("orgName"));
                                        res.setStartTime((Date) next.get("StartTime"));
                                        res.setEndTime((Date) next.get("EndTime"));
                                        res.setCostTime((Integer) next.get("costTime"));
                                        res.setTotalPower((Double) next.get("TotalPower"));
                                        res.setTotalElecMoney((Double) next.get("TotalElecMoney"));
                                        res.setTotalSeviceMoney((Double) next.get("TotalSeviceMoney"));
                                        res.setTotalMoney((Double) next.get("TotalMoney"));
//                                        synchronizedList.add(res);
                                        queue.add(res);
                                    }
                                });
                            }
//                            long c3 = System.currentTimeMillis();
//                            logger.info(Thread.currentThread().getName() + "|mongo返回setList大小|"+queue.size()+"|mongo sql耗时|"+(c2-c1)+"|遍历耗时:"+(c3-c2));
                            syncOrderLists.add(queue);
                        } catch (Exception e) {
                            logger.error("--------exportOrderInfo error--------:");
                            e.printStackTrace();
                        }
                    }
                }));
                sheetIndex++;
            }
//            logger.info("for headerflist阻塞等待返回|");
            //阻塞等待返回
            try {
                for (int i = 0; i < headerflist.size(); i++) {
//                    logger.info("headerflist阻塞等待返回|" + i);
                    headerflist.get(i).get();
//                    logger.info("headerflist get 阻塞等待返回|" + i);
                    if (syncOrderLists.size() >0) {
//                        long s3 = System.currentTimeMillis();
                        WriteSheet writeSheet = EasyExcel.writerSheet(i, "sheet" + i).head(ExportOrder2Bo.class).build();
                        List order=new ArrayList<ExportOrder2Bo>();
                        syncOrderLists.get(i).drainTo(order,100000);
                        excelWriter.write(order, writeSheet);
//                        long s4 = System.currentTimeMillis();
//                        logger.info("excelWriter SheetName:" + i + "|SheetNo: " + writeSheet.getSheetNo() + "|writerSheet 耗时|" + (s4 - s3));
                    }
                }

                /// 千万别忘记finish 会帮忙关闭流
                excelWriter.finish();
                System.out.println("--帮忙关闭流----");
            } catch (Exception e) {
                response.setStatus(400);
                logger.error("--------process callback error--------:", e);
            }
            long s2 = System.currentTimeMillis();
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
        } catch (Exception e) {
            logger.info("exportOrderInfo error!");
            response.setStatus(400);
            e.printStackTrace();
        }
        long s2 = System.currentTimeMillis();
        logger.info("导出订单耗时|" + (s2 - s1));
        return vo;
    }

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值