溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ES解決深分頁問題以及實(shí)現(xiàn)Scroll 查詢 API的示例分析

發(fā)布時間:2021-12-07 11:06:43 來源:億速云 閱讀:561 作者:柒染 欄目:大數(shù)據(jù)

這篇文章給大家介紹ES解決深分頁問題以及實(shí)現(xiàn)Scroll 查詢 API的示例分析,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

一、前言:

ES 普通的分頁查詢有深分頁限制,默認(rèn)是10000條。(因?yàn)樵酵竺娣衷胶膬?nèi)存)所以要查詢1萬條以后的數(shù)據(jù),要不就縮小查詢范圍,要不用別的方法。

ES 提供了 scroll 查詢。第一次查詢獲取到scroll_id,下次查詢通過scroll_id直接查詢。scroll相當(dāng)于維護(hù)了一份當(dāng)前索引段的快照信息,這個快照信息是你執(zhí)行這個scroll查詢時的快照。在這個查詢后的任何新索引進(jìn)來的數(shù)據(jù),都不會在這個快照中查詢到。但是它相對于from和size,不是查詢所有數(shù)據(jù)然后剔除不要的部分,而是記錄一個讀取的位置,保證下一次快速繼續(xù)讀取。而且也不能排序,只能按默認(rèn)的文檔順序,比較適合查詢掃描全量數(shù)據(jù)。

1、看一個DEMO
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;
            }
        }
    }
2、自己實(shí)現(xiàn)的基礎(chǔ)API
// 接口定義
 
 
/**
 * Scroll 游標(biāo)全量數(shù)據(jù)查詢 (不支持排序,只按照doc_id排序)
 *
 * @param clazz 實(shí)體類類型
 * @param query 查詢參數(shù)
 * @param scrollId 游標(biāo)
 * @param size 一次拿取數(shù)據(jù)多少。
 * @param <T>
 * @return
 */
<T extends EEntity> EsScrollResponse<T> listByQueryScroll(Class<T> clazz, IQuery query, String scrollId,int size);
 
 
// 具體實(shí)現(xiàn)
@Override
public <T extends EEntity> EsScrollResponse<T> listByQueryScroll(Class<T> clazz, IQuery query, String scrollId, int size) {
    // 參數(shù)校驗(yàn)
    if (size < 1 || size > 200) {
        throw new RuntimeException("ES 查詢一次請求的數(shù)了超出范圍,請在 1-200 之間");
    }
    // 返回結(jié)果初始化
    EsScrollResponse response = new EsScrollResponse<>();
    List<T> result = new ArrayList<>();
    // 獲取文檔信息
    Document document = clazz.getAnnotation(Document.class);
    // 全局使用的查詢返回參數(shù)初始化。
    SearchResponse searchResponse = null;
    // 第一次查詢沒有游標(biāo),獲取數(shù)據(jù)并記錄游標(biāo)
    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 {
        // 有游標(biāo)按游標(biāo)進(jìn)行查詢
        SearchScrollRequestBuilder searchScrollRequestBuilder = esDataSource.getClient().prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(ES_TIME_OUT_MINUTES));
        searchResponse = searchScrollRequestBuilder.execute().actionGet();
    }
    // 處理返回結(jié)果
    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 查詢的封裝實(shí)體
 */
@Data
public class EsScrollResponse<T> {
 
    /**
     * 存放數(shù)據(jù)
     */
    private List<T> data;
 
    /**
     * 指定游標(biāo)
     */
    private String scrollId;
}

三、兩種分頁方式比較

分頁方式

說明

優(yōu)點(diǎn)

缺點(diǎn)

場景

from + size

常用的分頁方式,指定分頁大小和偏移量可以直接獲取到需要的數(shù)據(jù)。但內(nèi)存消耗特別大,且速度很一般,當(dāng)我們指定from = 100000,size = 10 的時候,每個node都會取出top 100000的數(shù)據(jù),再進(jìn)行匯總排序,假設(shè)3個node,那么就需要取出3*100000條數(shù)據(jù)進(jìn)行排序后,再取top10的數(shù)據(jù)進(jìn)行返回。所以ES默認(rèn)的from+size的限制設(shè)置為10000。在數(shù)據(jù)量到達(dá)十萬級百萬級的時候這種分頁方式顯然不合理。

數(shù)據(jù)量小的情況使用最方便,靈活性好,實(shí)現(xiàn)簡單內(nèi)存消耗大,速度一般,數(shù)據(jù)量大的情況面臨深度分頁問題數(shù)據(jù)量較小且能容忍深度分頁問題
scroll(游標(biāo))

一種快照的查詢形式,快照一旦形成,本次滾動查詢內(nèi)便無法查出來新增的那些數(shù)據(jù),而且scroll是無法進(jìn)行排序的,也無法指定from,那么我們想查看指定頁碼的數(shù)據(jù)就必須將該頁數(shù)據(jù)之前的全部數(shù)據(jù)取出來再進(jìn)行丟棄,所以scroll一般用于導(dǎo)出全量數(shù)據(jù)。

導(dǎo)出全量數(shù)據(jù)時性能最好無法反應(yīng)數(shù)據(jù)的實(shí)時性(因?yàn)槭强煺瞻姹荆?,維護(hù)成本高,需要維護(hù)一個 scroll_id,且不支持排序,只按照doc_id排序全量數(shù)據(jù)的導(dǎo)出

關(guān)于ES解決深分頁問題以及實(shí)現(xiàn)Scroll 查詢 API的示例分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI