批量循环查询

1、rpc批量查询

    private List<DTO> query(String code, Integer type) {

        List<DTO> resultList = new ArrayList<>();
        List<DTO> skuList;
  
        //01.构造req
        Req request = buildTRequest(code, type);
        Response response;
        int fetchedCount = 0;
        int maxCycle = 1;
        boolean needMoreFetch = true;
        while (maxCycle <= 1000) {
            maxCycle++;
            try {
                response = skuGateway.query(request);
            } catch (Exception e) {
                throw new GatewayException(1,
                        String.format("获取sku失败 code:[%s]", code), e);
            }
            skuList = response.getData();
            
            if (CollectionUtils.isNotEmpty(skuList)) {
                resultList.addAll(skuList);
                fetchedCount += skuList.size();
            }

            if (total <= fetchedCount) {
                needMoreFetch = false;
            } else {
                request.setOffset(fetchedCount);
            }
        }
        return resultList;
    }

2、200、200条查询db数据

  • eg1
private void query(Request request) {

        int page = 1;
        List<DO> dOList = query(request, page);
        while (CollectionUtils.isNotEmpty(dOList) && page <= 1000) {//一次200,循环1000次,就是20w的数据量,肯定够了
            //处理数据
            judgeSameTask(dOList);
            if (200 > dOList.size()) {
                break;
            }
            page++;
            dOList = query(request, page);
        }
    }
  • eg2
private List<Info> batchQuery(Request request) {
        List<Info> result = new ArrayList<>();
        int page = 1;
        List<Info> infos = query(request, page);
        while (CollectionUtils.isNotEmpty(infos) && page <= 1000) {//一次200,循环1000次,就是20w的数据量,肯定够了
            result.addAll(infos);
            if (200 > infos.size()) {
                break;
            }
            page++;
            infos = query(request, page);
        }
        return totalSkuStockList;
    }


private List<ReturnPlanTaskDO> query(Request request, int page) {

        //01.构建查询条件
        XxxDOExample example = buildDOExample();

        //02.分页参数
        example.limit((page - 1) * 200, 200);

        //03.查询
        return myService.query(example);
    }
  • eg3
    private List<Info> batchQuery(TRequest request) {
        List<Info> result = new ArrayList<>();
        int page = 1;
        List<Info> infoList = query(request, page);
        while (CollectionUtils.isNotEmpty(infoList) && page <= 1000) {//一次200,循环1000次,就是20w的数据量,肯定够了
            result.addAll(infoList);
            if (200 > skuStockSaleInfos.size()) {
                break;
            }
            page++;
            infoList = query(request, page);
        }
        return result;
    }

    private List<Info> query(TRequest request, int page) {
        //01.构建查询条件
        Paging paging = new Paging();
        paging.setOffset((page - 1) * 200);
        paging.setLimit(200);
        request.setPaging(paging);

        //02.查询
        TResponse response = skuService.query(request);
        if (response != null && response.getCode() == 0) {
            return response.getInfoList();
        } else {
            return Collections.emptyList();
        }
    }
  • eg4
