0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ElasticSearch深度分頁(yè)詳解

OSC開(kāi)源社區(qū) ? 來(lái)源:OSC開(kāi)源社區(qū) ? 作者:何守優(yōu) ? 2022-11-17 09:53 ? 次閱讀

1 前言

ElasticSearch 是一個(gè)實(shí)時(shí)的分布式搜索與分析引擎,常用于大量非結(jié)構(gòu)化數(shù)據(jù)的存儲(chǔ)和快速檢索場(chǎng)景,具有很強(qiáng)的擴(kuò)展性??v使其有諸多優(yōu)點(diǎn),在搜索領(lǐng)域遠(yuǎn)超關(guān)系型數(shù)據(jù)庫(kù),但依然存在與關(guān)系型數(shù)據(jù)庫(kù)同樣的深度分頁(yè)問(wèn)題,本文就此問(wèn)題做一個(gè)實(shí)踐性分析探討。

2 from + size 分頁(yè)方式

from + size 分頁(yè)方式是 ES 最基本的分頁(yè)方式,類(lèi)似于關(guān)系型數(shù)據(jù)庫(kù)中的 limit 方式。from 參數(shù)表示:分頁(yè)起始位置;size 參數(shù)表示:每頁(yè)獲取數(shù)據(jù)條數(shù)。例如:

GET /wms_order_sku/_search
{
  "query": {
    "match_all": {}
  },
  "from": 10,
  "size": 20
}
該條 DSL 語(yǔ)句表示從搜索結(jié)果中第 10 條數(shù)據(jù)位置開(kāi)始,取之后的 20 條數(shù)據(jù)作為結(jié)果返回。這種分頁(yè)方式在 ES 集群內(nèi)部是如何執(zhí)行的呢? 在 ES 中,搜索一般包括 2 個(gè)階段,Query 階段和 Fetch 階段,Query 階段主要確定要獲取哪些 doc,也就是返回所要獲取 doc 的 id 集合,F(xiàn)etch 階段主要通過(guò) id 獲取具體的 doc。

2.1 Query 階段

d1d8917e-65b4-11ed-8abf-dac502259ad0.png 如上圖所示,Query 階段大致分為 3 步:

第一步:Client 發(fā)送查詢(xún)請(qǐng)求到 Server 端,Node1 接收到請(qǐng)求然后創(chuàng)建一個(gè)大小為 from + size 的優(yōu)先級(jí)隊(duì)列用來(lái)存放結(jié)果,此時(shí) Node1 被稱(chēng)為 coordinating node(協(xié)調(diào)節(jié)點(diǎn));

第二步:Node1 將請(qǐng)求廣播到涉及的 shard 上,每個(gè) shard 內(nèi)部執(zhí)行搜索請(qǐng)求,然后將執(zhí)行結(jié)果存到自己內(nèi)部的大小同樣為 from+size 的優(yōu)先級(jí)隊(duì)列里;

第三步:每個(gè) shard 將暫存的自身優(yōu)先級(jí)隊(duì)列里的結(jié)果返給 Node1,Node1 拿到所有 shard 返回的結(jié)果后,對(duì)結(jié)果進(jìn)行一次合并,產(chǎn)生一個(gè)全局的優(yōu)先級(jí)隊(duì)列,存在 Node1 的優(yōu)先級(jí)隊(duì)列中。(如上圖中,Node1 會(huì)拿到 (from + size) * 6 條數(shù)據(jù),這些數(shù)據(jù)只包含 doc 的唯一標(biāo)識(shí)_id 和用于排序的_score,然后 Node1 會(huì)對(duì)這些數(shù)據(jù)合并排序,選擇前 from + size 條數(shù)據(jù)存到優(yōu)先級(jí)隊(duì)列);

2.2 Fetch 階段

d1e84ff6-65b4-11ed-8abf-dac502259ad0.png

如上圖所示,當(dāng) Query 階段結(jié)束后立馬進(jìn)入 Fetch 階段,F(xiàn)etch 階段也分為 3 步:

