一次定时任务优化

背景:从多数据平台rpc拉取数据,整合->计算->入库

问题:内存占用高,计算过程用时过长

数据量不算很大,总共6个远程接口,每个接口数据量在4万条左右,数据库mysql(innodb引擎)

优化步骤:

1.针对关键查询建联合索引(必备步骤)

2.所有需要多次循环对比赋值的地方,list转换成map实现(效果相当明显,本机从2分多钟降至37秒,见代码)

3.远程日志只打印关键信息,如获取结果和条数,无需打印明细(对于任务的速度提升不明显,但是可以节省内存和存储空间)

4.大对象在初始化时指定大小,防止扩容带来的性能开销,在finally里面手动清除,不等待GC回收(此步在数据量多的情况下实用)

5.批量插入数据方式从一次语句插入改为分批次(3000条)插入一次,任务的执行速度发生了质的提升从3分半提升到1分半,内存也运行稳定,保持在1.3G左右,没再发生容器重启的情况

针对第2步的代码实现

//一般写法,嵌套循环比较
List<PubRateTerminalRespDto> sellTerminalList = getSellTerminalListAfterCalc(projectSellTerminalRespList, FinancialConstants.DIMENSION_PROJECT);

List<PubRateTerminalRespDto> pubRateList = getPubRateListAfterCalc(projectPubRateRespList, FinancialConstants.DIMENSION_PROJECT);

for (PubRateTerminalRespDto sellTerminal : sellTerminalList) {
            for (PubRateTerminalRespDto pubRate : pubRateList) {
                if (sellTerminal.getProjectId().equals(pubRate.getProjectId())) {
                    sellTerminal.setXjRate(pubRate.getXjRate());
                    sellTerminal.setCityId(pubRate.getCityId());
                    sellTerminal.setCrmId(pubRate.getCrmId());
                }
            }
        }

//优化写法,将list转换成map,在流中分组,filter中的内容必不可少,否则会报空指针,比嵌套循环高效许多
        List<PubRateTerminalRespDto> pubRateList = getPubRateListAfterCalc(projectPubRateRespList, FinancialConstants.DIMENSION_PROJECT);
        //转换为map
        Map<String, List<PubRateTerminalRespDto>> pubRateMap = pubRateList.stream().filter(vo -> StringUtils.isNotBlank(vo.getProjectId())).collect(Collectors.groupingBy(PubRateTerminalRespDto::getProjectId));

        for (PubRateTerminalRespDto sellTerminal : sellTerminalList) {
            if(CollectionUtils.isNotEmpty(pubRateMap.get(sellTerminal.getProjectId()))) {
                PubRateTerminalRespDto pubRate = pubRateMap.get(sellTerminal.getProjectId()).get(0);
                sellTerminal.setXjRate(pubRate.getXjRate());
                sellTerminal.setCityId(pubRate.getCityId());
                sellTerminal.setCrmId(pubRate.getCrmId());
            }
        }

针对第4步:

FinancialConstants.POINTS_NUMS = 40000;

        List<DataPlatformResponseDto> projectSellTerminalRespList = new ArrayList<>(FinancialConstants.POINTS_NUMS);

        List<PubRateTerminalRespDto> sellTerminalList = new ArrayList<>(FinancialConstants.POINTS_NUMS);

        List<DataPlatformPubRateResponseDto> projectPubRateRespList = new ArrayList<>(FinancialConstants.POINTS_NUMS);

        List<PubRateTerminalRespDto> pubRateList = new ArrayList<>(FinancialConstants.POINTS_NUMS);

        Map<String, List<PubRateTerminalRespDto>> pubRateMap = new HashMap<>(FinancialConstants.POINTS_NUMS);

        List<PointsMappingResponseDto> pointsMappingRespList = new ArrayList<>(FinancialConstants.POINTS_NUMS);

        Map<String, List<PointsMappingResponseDto>> pointsMappingMap = new HashMap<>(FinancialConstants.POINTS_NUMS);

try{
//业务代码
}finnaly{
 projectSellTerminalRespList.clear();
            sellTerminalList.clear();
            projectPubRateRespList.clear();
            pubRateList.clear();
            pubRateMap.clear();
            pointsMappingRespList.clear();
            pointsMappingMap.clear();
}

现在还有点问题:

代码中用了批量插入语句,预发布环境上任务跑一会儿容器会重启,内存问题

 insert into points_daily 

values
        <foreach collection="insertPointsDailyList" item="item" separator=",">

这种写法会拼装sql,4万条执行完成需要1分钟多一点

查看数据库对单语句长度的限制

select @@max_allowed_packet;   配的一个G,也就是说语句拼接到一个G就会报错

进行第5步优化:

private static final int BATCH_COUNT = 3000;

public void batchInsertDaily(List<FinancialPointsDaily> pointsDaily) {
        int index = 0;
        while (true) {
            if (index + BATCH_COUNT >= pointsDaily.size()) {
                financialMapper.insertPointsDaily(pointsDaily.subList(index, pointsDaily.size()));
                break;
            } else {
                financialMapper.insertPointsDaily(pointsDaily.subList(index, index + BATCH_COUNT));
                index = index + BATCH_COUNT;
            }
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值