背景
由於原來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倍,呵呵!
各位大佬有更好方案歡迎探討。