第一步:Node1 根據(jù)剛才合并后保存在優(yōu)先級(jí)隊(duì)列中的 from+size 條數(shù)據(jù)的 id 集合,發(fā)送請(qǐng)求到對(duì)應(yīng)的 shard 上查詢(xún) doc 數(shù)據(jù)詳情;

第二步:各 shard 接收到查詢(xún)請(qǐng)求后,查詢(xún)到對(duì)應(yīng)的數(shù)據(jù)詳情并返回為 Node1;(Node1 中的優(yōu)先級(jí)隊(duì)列中保存了 from + size 條數(shù)據(jù)的_id,但是在 Fetch 階段并不需要取回所有數(shù)據(jù),只需要取回從 from 到 from + size 之間的 size 條數(shù)據(jù)詳情即可,這 size 條數(shù)據(jù)可能在同一個(gè) shard 也可能在不同的 shard,因此 Node1 使用 multi-get 來(lái)提高性能)

第三步:Node1 獲取到對(duì)應(yīng)的分頁(yè)數(shù)據(jù)后,返回給 Client;

2.3 ES 示例

依據(jù)上述我們對(duì) from + size 分頁(yè)方式兩階段的分析會(huì)發(fā)現(xiàn),假如起始位置 from 或者頁(yè)條數(shù) size 特別大時(shí),對(duì)于數(shù)據(jù)查詢(xún)和 coordinating node 結(jié)果合并都是巨大的性能損耗。 例如:索引 wms_order_sku 有 1 億數(shù)據(jù),分 10 個(gè) shard 存儲(chǔ),當(dāng)一個(gè)請(qǐng)求的 from = 1000000, size = 10。在 Query 階段,每個(gè) shard 就需要返回 1000010 條數(shù)據(jù)的_id 和_score 信息,而 coordinating node 就需要接收 10 * 1000010 條數(shù)據(jù),拿到這些數(shù)據(jù)后需要進(jìn)行全局排序取到前 1000010 條數(shù)據(jù)的_id 集合保存到 coordinating node 的優(yōu)先級(jí)隊(duì)列中,后續(xù)在 Fetch 階段再去獲取那 10 條數(shù)據(jù)的詳情返回給客戶(hù)端。 分析:這個(gè)例子的執(zhí)行過(guò)程中,在 Query 階段會(huì)在每個(gè) shard 上均有巨大的查詢(xún)量,返回給 coordinating node 時(shí)需要執(zhí)行大量數(shù)據(jù)的排序操作,并且保存到優(yōu)先級(jí)隊(duì)列的數(shù)據(jù)量也很大,占用大量節(jié)點(diǎn)機(jī)器內(nèi)存資源。

2.4 實(shí)現(xiàn)示例

d1f8b030-65b4-11ed-8abf-dac502259ad0.png

private SearchHits getSearchHits(BoolQueryBuilder queryParam, int from, int size, String orderField) {
        SearchRequestBuilder searchRequestBuilder = this.prepareSearch();
        searchRequestBuilder.setQuery(queryParam).setFrom(from).setSize(size).setExplain(false);
        if (StringUtils.isNotBlank(orderField)) {
            searchRequestBuilder.addSort(orderField, SortOrder.DESC);
        }
        log.info("getSearchHits searchBuilder:{}", searchRequestBuilder.toString());
        SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
        log.info("getSearchHits searchResponse:{}", searchResponse.toString());
        return searchResponse.getHits();
    }

2.5 小結(jié)

其實(shí) ES 對(duì)結(jié)果窗口的返回?cái)?shù)據(jù)有默認(rèn) 10000 條的限制(參數(shù):index.max_result_window = 10000),當(dāng) from + size 的條數(shù)大于 10000 條時(shí) ES 提示可以通過(guò) scroll 方式進(jìn)行分頁(yè),非常不建議調(diào)大結(jié)果窗口參數(shù)值。 d23e80b0-65b4-11ed-8abf-dac502259ad0.png

3 Scroll 分頁(yè)方式

