這篇文章給大家介紹ES解決深分頁問題以及實現Scroll 查詢 API的示例分析,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
ES 普通的分頁查詢有深分頁限制,默認是10000條。(因為越往后面分越耗內存)所以要查詢1萬條以后的數據,要不就縮小查詢范圍,要不用別的方法。
ES 提供了 scroll 查詢。第一次查詢獲取到scroll_id,下次查詢通過scroll_id直接查詢。scroll相當于維護了一份當前索引段的快照信息,這個快照信息是你執行這個scroll查詢時的快照。在這個查詢后的任何新索引進來的數據,都不會在這個快照中查詢到。但是它相對于from和size,不是查詢所有數據然后剔除不要的部分,而是記錄一個讀取的位置,保證下一次快速繼續讀取。而且也不能排序,只能按默認的文檔順序,比較適合查詢掃描全量數據。
private static void scrollSearch(String indexName, String typeName,
String... ids) {
IdsQueryBuilder qb = QueryBuilders.idsQuery().addIds(ids);
SearchResponse sResponse = client.prepareSearch(indexName)
.setTypes(typeName)
.setQuery(qb)
.setScroll(new TimeValue(5000))
.setSize(50)
.execute()
.actionGet();
int tShards = sResponse.getTotalShards();
long timeCost = sResponse.getTookInMillis();
int sShards = sResponse.getSuccessfulShards();
System.out.println(tShards+","+timeCost+","+sShards);
while (true) {
SearchHits hits = sResponse.getHits();
SearchHit[] hitArray = hits.getHits();
for(int i = 0; i < hitArray.length; i++) {
SearchHit hit = hitArray[i];
Map<String, Object> fields = hit.getSource();
for(String key : fields.keySet()) {
System.out.println(key);
System.out.println(fields.get(key));
}
}
sResponse = client.prepareSearchScroll(sResponse.getScrollId()).setScroll(new TimeValue(5000)).execute().actionGet();
//Break condition: No hits are returned
if (sResponse.getHits().getHits().length == 0) {
break;
}
}
}// 接口定義
/**
* Scroll 游標全量數據查詢 (不支持排序,只按照doc_id排序)
*
* @param clazz 實體類類型
* @param query 查詢參數
* @param scrollId 游標
* @param size 一次拿取數據多少。
* @param <T>
* @return
*/
<T extends EEntity> EsScrollResponse<T> listByQueryScroll(Class<T> clazz, IQuery query, String scrollId,int size);
// 具體實現
@Override
public <T extends EEntity> EsScrollResponse<T> listByQueryScroll(Class<T> clazz, IQuery query, String scrollId, int size) {
// 參數校驗
if (size < 1 || size > 200) {
throw new RuntimeException("ES 查詢一次請求的數了超出范圍,請在 1-200 之間");
}
// 返回結果初始化
EsScrollResponse response = new EsScrollResponse<>();
List<T> result = new ArrayList<>();
// 獲取文檔信息
Document document = clazz.getAnnotation(Document.class);
// 全局使用的查詢返回參數初始化。
SearchResponse searchResponse = null;
// 第一次查詢沒有游標,獲取數據并記錄游標
if (StringUtils.isEmpty(scrollId)) {
SearchRequestBuilder searchRequestBuilder = esDataSource.getClient().prepareSearch(document.indexName()).setTypes(document.type()).setQuery(query.buildQuery()).setScroll(TimeValue.timeValueMinutes(ES_TIME_OUT_MINUTES)).setSize(size);
searchResponse = searchRequestBuilder.setTimeout(TimeValue.timeValueMinutes(ES_TIME_OUT_MINUTES)).execute().actionGet();
} else {
// 有游標按游標進行查詢
SearchScrollRequestBuilder searchScrollRequestBuilder = esDataSource.getClient().prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(ES_TIME_OUT_MINUTES));
searchResponse = searchScrollRequestBuilder.execute().actionGet();
}
// 處理返回結果
for (SearchHit hit : searchResponse.getHits()) {
T rt = JSON.parseObject(hit.getSourceAsString(), clazz);
result.add(rt);
}
response.setData(result);
response.setScrollId(searchResponse.getScrollId());
return response;
}
/**
* ES Scroll 查詢的封裝實體
*/
@Data
public class EsScrollResponse<T> {
/**
* 存放數據
*/
private List<T> data;
/**
* 指定游標
*/
private String scrollId;
}分頁方式 | 說明 | 優點 | 缺點 | 場景 |
|---|---|---|---|---|
| from + size | 常用的分頁方式,指定分頁大小和偏移量可以直接獲取到需要的數據。但內存消耗特別大,且速度很一般,當我們指定from = 100000,size = 10 的時候,每個node都會取出top 100000的數據,再進行匯總排序,假設3個node,那么就需要取出3*100000條數據進行排序后,再取top10的數據進行返回。所以ES默認的from+size的限制設置為10000。在數據量到達十萬級百萬級的時候這種分頁方式顯然不合理。 | 數據量小的情況使用最方便,靈活性好,實現簡單 | 內存消耗大,速度一般,數據量大的情況面臨深度分頁問題 | 數據量較小且能容忍深度分頁問題 |
| scroll(游標) | 一種快照的查詢形式,快照一旦形成,本次滾動查詢內便無法查出來新增的那些數據,而且scroll是無法進行排序的,也無法指定from,那么我們想查看指定頁碼的數據就必須將該頁數據之前的全部數據取出來再進行丟棄,所以scroll一般用于導出全量數據。 | 導出全量數據時性能最好 | 無法反應數據的實時性(因為是快照版本),維護成本高,需要維護一個 scroll_id,且不支持排序,只按照doc_id排序 | 全量數據的導出 |
關于ES解決深分頁問題以及實現Scroll 查詢 API的示例分析就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。