通過業務自增id徹底解決es深翻頁的問題Caused by QueryPhaseExecutionException Result window is too l

背景

由於原來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倍,呵呵!

各位大佬有更好方案歡迎探討。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章