scroll 分頁(yè)方式類(lèi)似關(guān)系型數(shù)據(jù)庫(kù)中的 cursor(游標(biāo)),首次查詢(xún)時(shí)會(huì)生成并緩存快照,返回給客戶(hù)端快照讀取的位置參數(shù)(scroll_id),后續(xù)每次請(qǐng)求都會(huì)通過(guò) scroll_id 訪(fǎng)問(wèn)快照實(shí)現(xiàn)快速查詢(xún)需要的數(shù)據(jù),有效降低查詢(xún)和存儲(chǔ)的性能損耗。

3.1 執(zhí)行過(guò)程

scroll 分頁(yè)方式在 Query 階段同樣也是 coordinating node 廣播查詢(xún)請(qǐng)求,獲取、合并、排序其他 shard 返回的數(shù)據(jù)_id 集合,不同的是 scroll 分頁(yè)方式會(huì)將返回?cái)?shù)據(jù)_id 的集合生成快照保存到 coordinating node 上。Fetch 階段以游標(biāo)的方式從生成的快照中獲取 size 條數(shù)據(jù)的_id,并去其他 shard 獲取數(shù)據(jù)詳情返回給客戶(hù)端,同時(shí)將下一次游標(biāo)開(kāi)始的位置標(biāo)識(shí)_scroll_id 也返回。這樣下次客戶(hù)端發(fā)送獲取下一頁(yè)請(qǐng)求時(shí)帶上 scroll_id 標(biāo)識(shí),coordinating node 會(huì)從 scroll_id 標(biāo)記的位置獲取接下來(lái) size 條數(shù)據(jù),同時(shí)再次返回新的游標(biāo)位置標(biāo)識(shí) scroll_id,這樣依次類(lèi)推直到取完所有數(shù)據(jù)。

3.2 ES 示例

第一次查詢(xún)時(shí)不需要傳入_scroll_id,只要帶上 scroll 的過(guò)期時(shí)間參數(shù)(scroll=1m)、每頁(yè)大?。╯ize)以及需要查詢(xún)數(shù)據(jù)的自定義條件即可,查詢(xún)后不僅會(huì)返回結(jié)果數(shù)據(jù),還會(huì)返回_scroll_id。

GET /wms_order_sku2021_10/_search?scroll=1m
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "shipmentOrderCreateTime": {
              "gte": "2021-10-04 0000",
              "lt": "2021-10-15 0000"
            }
          }
        }
      ]
    }
  },
  "size": 20
}
d26179d0-65b4-11ed-8abf-dac502259ad0.png

