接口查询优化:优雅的处理大批量数据及 in 超过 1000 问题

Posted ABin-阿斌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了接口查询优化:优雅的处理大批量数据及 in 超过 1000 问题相关的知识,希望对你有一定的参考价值。

我是 ABin-阿斌:写一生代码,创一世佳话,筑一览芳华。
如果小伙伴觉得不错就一键三连吧~

文章目录

一、问题点

  • 有时候在查询数据时可能需要根据【A表】的某个 ID 去获取 【B表】 的具体信息,但是又因为数据量的庞大,我们需要对 【A表】查出来的数据结果进行分页处理之后再循环调用【B表】

二、解决方案

1、在业务层面用Java代码进行分页处理

  • 比如:通过策略ID去获取当前策略下的订单信息,那么为了防止订单信息过多我们可以这么做

  • 首先将查询获取订单信息在Service层写一个分页接口,然后进行调用
  • 具体的分页参数信息可以根据业务来定,这里进行分批处理,一页:100条
  • 然后进行循环翻页处理


2、使用工具类 List 总数切分

  • 注意: ListUtil.partition(),这个方法从某种意义上说并没有达到真正分页的效果,因为当我们使用这个方法时,List结果早已执行出来了;我们要做的就是在结果没有出来之前就减少 DB 的压力。

  • 当然,具体业务具体分析,少量的数据可以使用这个方法

  • 直接使用 Hutool 工具类中的 ListUtil.partition(list,100)方法,参数一: 具体 list、参数二: 具体分多少条

3、自己封装一个分页工具类

  • 下面这个工具类适用于 MyBatisPlus

工具类代码

public class MybatisParameterUtils 


    public static <T, F> void cutInParameter(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.in(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().in(column, objects);
            
        );
    

    public static <T, F> void cutNotInParameter(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.notIn(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().notIn(column, objects);
            
        );
    


    public static <T, F> void cutInParameter(LambdaQueryChainWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.in(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().in(column, objects);
            
        );
    

    public static <T, F> void cutNotInParameter(LambdaQueryChainWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.notIn(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().notIn(column, objects);
            
        );
    

    public static <T, F> void cutInParameter(LambdaUpdateWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.in(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().in(column, objects);
            
        );
    

    public static <T, F> void cutNotInParameter(LambdaUpdateWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.notIn(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().notIn(column, objects);
            
        );
    


    public static <T, F> void cutInParameter(LambdaUpdateChainWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.in(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().in(column, objects);
            
        );
    

    public static <T, F> void cutNotInParameter(LambdaUpdateChainWrapper<T> wrapper, SFunction<T, ?> column, List<F> coll) throws Exception 
        List<List<F>> newList = splitList(coll, 900);
        if (ObjectUtils.isEmpty(newList)) 
            throw new Exception("参数错误");
         else if (newList.size() == 1) 
            wrapper.notIn(column, newList.get(0));
            return;
        

        wrapper.and(i -> 
            i.in(column, newList.get(0));
            newList.remove(0);
            for (List<F> objects : newList) 
                i.or().notIn(column, objects);
            
        );
    


    public static <F> List<List<F>> splitList(List<F> list, int groupSize) 
        int length = list.size();
        // 计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize;
        List<List<F>> newList = new ArrayList<>(num);
        for (int i = 0; i < num; i++) 
            // 开始位置
            int fromIndex = i * groupSize;
            // 结束位置
            int toIndex = Math.min((i + 1) * groupSize, length);
            newList.add(list.subList(fromIndex, toIndex));
        
        return newList;
    

案例展示

  • 思路就是把条件拆成小于 1000 的组合条件 写 xml 同理
  //                                    这是一个条件wrapper    get方法的方法引用    一个参数list   
    MybatisParameterUtils.cutInParameter(deleteInfoWrapper,   Vo::getId,          list);
  • 要带进去 In 的参数,这个参数有数量可能会很大
  • 参数一:wrapper;参数二:要 in 那个的参数;参数三:具体带进去 in 的参数(list)

4、适用于Mybatis SQL写法

<if test="dto.idList != null and dto.idList.size() > 0">
                and (t.id IN
                <foreach collection="dto.idList" index="index" open="(" close=")" item="item" >
                    <if test="index !=0">
                        <choose>
                            <when test="index % 1000 == 999">) OR t.id IN (</when>
                            <otherwise>,</otherwise>
                        </choose>
                    </if>
                    #item
                </foreach>
                )
</if>

以上是关于接口查询优化:优雅的处理大批量数据及 in 超过 1000 问题的主要内容,如果未能解决你的问题,请参考以下文章

从3s到25ms!看看京东的接口优化技巧,确实很优雅!!

性能优化之mybatis实现接口的批量查询,减少数据库的查询消耗

基于 request cache 请求缓存技术优化批量商品数据查询接口_一点课堂(多岸学院)

多条数据批量插入优化方案

Mysql优化系列之——优化器对子查询的处理

记一次接口性能优化实践总结:优化接口性能的八个建议