背景
由于原来mysql库里面的数据量从2万增加到40万,并且mysql库缩容导致,每小时其他项目级小伙伴来调xinyun库的这张表导致cpu每次开销都在20%以上,并且该库数据可能还会继续增加,每次拉数据会延长到半小时左右,非常耗能,所以将原有的mysql表同步到es中...,但是发现es翻页到一万条数据后就会出现以下问题。
通过查询发现该问题在es使用中普遍存在,有三种方案:
一、 max_result_window 的值调至 50000。
[root@dnsserver ~]# curl -XPUT "127.0.0.1:9200/custm/_settings" -d
'{
"index" : {
"max_result_window" : 50000
}
}'
二、通过scroll分页通过游标来获取;
三、通过scroll after 来通过上下页;
通过业务的分析和尝试,发现以上的三种方案都行不通....
方案一,这个可能会影响整个集群,不能随便改;
方案二和三,因为原有的接口传过来一个页码,原接口现在暂时不支持修改,所以如果要改可能要让原来下游接换接口,并且无法返回游标过来,这个比较头大。
尝试过的方案:
将游标放到缓存中-------->存在一定风险,因为页数是一页一页翻页,如果哪天来一个突然间100页的,不支指定页码,并且还有可能存在死循环调用风险。
通过传入的页面,循环获取游标不断提交,这个时间太长了代码和时间如下:
通过指定循环到指定页码获取,发现前十页还好,后面就...
6秒多.....有点恐怖。
然后不断的寻找问题的解决方案中,不断尝试...。在同事的分享下,彻底解决了该问题,真的非常给力;
解决方案:
通过原数据库自增Id将通过条件方式进行区间条件查询,每次分页请求的时候,通过页码*条数获取指定范围的条数就解决了该问题。
代码实现
@Override
public DataRO queryByCondition(PopFlowConfigQueryAo queryAo) {
LOGGER.info("查询参数:{}", JSONObject.toJSONString(queryAo));
SearchRequestBuilder searchRequestBuilder = buildSearchRequestBuilder();
BoolQueryBuilder boolQueryBuilder = boolQuery();
if(null!=queryAo && queryAo.getPageAO()!=null && queryAo.getPageAO().getPageIndex() != 0 && queryAo.getPageAO().getPageSize()!= 0){
int page = (queryAo.getPageAO().getPageIndex()-1);
// searchRequestBuilder.setSize(queryAo.getPageAO().getPageSize());
RangeQueryBuilder rangeCreate = QueryBuilders.rangeQuery("popFlowConfigId");
int index = page * queryAo.getPageAO().getPageSize();
rangeCreate.gte(index);
int size = ((1+page)* queryAo.getPageAO().getPageSize())-1;
rangeCreate.lt(size);
boolQueryBuilder.must(rangeCreate);
}else{
searchRequestBuilder.setSize(10000);
}
searchRequestBuilder.setQuery(boolQueryBuilder);
searchRequestBuilder.addSort("popFlowConfigId", SortOrder.ASC);
LOGGER.info("pop查询{}", searchRequestBuilder.toString());
SearchResponse searchResponse = searchRequestBuilder.get();
DataRO> response = new DataRO<>();
List resultList = new ArrayList<>();
SearchHits hits=searchResponse.getHits();
int count = (int) searchResponse.getHits().totalHits;
LOGGER.info("查询总条数{}",count);
List list = BeanUtil.transformListBean(hits, PopFlowConfigRo.class);
if (list != null && list.size() > 0) {
resultList.addAll(list);
}
response.setData(resultList);
return response;
}
通过排序popFlowConfigId为正序;
再通过原数据库主键:popFlowConfigId 进行区间的条件查询,比如
传入页码为1的时候通过代码:
默认减1当成0,由于es也是通过区间rangeQuery
popFlowConfigId>= 0 * size = 0;
popFlowConfigId<((1+page) * 1000)-1;
通过这个算出:
page = 1
form -> 0 ,size -> 999
range 0 ~ 999
page = 2
form -> 0 ,size -> 999
range 1000 ~ 1999
....
得出 from=0 size=999 这里就类似数据库的 0~999区间,这样的话也就是说,每只查出这个popFlowConfigId在这个区间中的数据,一页一页的查,并且解决了由于scroll 或scroll after无法指定页码查询问题或需要通过游标,而且性能也非常高。
优化后