第二次查詢(xún)時(shí)不需要指定索引,在 JSON 請(qǐng)求體中帶上前一個(gè)查詢(xún)返回的 scroll_id,同時(shí)傳入 scroll 參數(shù),指定刷新搜索結(jié)果的緩存時(shí)間(上一次查詢(xún)緩存 1 分鐘,本次查詢(xún)會(huì)再次重置緩存時(shí)間為 1 分鐘)
GET /_search/scroll
{
  "scroll":"1m",
  "scroll_id" : "DnF1ZXJ5VGhlbkZldGNoIAAAAAJFQdUKFllGc2E4Y2tEUjR5VkpKbkNtdDFMNFEAAAACJj74YxZmSWhNM2tVbFRiaU9VcVpDUWpKSGlnAAAAAiY--F4WZkloTTNrVWxUYmlPVXFaQ1FqSkhpZwAAAAJMQKhIFmw2c1hwVFk1UXppbDhZcW1za2ZzdlEAAAACRUHVCxZZRnNhOGNrRFI0eVZKSm5DbXQxTDRRAAAAAkxAqEcWbDZzWHBUWTVRemlsOFlxbXNrZnN2UQAAAAImPvhdFmZJaE0za1VsVGJpT1VxWkNRakpIaWcAAAACJ-MhBhZOMmYzWVVMbFIzNkdnN1FwVXVHaEd3AAAAAifjIQgWTjJmM1lVTGxSMzZHZzdRcFV1R2hHdwAAAAIn4yEHFk4yZjNZVUxsUjM2R2c3UXBVdUdoR3cAAAACJ5db8xZxeW5NRXpHOFR0eVNBOHlOcXBGbWdRAAAAAifjIQkWTjJmM1lVTGxSMzZHZzdRcFV1R2hHdwAAAAJFQdUMFllGc2E4Y2tEUjR5VkpKbkNtdDFMNFEAAAACJj74YhZmSWhNM2tVbFRiaU9VcVpDUWpKSGlnAAAAAieXW_YWcXluTUV6RzhUdHlTQTh5TnFwRm1nUQAAAAInl1v0FnF5bk1Fekc4VHR5U0E4eU5xcEZtZ1EAAAACJ5db9RZxeW5NRXpHOFR0eVNBOHlOcXBGbWdRAAAAAkVB1Q0WWUZzYThja0RSNHlWSkpuQ210MUw0UQAAAAImPvhfFmZJaE0za1VsVGJpT1VxWkNRakpIaWcAAAACJ-MhChZOMmYzWVVMbFIzNkdnN1FwVXVHaEd3AAAAAkVB1REWWUZzYThja0RSNHlWSkpuQ210MUw0UQAAAAImPvhgFmZJaE0za1VsVGJpT1VxWkNRakpIaWcAAAACTECoShZsNnNYcFRZNVF6aWw4WXFtc2tmc3ZRAAAAAiY--GEWZkloTTNrVWxUYmlPVXFaQ1FqSkhpZwAAAAJFQdUOFllGc2E4Y2tEUjR5VkpKbkNtdDFMNFEAAAACRUHVEBZZRnNhOGNrRFI0eVZKSm5DbXQxTDRRAAAAAiY--GQWZkloTTNrVWxUYmlPVXFaQ1FqSkhpZwAAAAJFQdUPFllGc2E4Y2tEUjR5VkpKbkNtdDFMNFEAAAACJj74ZRZmSWhNM2tVbFRiaU9VcVpDUWpKSGlnAAAAAkxAqEkWbDZzWHBUWTVRemlsOFlxbXNrZnN2UQAAAAInl1v3FnF5bk1Fekc4VHR5U0E4eU5xcEZtZ1EAAAACTECoRhZsNnNYcFRZNVF6aWw4WXFtc2tmc3ZR"
}
d286e4ae-65b4-11ed-8abf-dac502259ad0.png

3.3 實(shí)現(xiàn)示例

d2a34900-65b4-11ed-8abf-dac502259ad0.png

protected  Page searchPageByConditionWithScrollId(BoolQueryBuilder queryParam, Class targetClass, Page page) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        SearchResponse scrollResp = null;
        String scrollId = ContextParameterHolder.get("scrollId");
        if (scrollId != null) {
            scrollResp = getTransportClient().prepareSearchScroll(scrollId).setScroll(new TimeValue(60000)).execute()
                    .actionGet();
        } else {
            logger.info("基于scroll的分頁(yè)查詢(xún),scrollId為空");
            scrollResp = this.prepareSearch()
                    .setSearchType(SearchType.QUERY_AND_FETCH)
                    .setScroll(new TimeValue(60000))
                    .setQuery(queryParam)
                    .setSize(page.getPageSize()).execute().actionGet();
            ContextParameterHolder.set("scrollId", scrollResp.getScrollId());
        }
        SearchHit[] hits = scrollResp.getHits().getHits();
        List list = new ArrayList(hits.length);
        for (SearchHit hit : hits) {
            T instance = targetClass.newInstance();
            this.convertToBean(instance, hit);
            list.add(instance);
        }
        page.setTotalRow((int) scrollResp.getHits().getTotalHits());
        page.setResult(list);
        return page;
    }

3.4 小結(jié)

