后台用户数据导出时遇到的问题

练手后台:用户数据导出时遇到的问题解决记录

源代码如下

 public void subExport(HttpServletResponse response, String createTimeStart,String createTimeEnd) throws IOException {
        Map<String, Object> map = new HashMap<>();
        if (createTimeStart != null) {
            map.put("createTime1", createTimeStart);
            map.put("createTime2", createTimeEnd);
        }

        List<Subscribers> data = subscribersService.selectByMapExport(map);

        List<Map<String, Object>> list = new ArrayList<>();
        for (Subscribers subscribers : data) {
            Map<String, Object> itemX = new LinkedHashMap<>();
            itemX.put("账号", subscribers.getMobile());
            itemX.put("姓名", subscribers.getUsername());
            itemX.put("注册时间", DateUtil.format(subscribers.getLogintime(), "yyyy-MM-dd HH:ss:mm"));
            Map<String, String> memberLevel = getMemberLevel(subscribers.getMember(), subscribers.getIsmember());
            itemX.put("用户等级", memberLevel.get("memebrIcon"));
            itemX.put("用户社团", memberLevel.get("isMemberIcon"));
            itemX.put("状态", subscribers.getDatastatus() ? "可用" : "冻结");
            BigDecimal moneyGold = moneyGoldService.getGoldMoneyByUserId(subscribers.getId(), null);
            BigDecimal moneySilver = moneySilverService.getSilverMoneyByUserId(subscribers.getId(),null);
            BigDecimal venosa = venosaService.getVenosaByUserId(subscribers.getId(),null);
            itemX.put("钻石余额",venosa);
            itemX.put("余额", moneyGold);
            itemX.put("优惠券", moneySilver);
            list.add(itemX);
        }

        String tempPath = System.getProperty("java.io.tmpdir") + IdUtil.fastSimpleUUID() + ".xlsx";
        File file = new File(tempPath);
        BigExcelWriter bigExcelWriter = ExcelUtil.getBigWriter(file);
        bigExcelWriter.write(list, true);
        response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode("用户信息.xlsx", "UTF-8"));
        ServletOutputStream out = response.getOutputStream();
        file.deleteOnExit();
        bigExcelWriter.flush(out, true);
        IoUtil.close(out);
    }

使用导出功能时上述代码在用户数据过五千,订单数据过万的情况下一次导出需要30-40秒的时间,用户在使用时不清楚情况会进行多次点击,后台没有进行重复请求拦截,结果就会导致整个后台与数据访问相关的功能卡死,所以优化方向初始定在两个方面:

1. 进行重复请求的拦截
2. 对于和用户数据交互的金额数据(其他表)数据进行单独存储,不在依靠记录加减进行实时查询

  • 重复请求拦截
String exportLock = "subExport:"+createTimeEnd;
        String lockValue = UUID.randomUUID().toString().replace("-", "");
        boolean isLock = redisUtil.getLock(exportLock,lockValue,10);
        if (!isLock){
            throw new SelfRuntimeException(new NetState(NetState.FEAILED, "请勿重复点击!"));
        }

后台使用**“subExport:”+createTimeEnd**作为键名,UUID为值,创建键值对,建立Redis锁(相关介绍就不贴了),鉴于当前导出的数据量初试时间给了10s,用户进行导出请求时,后台会在缓存中查询此次访问的redis锁,查询到则进行异常抛出,达到重复请求拦截的效果

前端JSP页面对按键进行置灰操作(前端也可在访问时携带请求头或者session进行重复请求拦截)

  • 交互数据处理
//            Map<String, String> memberLevel = getMemberLevel(subscribers.getMember(), subscribers.getIsmember());
//            itemX.put("用户等级", memberLevel.get("memebrIcon"));
//            itemX.put("用户社团", memberLevel.get("isMemberIcon"));
//            itemX.put("状态", subscribers.getDatastatus() ? "可用" : "冻结");
//            BigDecimal moneyGold = moneyGoldService.getGoldMoneyByUserId(subscribers.getId(), null);
//            BigDecimal moneySilver = moneySilverService.getSilverMoneyByUserId(subscribers.getId(),null);
//            BigDecimal venosa = venosaService.getVenosaByUserId(subscribers.getId(),null);
//            itemX.put("钻石余额",venosa);
//            itemX.put("余额", moneyGold);
//            itemX.put("优惠券", moneySilver);

实时查询的数据存储在用户钱包数据表中,表使用userId作为主键,包含钻石、余额以及优惠券字段,
在进行金额数据相关操作(例如:付款,商城抵扣购买等等)时,先将金额记录插入记录表,之后修改用户钱包表记录(详细操作在之后的文章中贴出),并进行记录,对于失败的操作,可通过xxJob进行定时或者手动补偿

  1. 补充
    在进行上述两步优化后,导出时间还是会经常达到10s以上,达不到使用要求,通过JMeter检测到内存占用逐步加大至80,通过:
log.warn("开始时间:"+DateUtil.now());
log.warn("结束时间:"+DateUtil.now());

圈定最消耗时间的代码块,发现问题代码:

 List<Map<String, Object>> list = new ArrayList<>();

此段代码使用ArrayList存储导出数据,ArrayList初始长度为10,超过10则会进行动态扩容,扩容过程会消耗内存和CPU,由于查询数据

List<Subscribers> data = subscribersService.selectByMapExport(map);

已知了data的长度,所以给与ArrayList一个初始长度:data.size(),情况得到改善,一次导出时间缩减到2-3s

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值