通过业务自增id彻底解决es深翻页的问题Caused by QueryPhaseExecutionException Result window is too l

    技术2022-07-11  98

    背景

    由于原来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无法指定页码查询问题或需要通过游标,而且性能也非常高。

    优化后

    性能最少提升了10倍,呵呵!

    各位大佬有更好方案欢迎探讨。

     

    Processed: 0.031, SQL: 9