scroll 分頁(yè)方式的優(yōu)點(diǎn)就是減少了查詢(xún)和排序的次數(shù),避免性能損耗。缺點(diǎn)就是只能實(shí)現(xiàn)上一頁(yè)、下一頁(yè)的翻頁(yè)功能,不兼容通過(guò)頁(yè)碼查詢(xún)數(shù)據(jù)的跳頁(yè),同時(shí)由于其在搜索初始化階段會(huì)生成快照,后續(xù)數(shù)據(jù)的變化無(wú)法及時(shí)體現(xiàn)在查詢(xún)結(jié)果,因此更加適合一次性批量查詢(xún)或非實(shí)時(shí)數(shù)據(jù)的分頁(yè)查詢(xún)。 啟用游標(biāo)查詢(xún)時(shí),需要注意設(shè)定期望的過(guò)期時(shí)間(scroll = 1m),以降低維持游標(biāo)查詢(xún)窗口所需消耗的資源。注意這個(gè)過(guò)期時(shí)間每次查詢(xún)都會(huì)重置刷新為 1 分鐘,表示游標(biāo)的閑置失效時(shí)間(第二次以后的查詢(xún)必須帶 scroll = 1m 參數(shù)才能實(shí)現(xiàn))

4 Search After 分頁(yè)方式

Search After 分頁(yè)方式是 ES 5 新增的一種分頁(yè)查詢(xún)方式,其實(shí)現(xiàn)的思路同 Scroll 分頁(yè)方式基本一致,通過(guò)記錄上一次分頁(yè)的位置標(biāo)識(shí),來(lái)進(jìn)行下一次分頁(yè)數(shù)據(jù)的查詢(xún)。相比于 Scroll 分頁(yè)方式,它的優(yōu)點(diǎn)是可以實(shí)時(shí)體現(xiàn)數(shù)據(jù)的變化,解決了查詢(xún)快照導(dǎo)致的查詢(xún)結(jié)果延遲問(wèn)題。

4.1 執(zhí)行過(guò)程

Search After 方式也不支持跳頁(yè)功能,每次查詢(xún)一頁(yè)數(shù)據(jù)。第一次每個(gè) shard 返回一頁(yè)數(shù)據(jù)(size 條),coordinating node 一共獲取到 shard 數(shù) * size 條數(shù)據(jù) , 接下來(lái) coordinating node 在內(nèi)存中進(jìn)行排序,取出前 size 條數(shù)據(jù)作為第一頁(yè)搜索結(jié)果返回。當(dāng)拉取第二頁(yè)時(shí),不同于 Scroll 分頁(yè)方式,Search After 方式會(huì)找到第一頁(yè)數(shù)據(jù)被拉取的最大值,作為第二頁(yè)數(shù)據(jù)拉取的查詢(xún)條件。 這樣每個(gè) shard 還是返回一頁(yè)數(shù)據(jù)(size 條),coordinating node 獲取到 shard 數(shù) * size 條數(shù)據(jù)進(jìn)行內(nèi)存排序,取得前 size 條數(shù)據(jù)作為全局的第二頁(yè)搜索結(jié)果。
后續(xù)分頁(yè)查詢(xún)以此類(lèi)推…

4.2 ES 示例

第一次查詢(xún)只傳入排序字段和每頁(yè)大小 size

GET /wms_order_sku2021_10/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "shipmentOrderCreateTime": {
              "gte": "2021-10-12 0000",
              "lt": "2021-10-15 0000"
            }
          }
        }
      ]
    }
  },
  "size": 20,
  "sort": [
    {
      "_id": {
        "order": "desc"
      }
    },{
      "shipmentOrderCreateTime":{
        "order": "desc"
      }
    }
  ]
}
d2dc8706-65b4-11ed-8abf-dac502259ad0.png

接下來(lái)每次查詢(xún)時(shí)都帶上本次查詢(xún)的最后一條數(shù)據(jù)的 _id 和 shipmentOrderCreateTime 字段,循環(huán)往復(fù)就能夠?qū)崿F(xiàn)不斷下一頁(yè)的功能
GET /wms_order_sku2021_10/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "shipmentOrderCreateTime": {
              "gte": "2021-10-12 0000",
              "lt": "2021-10-15 0000"
            }
          }
        }
      ]
    }
  },
  "size": 20,
  "sort": [
    {
      "_id": {
        "order": "desc"
      }
    },{
      "shipmentOrderCreateTime":{
        "order": "desc"
      }
    }
  ],
  "search_after": ["SO-460_152-1447931043809128448-100017918838",1634077436000]
}
d2f15096-65b4-11ed-8abf-dac502259ad0.png

