记一次数据批量插入实践

背景:

        2023月7月份入职新公司,初来乍到还没参入到具体的项目中,技术负责人安排写一个批量处理数据的服务,于是便有了以下文章。

        数据流程大概是这样,从clickhouse表中获取数据,并从elasticserach中根据业务序号增加若干属性,批量保存到mysql中,最后再更新clickhouse中数据状态为已生成。

        本文以10000条数据示例(本机环境 docker(mysql+clickhouse)、es测试服务器环境)。

思考:

        很简单的处理流程,刚参加工作的时候这样的服务写过很多次(jdbc Class.forName Connection PreparedStatment Resulset一些列操作),顺手拈来的事情。

  • 技术框架:springboot+mybatis plus+spring data elasticserach + dynamic datasource
  • 实现思路:
  1. orm框架选择mybatis处理mysql和clickhouse,使用mybatis plus封装好的saveOrUpdateBatch方法省得自己写sql,
  2. 使用spring data elasticserach封装好的方法进行es查询;
  3. 要同时操作clickhouse和mysql并且需要支持事务,选择dynamic datasource;       

        和相关同事沟通每天的数据量大概在5-6K(这里解释下数据量为什么自己不去看,内网环境需要vpn,刚入职暂未分配账号),于是开始了编码工作。

过程实践:

        定义方法:

         

        1、生成保存到mysql的数据;

                1.1、clickhouse批量查询数据;

                1.2、查询数据结果根据业务编号从es中查询数据信息;

                1.3、2个结果合并属性并生成mysql入库对象集合。

                部分代码:                        

public List<QcBaseDataInfo> generate(String day, int limit) {
        /**
         * 1、从clickhouse取昨日数据
         * 2、从es中取数据(根据序列号)
         * 3、封装智能质检数据
         */
        Assert.notNull(day, "业务数据日期不允许为空");
        List<ChWicDataInfo> chWicDataInfos = queryFormCh(day, limit);
        List<QcBaseDataInfo> qcBaseDataInfoList = getPropertiesFromEs(chWicDataInfos);
        return qcBaseDataInfoList;
    }

@Override
public List<ChWicDataInfo> queryFormCh(String day, int limit) {
        Assert.notNull(day, "业务数据日期不允许为空");
        List<ChWicDataInfo> queryResult = chWicDataInfoService.list(new QueryWrapper<ChWicDataInfo>().lambda()
                .eq(ChWicDataInfo::getAcctDay, day)
                .eq(ChWicDataInfo::getIsDown, STATUS_ASR_DEAL_FINISHED)
                .eq(ChWicDataInfo::getHadToDataLake, false)
                .last(" limit " + limit));
        return queryResult;
    }

@Override
public List<QcBaseDataInfo> getPropertiesFromEs(List<ChWicDataInfo> chWicDataInfos) {
        List<String> serialNumbers = new ArrayList<>();
        chWicDataInfos.forEach(q -> {
            serialNumbers.add(q.getId());
        });
        log.info("start query serialNumbers:{} from es", serialNumbers);
        List<EsWicDataInfo> esWicDataInfos = esWicDataRepository.findBySerialNumberIn(serialNumbers);
        log.info("query from es end...");
        List<QcBaseDataInfo> qcBaseDataInfoList = construct(chWicDataInfos, esWicDataInfos);
        return qcBaseDataInfoList;
    }

        2、数据批量保存/更新到mysql中;

                部分代码:                

@Override
 public boolean saveOrUpdateBatch(List<QcBaseDataInfo> data) {
        boolean result = qcBaseDataInfoService.saveOrUpdateBatch(data, BATCH_SIZE);
        return result;
 }

        3、clickhouse中更新数据状态为已更新;

                部分代码:           

本地测试后放到生产环境执行,发现5万多数据2个多小时才执行完,纳尼,不能够啊。

于是开始想问题出在了那里,开始认为可能是es查询费事,经测试后无问题;

batchSize(原设置为2000)设置的过大?测试后亦无问题;

自始至终就没想过是mybatis plus的批量更新updateBatchById的问题。

一直找不到问题于是便开始了分步骤测试。 

分步骤测试:

        思路:针对每步操作进行测试,排查用时过长的操作。        

  • mysql保存 10000条 batchSize=1024

        批量保存用时:saveBatch 23s

        批量保存或更新用时:saveOrUpdateBatch 111s

  • mysql保存+clickhouse更新 10000条 batchSize=1024

        批量保存用时:saveBatch 600s

        批量保存或更新用时:saveOrUpdateBatch 未测试

  • mysql保存+clickhouse更新 10000条 batchSize=1024 替换mybatis plus updateBatchById 为 update table set column={value} where id in ({ids})

        批量保存用时:saveBatch 22s

        批量保存或更新用时:saveOrUpdateBatch 180S

        生产环境每天的数据量约60k/每天,现每天约18分钟处理完成,执行效率提高了好几个数量级。

        排查到是mybatis plus的updateBatchById耗时过长,查看了源代码是批量执行,不知道为啥这么慢。

        mybatis plus updateBatcheById源代码: 

        

总结:

        一次小事情反应出一个大问题,想当然的以为框架封装的方法肯定是最优的,加上经验使然导致了本次问题,以下一句话可总结,分享。

        纸上得来终觉浅,觉知此事要躬行。

        纸上得来终觉浅,觉知此事要躬行。

        纸上得来终觉浅,觉知此事要躬行。

本文完。

20230809更新:

业务表有自增主键没在代码中维护,代码中使用的业务序列号做为id

 mybatis plus的savaOrUpdateBatch会先按serialNumber进行查询,存在则更新不存在则插入,于是想把serialNumber做成唯一索引执行应该会更快?果然经测试10000条数据耗时减少了一半。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

myskybeyond

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值