文章目录
- 1、rpc批量查询
- 2、200、200条查询db数据
- 3、分页查询注意事项
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文章来源:https://uudwc.com/A/LakZM
3.3 如果是页面分页查询某一页的数据,而且需要字段a排序展示,则order by a asc,id asc文章来源地址https://uudwc.com/A/LakZM
- 原因:如果直接order by id,则查询出来的几条数据,不是按a字段排序的,可能不符合要求(a字段为销量,order by id查询出来的几条数据,销量可能不是最高的前几条)
example.setOrderByClause("poi_id asc, create_at asc");