4.3 實(shí)現(xiàn)示例

d31396c4-65b4-11ed-8abf-dac502259ad0.png
d33703ca-65b4-11ed-8abf-dac502259ad0.png

public  ScrollDto queryScrollDtoByParamWithSearchAfter(
            BoolQueryBuilder queryParam, Class targetClass, int pageSize, String afterId,
            List fieldSortBuilders) {
        SearchResponse scrollResp;
        long now = System.currentTimeMillis();
        SearchRequestBuilder builder = this.prepareSearch();
        if (CollectionUtils.isNotEmpty(fieldSortBuilders)) {
            fieldSortBuilders.forEach(builder::addSort);
        }
        builder.addSort("_id", SortOrder.DESC);
        if (StringUtils.isBlank(afterId)) {
            log.info("queryScrollDtoByParamWithSearchAfter基于afterId的分頁(yè)查詢(xún),afterId為空");
            SearchRequestBuilder searchRequestBuilder = builder.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                    .setQuery(queryParam).setSize(pageSize);
            scrollResp = searchRequestBuilder.execute()
                    .actionGet();
            log.info("queryScrollDtoByParamWithSearchAfter基于afterId的分頁(yè)查詢(xún),afterId 為空,searchRequestBuilder:{}", searchRequestBuilder);
        } else {
            log.info("queryScrollDtoByParamWithSearchAfter基于afterId的分頁(yè)查詢(xún),afterId=" + afterId);
            Object[] afterIds = JSON.parseObject(afterId, Object[].class);
            SearchRequestBuilder searchRequestBuilder = builder.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                    .setQuery(queryParam).searchAfter(afterIds).setSize(pageSize);
            log.info("queryScrollDtoByParamWithSearchAfter基于afterId的分頁(yè)查詢(xún),searchRequestBuilder:{}", searchRequestBuilder);
            scrollResp = searchRequestBuilder.execute()
                    .actionGet();
        }
        SearchHit[] hits = scrollResp.getHits().getHits();
        log.info("queryScrollDtoByParamWithSearchAfter基于afterId的分頁(yè)查詢(xún),totalRow={}, size={}, use time:{}", scrollResp.getHits().getTotalHits(), hits.length, System.currentTimeMillis() - now);
        now = System.currentTimeMillis();

        List list = new ArrayList<>();
        if (ArrayUtils.getLength(hits) > 0) {
            list = Arrays.stream(hits)
                    .filter(Objects::nonNull)
                    .map(SearchHit::getSourceAsMap)
                    .filter(Objects::nonNull)
                    .map(JSON::toJSONString)
                    .map(e -> JSON.parseObject(e, targetClass))
                    .collect(Collectors.toList());
            afterId = JSON.toJSONString(hits[hits.length - 1].getSortValues());
        }
        log.info("es數(shù)據(jù)轉(zhuǎn)換bean,totalRow={}, size={}, use time:{}", scrollResp.getHits().getTotalHits(), hits.length, System.currentTimeMillis() - now);
        return ScrollDto.builder().scrollId(afterId).result(list).totalRow((int) scrollResp.getHits().getTotalHits()).build();
    }

4.4 小結(jié)

Search After 分頁(yè)方式采用記錄作為游標(biāo),因此 Search After 要求 doc 中至少有一條全局唯一變量(示例中使用_id 和時(shí)間戳,實(shí)際上_id 已經(jīng)是全局唯一)。Search After 方式是無(wú)狀態(tài)的分頁(yè)查詢(xún),因此數(shù)據(jù)的變更能夠及時(shí)的反映在查詢(xún)結(jié)果中,避免了 Scroll 分頁(yè)方式無(wú)法獲取最新數(shù)據(jù)變更的缺點(diǎn)。同時(shí) Search After 不用維護(hù) scroll_id 和快照,因此也節(jié)約大量資源。

5 總結(jié)思考

5.1 ES 三種分頁(yè)方式對(duì)比總結(jié)

d35670c0-65b4-11ed-8abf-dac502259ad0.png

