美文网首页
数据量大的情况下批量处理总结

数据量大的情况下批量处理总结

作者: 蒋仁勇 | 来源:发表于2018-09-01 15:34 被阅读0次
    记得在第一家公司做一个需求时,遇到了数据库瓶颈,一条查询视图的SQL老是超时,放在现在估计是看索引,看SQL分析了像这样: SQL分析.png

    然而当时不懂这些啊,就是记得好像是因为in后面的数据太多了,超过1000就奇差无比,我记得当时百度了N多资料,让我整出了for循环每1000条就and拼接的玩法,像这样:

                StringBuilder sb = new StringBuilder();
        for (int i = 0; i < numberList.size(); i++) {
            if ((i % 1000) == 0 && i > 0) {
                sb.deleteCharAt(sb.length() - 1);
                                 // resolved ORA-01795 problem.
                sb.append(") OR  number IN ( '" + numberList.get(i) + "',");
            } else {
                sb.append("'" + numberList.get(i) + "',");
            }
        }
                // delete the last comma    
                sb.deleteCharAt(sb.length() - 1); 
        String sql = "select *,*from table where number in ("
                + sb.toString()
                + ") and ....";
    

    很傻吧,但是最近做的项目却用到了这里的一段,这也再次提醒我积累的重要性,首先,这次的需求大概是处理照片的,涉及到文件io操作这块确实很痛苦啊,哦跑偏题了,回到正题,还记得我遇到的第一个坑就是在for循环里一遍一遍操作数据库,所以我现在写插入都是这样的:

    dateMapper.batchInsert(new ArrayList<data>() {{add(oneData); }});


    /**
     * 批量插入
     *
     * @param dataList
     */
    void batchInsert(@Param("dataList") List<data> dataList);
    
      <insert id="batchInsert">
        INSERT INTO table (ID,*,CREATE_TIME, MODIFY_TIME)
        VALUES
        <foreach collection="dataList" item="data" index="index" separator=",">
            (
            #{data.*},
            now(),
            now()
            )
        </foreach>
    </insert>
    

    看到没,就算只一条一条插入我也写成支持批量插入的:)

    然而比较尴尬的事情是,因为这个业务是有调用其他服务的,如果数据量一大(比如1万条或10万条),其他服务就挂了,而且传参也会超出长度的不是吗?,所以就有了上面积累下来的代码的作用了,我拆成50条每次去批量调接口就没有问题了。这里涉及到异步 异步.png
    ,事务 事务.png

    ,分布式事务等问题,就不细说了,说回批量的实现,借助了一开始的代码,实现如下:

    @Async
    public void add( List<data> dataAddList) {
        if (CollectionUtils.isNotEmpty(dataAddList)) {
            StringBuilder sb = new StringBuilder();
            int size = dataAddList.size();
            for (int i = 0; i < dataAddList.size(); i++) {
                sb.append(dataAddList.get(i).getId());
                sb.append(",");
                if (i > 0 && (i % 50) == 0) {
                    size -= 50;
                    int batch = i / 50;
                    batchAdd(dataAddList.subList((batch - 1) * 50, batch * 50), sb.deleteCharAt(sb.length() - 1).toString());
                    sb = new StringBuilder();
                }
                if (size < 50) {
                    for (int j = 0; j < size - 1; j++) {
                        sb.append(dataAddList.get(i + j).getId());
                        sb.append(",");
                    }
                    batchAdd( dataAddList.subList(i, dataAddList.size()), sb.deleteCharAt(sb.length() - 1).toString());
                    return;
                }
            }
        }
    }
    

    顺便安利几个项目里用到的io方法吧,真的感觉还挺好用的:

    //移动文件到目录
    FileUtils.moveFileToDirectory(file, directory, true);
    // 追加写入by FileUtils
    FileUtils.writeStringToFile(file, string, "utf-8", true);
    打开一个文件流
    FileInputStream Input = FileUtils.openInputStream(file);
    然后是因为发送文件只能发送MultipartFile 就用测试mock了一个发过去
    MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", IOUtils.toByteArray(Input));

    String md5 = DigestUtils.md5Hex(multipartFile.getBytes());
    //获取MD5,注意此处有坑,md5Hex(input)会导致mock失败只能先关流再重新开流才行,而如果先mock再获取MD5的话会导致获取的MD5值完全一致,所以我一开始是很蠢的开一遍流再关流再重新开流来解决的,后来发现用multipartFile.getBytes()替代input可以解决这个问题,至于网上的用这种input复用方法个人实验发现完全没卵用:


    inputStream复用.png

    说起积累,忍不住在最后闲聊几句,很多的时候,我们大部分程序员大部分时间在写些简单重复的代码,谁会天天写高并发,大数据处理,分布式架构等等高大上的东西呢?,这些大部分只是在面试的时候会问到,真写过有几个人写过呢,我相信写增删改查的时间一定占据了我们大部分时间。那就有很大培训班,网课,之类的给你安利各种高大上的技术,牛逼的架构什么的,其实我倒是觉得写好每一行代码才是正经事,每写一行代码想想若干年后可能会有人对你的代码一番吐槽,我相信你就不会为了赶时间完成任务而那么随意了,我记得潘爱民演讲的时候拿出他几十年写的一小段C语言代码,那已经成为微软底层的一部分了,我们现在在电脑上浏览这篇文章,我在编辑的时候就会用到这行代码。这是对细节上的讲究,谈回积累,很多人写了N多代码却没有任何复盘和总结,实现过就完了,下次再遇到类似希求又写一遍,有意思吗?不是说鼓励大家存工作的代码,事实上每个程序员签劳动合同的同时都会签订保密协议,在第二家公司的时候也见到过有傻子把公司代码上传到GitHub上被告到法院的例子,但这不代表你不能记笔记,不能总结,不能留存复用自己凝思苦想的结果,事实上,我在简书上写这些,也就是趁自己有空把自己做过的项目需求总结一下,回顾一下,积累一下,我觉得花半天时间总结一下做了一两个星期或几个月的项目绝对比在电脑面前看半天网课老师吹牛逼来的实际有用得多。建议各位可以像这样

    第*周(yyyy.MM.dd-yyyy.MM.dd):双周
    周一(yyyy.MM.dd):
    周二(yyyy.MM.dd):
    周三(yyyy.MM.dd):
    周四(yyyy.MM.dd):
    周五(yyyy.MM.dd):

    写份工作日志(自己用,有日报或周报要求的也可以写)每天一句话,离职的时候,我都是直接这个一给就交接了,希望程序员路上与君共勉!

    相关文章

      网友评论

          本文标题:数据量大的情况下批量处理总结

          本文链接:https://www.haomeiwen.com/subject/kaxuwftx.html