List<DO> result = Lists.newArrayList();
boolean loop = true;
long id = 0L;
do {
    XxxDOExample example = new XxxDOExample();
    example.limit(200);
    example.setOrderByClause("id asc");
    XxxDOExample.Criteria criteria = example.createCriteria();
    criteria1.andValidEqualTo(Boolean.TRUE);
    criteria1.andIdGreaterThan(id);
    List<DO> selectByExample = myMapper.selectByExample(example);
    if (CollectionUtils.isNotEmpty(selectByExample)) {
    result.addAll(selectByExample);
    int size = selectByExample.size();
    if (size < 200) {
        loop = false;
    } else {
        id = selectByExample.get(size - 1).getId();
    }
} while (loop);

第一次查询0-100,数据。(过滤或者处理一下这100条数据)后,存入list中,fetchCount = 100,

第二次查询100-200。(过滤或者处理一下这100条数据)后,存入list中,fetchCount = 200,

···

直到第10次处理完,fetchCount == total(1000)了,needFalse = false,停止查询

补充:为什么采用这种,每次查询100条,(处理或不处理)存入list,直到全部1000条都(处理或不处理)存入list。而不是直接一次查询出这1000条数据

解:因为如果你不查询一次,你根本就不知道数据库中有1000条满足条件的数据。所以,你也可以先查询一次limit offset(0-1)查出total = 1000,此时你再执行sql查询page(0-1000)一次全部查出来放入list

limit 20000 , 10:从第20001行数据开始查找,查找10条(20001、20002、····200010)=》 where id 〉 20000 limit 0 ,10。PageModel即父类RowBounds(mysql中的)即这种形式limit、offset

limit 10 offset 3 :(从第4行开始查找,找10条数据),即4、5、6、···13。

<if test="rows != null">
      <if test="offset != null">
        limit ${offset}, ${rows}    偏移量、多少条
      </if>
      <if test="offset == null">
        limit ${rows}
      </if>
    </if>
    这里limit ${offset}, ${rows}   对比 limit 20000 , 10
    即offset是偏移量,从第20001开始查询,查10条。 这里是limit 0,1000 从第一条开始查查询1000条

  • eg5
return Lists.partition(skuIds, 200).stream().map(skus -> {
	XxxDOExample example = new XxxDOExample();
example.createCriteria().andSkuIdIn(skus)
                    .andValidEqualTo(Boolean.TRUE);
            return example;
        }).map(example -> myMapper.selectByExample(example))
                .reduce((list1, list2) -> {
                    list1.addAll(list2);
                    return list1;
         }).orElse(Lists.newArrayList());

3、分页查询注意事项

只要使用分页查询,必须加上order by

1、原因

不使用order by,分页查询的数据不准确

  • 背景:

    select查询,如果不走二级索引查询,查询结果默认是按照主键id,正序排序;

    如果走了二级索引,则结果默认按二级索引排序展示

  • 问题复现

    select limit 0,5,使用了二级索引A进行查询,则结果按照索引A正序排序,为:7,8,9,1,2;

    又进行了select 5,10查询第二页;

    此时,因为数据库原因(主从切换,原本该走从查询的,现在走主查询了),或数据量变化(原本数据量级该走A索引查询的,现在走B索引查询了)

    select limit 5,5,使用了二级索引B进行查询,则结果按照索引B正序排序,为:11,12,13,14,2;导致id = 2的数据,在第一页 和 第二页分页查询结果中都出现了!!!甚至会导致数据丢失

2、解:

分页查询一定使用order by

3、SOP

3.1 分页查询全量数据(200、200、200 —)

  • 无脑order by id asc即可

  • 原因:

    此时如果order by 字段a,会有以下问题

    • 字段a,不是联合索引一员,导致走不到索引

    • 字段a,是联合索引一员,但是,中间断了x_y_z_a,但是本次查询条件仅有x_y ,order by a,也会使索引失效

    • 字段a,是联合索引一员,且连续,能走到索引,但是order by a字段,a字段有很多相同的数据。比如a字段为仓id

      select order by a ,limit 0, 5 ,结果为7,8,9,1,2

​ select order by a ,limit 5, 5 ,结果为2,11,12,13,14

​ 原因就是a字段有很多相同的数据,order by a,前后两页也有可能有相同数据

​ 比如联合索引为valid_poiId,select * from table where valid = 1 limit 0,200这个时候查出来的数据都是valid = 1的,因此查询结果会按照联合索引中的第二个字段poiId正序排序的!!!

即生效sql实际上为select * from table where valid = 1 order by poiId asc limit 0,200

问题:当poi_id有序增长时,id的增长并不有序,甚至可能跨度很大!!!,导致翻页时,如果使用了granThanId方式,取最后一条数据的id可能跳跃很大,导致漏数据。

复现:

查询结果 poiId id

​ 1 10009577 777

​ 2 10001014 77777777

这样即使查询出来的两条数据按poiId排序挨在一起,但是id却跨度很大很大。上一次granthanId的id为776,不会丢数据。这一次granThanid的id为77777777,id > 77777777,这样777- 77777777中间很多id的数据都没查出来!!!相当于丢数据了

所以,此时必须添加一个能确定顺序的索引,即order by a asc,id asc 。不如,直接order by id

3.2 分页查询全量数据,建议使用greathanId

3.3 如果是页面分页查询某一页的数据,而且需要字段a排序展示,则order by a asc,id asc

  • 原因:如果直接order by id,则查询出来的几条数据,不是按a字段排序的,可能不符合要求(a字段为销量,order by id查询出来的几条数据,销量可能不是最高的前几条)
example.setOrderByClause("poi_id asc, create_at asc");
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值