如果數(shù)據(jù)量小(from+size 在 10000 條內(nèi)),或者只關(guān)注結(jié)果集的 TopN 數(shù)據(jù),可以使用 from/size 分頁(yè),簡(jiǎn)單粗暴

數(shù)據(jù)量大,深度翻頁(yè),后臺(tái)批處理任務(wù)(數(shù)據(jù)遷移)之類(lèi)的任務(wù),使用 scroll 方式

數(shù)據(jù)量大,深度翻頁(yè),用戶(hù)實(shí)時(shí)、高并發(fā)查詢(xún)需求,使用 search after 方式

5.2 個(gè)人思考

在一般業(yè)務(wù)查詢(xún)頁(yè)面中,大多情況都是 10-20 條數(shù)據(jù)為一頁(yè),10000 條數(shù)據(jù)也就是 500-1000 頁(yè)。正常情況下,對(duì)于用戶(hù)來(lái)說(shuō),有極少需求翻到比較靠后的頁(yè)碼來(lái)查看數(shù)據(jù),更多的是通過(guò)查詢(xún)條件框定一部分?jǐn)?shù)據(jù)查看其詳情。因此在業(yè)務(wù)需求敲定初期,可以同業(yè)務(wù)人員商定 1w 條數(shù)據(jù)的限定,超過(guò) 1w 條的情況可以借助導(dǎo)出數(shù)據(jù)到 Excel 表,在 Excel 表中做具體的操作。

如果給導(dǎo)出中心返回大量數(shù)據(jù)的場(chǎng)景可以使用 Scroll 或 Search After 分頁(yè)方式,相比之下最好使用 Search After 方式,既可以保證數(shù)據(jù)的實(shí)時(shí)性,也具有很高的搜索性能。

總之,在使用 ES 時(shí)一定要避免深度分頁(yè)問(wèn)題,要在跳頁(yè)功能實(shí)現(xiàn)和 ES 性能、資源之間做一個(gè)取舍。必要時(shí)也可以調(diào)大 max_result_window 參數(shù),原則上不建議這么做,因?yàn)?1w 條以?xún)?nèi) ES 基本能保持很不錯(cuò)的性能,超過(guò)這個(gè)范圍深度分頁(yè)相當(dāng)耗時(shí)、耗資源,因此謹(jǐn)慎選擇此方式。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Query
    +關(guān)注

    關(guān)注

    0

    文章

    11

    瀏覽量

    9324
  • Elasticsearch
    +關(guān)注

    關(guān)注

    0

    文章

    26

    瀏覽量

    2815

原文標(biāo)題:ElasticSearch深度分頁(yè)詳解

文章出處:【微信號(hào):OSC開(kāi)源社區(qū),微信公眾號(hào):OSC開(kāi)源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Windows安裝ElasticSearch

    Windows安裝ElasticSearch
    的頭像 發(fā)表于 02-15 17:09 ?934次閱讀
    Windows安裝<b class='flag-5'>ElasticSearch</b>

    linux安裝配置ElasticSearch之源碼安裝

    ElasticSearch是基于Lucene這個(gè)非常成熟的索引方案,另加上一些分布式的實(shí)現(xiàn):集群,sharding,replication等。以下是對(duì)其采用源碼安裝的方法1.下載
    發(fā)表于 01-11 17:27

    ElasticSearch的詞條查詢(xún)

    ElasticSearch查詢(xún) 第三篇:詞條查詢(xún)
    發(fā)表于 04-30 17:03

    ElasticSearch的初步環(huán)境

    ElasticSearch最實(shí)用入門(mén)指南——初步環(huán)境
    發(fā)表于 03-31 11:32

    elasticsearch介紹PPT

    elasticsearch介紹PPT
    發(fā)表于 12-13 21:05 ?20次下載

    Jquery簡(jiǎn)單分頁(yè)實(shí)現(xiàn)

    這篇文章主要介紹了Jquery簡(jiǎn)單分頁(yè)實(shí)現(xiàn)方法,實(shí)例分析了jquery分頁(yè)的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下。
    發(fā)表于 11-28 11:55 ?1066次閱讀

    一文詳解linux的分頁(yè)模型

    也就是我們實(shí)際中編碼時(shí)遇到的內(nèi)存地址并不是對(duì)應(yīng)于實(shí)際內(nèi)存上的地址,我們編碼中使用的地址是一個(gè)邏輯地址,會(huì)通過(guò)分段和分頁(yè)這兩個(gè)機(jī)制把它轉(zhuǎn)為物理地址。而由于linux使用的分段機(jī)制有限,可以認(rèn)為
    的頭像 發(fā)表于 05-18 08:59 ?2036次閱讀
    一文<b class='flag-5'>詳解</b>linux的<b class='flag-5'>分頁(yè)</b>模型

    Elasticsearch6.1教程

    Elasticsearch6.1教程
    發(fā)表于 07-04 14:40 ?0次下載

    ElasticSearch是什么?應(yīng)用場(chǎng)景是什么?

    ElasticSearch是什么 ElasticSearch的功能 ElasticSearch的應(yīng)用場(chǎng)景 ElasticSearch的特點(diǎn)
    的頭像 發(fā)表于 10-09 18:38 ?2376次閱讀

    ElasticSearch 深度分頁(yè)實(shí)踐性分析探討

    該條 DSL 語(yǔ)句表示從搜索結(jié)果中第 10 條數(shù)據(jù)位置開(kāi)始,取之后的 20 條數(shù)據(jù)作為結(jié)果返回。這種分頁(yè)方式在 ES 集群內(nèi)部是如何執(zhí)行的呢?
    發(fā)表于 11-21 11:21 ?368次閱讀

    圖文詳解Linux分頁(yè)機(jī)制

    分頁(yè)機(jī)制是 80x86 內(nèi)存管理機(jī)制的第二種機(jī)制,分段機(jī)制用于把虛擬地址轉(zhuǎn)換為線(xiàn)性地址,而分頁(yè)機(jī)制用于把線(xiàn)性地址轉(zhuǎn)換為物理地址。
    發(fā)表于 05-30 09:10 ?409次閱讀
    圖文<b class='flag-5'>詳解</b>Linux<b class='flag-5'>分頁(yè)</b>機(jī)制

    聊聊分頁(yè)列表緩存設(shè)計(jì)

    這是最簡(jiǎn)單易懂的方案,我們按照不同的分頁(yè)條件查詢(xún)出結(jié)果后,直接緩存分頁(yè)結(jié)果 。
    的頭像 發(fā)表于 06-06 18:25 ?674次閱讀
    聊聊<b class='flag-5'>分頁(yè)</b>列表緩存設(shè)計(jì)

    Elasticsearch保姆級(jí)入門(mén)

    我們需要?jiǎng)?chuàng)建一個(gè)供 Elasticsearch 和 Kibana 使用的 network。這個(gè) network 將被用于 Elasticsearch 和 Kibana 之間的通信。
    的頭像 發(fā)表于 09-01 15:24 ?774次閱讀
    <b class='flag-5'>Elasticsearch</b>保姆級(jí)入門(mén)

    SpringBoot 連接ElasticSearch的使用方式

    在上篇 ElasticSearch 文章中,我們?cè)敿?xì)的介紹了 ElasticSearch 的各種 api 使用。 實(shí)際的項(xiàng)目開(kāi)發(fā)過(guò)程中,我們通?;谀承┲髁骺蚣芷脚_(tái)進(jìn)行技術(shù)開(kāi)發(fā),比如
    的頭像 發(fā)表于 10-09 10:35 ?791次閱讀

    mybatis邏輯分頁(yè)和物理分頁(yè)的區(qū)別

    MyBatis是一個(gè)開(kāi)源的Java持久層框架,它與其他ORM(對(duì)象關(guān)系映射)框架相比,具有更加靈活和高性能的特點(diǎn)。MyBatis提供了兩種分頁(yè)方式,即邏輯分頁(yè)和物理分頁(yè)。在本文中,我們將詳細(xì)介紹
    的頭像 發(fā)表于 12-03 14:54 ?781次閱讀