亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

elasticsearch集群查詢超10000的解決方案

 更新時間:2024年08月07日 10:55:23   作者:蒼煜  
ES為了避免用戶的過大分頁請求造成ES服務所在機器內存溢出,默認對深度分頁的條數進行了限制,默認的最大條數是10000條,這篇文章主要給大家介紹了關于elasticsearch集群查詢超10000的解決方案,需要的朋友可以參考下

前言

默認情況下,Elasticsearch集群中每個分片的搜索結果數量限制為10000。這是為了避免潛在的性能問題。

但是我們 在實際工作過程中時常會遇到 需要深度分頁,以及查詢批量數據更新的情況

問題:當請求form + size >10000 時,請求直接報錯

1:修改max_result_window 參數(不推薦)

在此方案中,我們建議僅限于測試用,生產禁用,畢竟當數據量大的時候,過大的數據量可能導致es的內存溢出,直接崩掉,一年績效白干。

PUT wkl_test/_settings
{
   "index":{
        "max_result_window":2147483647
    }
}

查看索引的 settings

重新查數據:

2:使用游標 scroll API

使用scroll API:scroll API可以幫助我們在不加載所有數據的情況下獲取所有結果。它會在后臺執(zhí)行查詢以獲取滾動ID,并將其用于進行后續(xù)查詢。這樣就可以一次性獲取所有結果,而不必擔心限制

ES語句查詢

在游標方案中,我們只需要在第一次拿到游標id,之后通過游標就能唯一確定查詢,在這個查詢中通過我們指定的 size 移動游標,具體操作看看下面實操。

  • 游標查詢,設置游標有效時間,有效時間內,游標都可以使用,過期就不行了
GET wkl_test/_search?scroll=5m
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "seq": {
        "order": "asc"
      }
    }
  ],
  "size": 200
}
  • 上面操作中通過游標的結果返回

  • 之后將_scroll_id 復制到窗口,就可以不端通過這個_scroll_id 進行之前設置的頁數不斷翻頁
    以此類推,后面每次滾屏都把前一個的scroll_id復制過來。注意到,后續(xù)請求時沒有了index信息,size信息等,這些都在初始請求中,只需要使用scroll_id和scroll兩個參數即可。

    注意,此時游標移動了,所以我們可以通過游標的方式不斷后移,直到移動到我們想要的 from+size 范圍內。再次點擊

java實現

@Test
    public void testScroll(){
        RestHighLevelClient restHighLevelClient ;
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.mustNot(QueryBuilders.existsQuery("seq"));

        try {
            //滾動查詢的Scroll,設置請求滾動時間窗口時間
            Scroll scroll = new Scroll(TimeValue.timeValueMillis(180000));

            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            //加入query語句
            sourceBuilder.query(boolQueryBuilder);
            //每次滾動的長度
            sourceBuilder.size(SIZE);
            //加入排序字段
            sourceBuilder.sort("id", SortOrder.DESC);
            //構建searchRequest
            //加入scroll和構造器
            SearchRequest searchRequest = new SearchRequest()
                    .indices("wkl_test")
                    .source(sourceBuilder)
                    .scroll(scroll);
            //存儲scroll的list
            List<String> scrollIdList = new ArrayList<>();
            //執(zhí)行首次檢索
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //首次檢索返回scrollId,用于下一次的滾動查詢
            String scrollId = searchResponse.getScrollId();
            //拿到hits結果
            SearchHit[] hits = searchResponse.getHits().getHits();
            long value = searchResponse.getHits().getTotalHits().value;
            //保存返回結果List大小
            Long resultSize = 0L;
            scrollIdList.add(scrollId);
            try {
                //滾動查詢將SearchHit封裝到result中
                while (ArrayUtils.isNotEmpty(hits) && hits.length > 0) {
                    BulkRequest bulkRequest = new BulkRequest();
                    JSONArray esArray = new JSONArray();
                    for (SearchHit hit : hits) {
                        String sourceAsString = hit.getSourceAsString();
                        String index = hit.getIndex();
                        JSONObject jsonObject = JSONObject.parseObject(sourceAsString);
                        String seq = jsonObject.getString("seq");
                        if(StringUtils.isBlank(seq) ){
                            esArray.add(jsonObject);
                            String uuid = jsonObject.getString("id");
                            jsonObject.put("is_del",1);
                            bulkRequest.add(new UpdateRequest(index, uuid).doc(jsonObject));
                        }
                    }
                    resultSize = resultSize+hits.length;

                    //發(fā)送請求
                    //實時更新
                    bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
                    BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
                    System.out.println(bulk.getTook()+"-------"+bulk.getItems().length);

                    //說明滾動完了,返回結果即可
                    if (resultSize > 20000) {
                        break;
                    }
                    //繼續(xù)滾動,根據上一個游標,得到這次開始查詢位置
                    SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
                    searchScrollRequest.scroll(scroll);
                    //得到結果
                    SearchResponse searchScrollResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
                    //定位游標
                    scrollId = searchScrollResponse.getScrollId();
                    hits = searchScrollResponse.getHits().getHits();
                    scrollIdList.add(scrollId);
                }
                System.out.println("----徹底結束了-----");
            } finally {
                //清理scroll,釋放資源
                ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
                clearScrollRequest.setScrollIds(scrollIdList);
                restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

scroll API 的優(yōu)缺點和總結

優(yōu)缺點:

  • scroll查詢的相應數據是非實時的,如果遍歷過程中插入新的數據,是查詢不到的。并且保留上下文需要足夠的堆內存空間。
  • 相比于 from/size 和 search_after 返回一頁數據,Scroll API 可用于從單個搜索請求中檢索大量結果。但是 scroll 滾動遍歷查詢是非實時的,數據量大的時候,響應時間可能會比較長

適用場景

  • 全量或數據量很大時遍歷結果數據,而非分頁查詢。
  • scroll方案基于快照,不能用在高實時性的場景下,建議用在類似數據導出場景下使用

3: search_after + PIT 深度查詢

  • Search_after是 ES 5 新引入的一種分頁查詢機制,其原理幾乎就是和scroll一樣,因此代碼也幾乎是一樣的。
  • 官方文檔說明不再建議使用scroll滾動分頁和from size分頁,建議使用search_after
  • search_after 分頁的方式和 scroll 搜索有一些顯著的區(qū)別,首先它是根據上一頁的最后一條數據來確定下一頁的位置,同時在分頁請求的過程中,如果有索引數據的增刪改查,這些變更也會實時的反映到游標上。

不帶PIT

ES語句實現

檢索第一頁的查詢如下所示:

GET wkl_test/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "seq": {
        "order": "asc"
      }
    }
  ],
  "size": 200
}

上述請求的結果包括每個文檔的 sort 值數組。

這些 sort 值可以與 search_after 參數一起使用,以開始返回在這個結果列表之后的任何文檔。例如,我們可以使用上一個文檔的 sort 值并將其傳遞給 search_after 以檢索下一頁結果:

Java 實現

@Test
    public void testSearchAfter() throws IOException {
        RestHighLevelClient restHighLevelClient = es7UtilApi.getRestHighLevelClient();

        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(matchAllQueryBuilder);
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(200);
        searchSourceBuilder.sort("seq", SortOrder.ASC);
        searchSourceBuilder.trackTotalHits(true);

        SearchRequest searchRequest = new SearchRequest()
                .indices("wkl_test")
                .source(searchSourceBuilder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        long value = hits.getTotalHits().value;
        System.out.println("查詢到記錄數=" + value);

        List<JSONObject> list = new ArrayList<>();
        SearchHit[] searchHists = hits.getHits();
        Object[] sortValues = searchHists[searchHists.length - 1].getSortValues();
        if (searchHists.length > 0) {
            for (SearchHit hit : searchHists) {
                String sourceAsString = hit.getSourceAsString();
                JSONObject jsonObject = JSON.parseObject(sourceAsString);
                jsonObject.put("_id", hit.getId());
                list.add(jsonObject);
            }
        }

        //往后的每次請求都攜帶上一次的sort_id進行訪問。
        while (ArrayUtils.isNotEmpty(searchHists) && searchHists.length > 0){
            searchSourceBuilder.searchAfter(sortValues);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponseAfter = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            hits = searchResponseAfter.getHits();
            searchHists = hits.getHits();
            sortValues = searchHists[searchHists.length - 1].getSortValues();
            if (searchHists.length > 0) {
                for (SearchHit hit : searchHists) {
                    String sourceAsString = hit.getSourceAsString();
                    JSONObject jsonObject = JSON.parseObject(sourceAsString);
                    jsonObject.put("_id", hit.getId());
                    list.add(jsonObject);
                }
            }
            if(list.size()>20000){
                break;
            }
            System.out.println("-----徹底結束了-------");
        }

    }

問題

「優(yōu)點:」

  • 無狀態(tài)查詢,可以防止在查詢過程中,數據的變更無法及時反映到查詢中。

  • 不需要維護scroll_id,不需要維護快照,因此可以避免消耗大量的資源。

「缺點:」

  • 由于無狀態(tài)查詢,因此在查詢期間的變更可能會導致跨頁面的不一值。

  • 排序順序可能會在執(zhí)行期間發(fā)生變化,具體取決于索引的更新和刪除。

  • 至少需要制定一個唯一的不重復字段來排序。

  • 它不適用于大幅度跳頁查詢,或者全量導出,對第N頁的跳轉查詢相當于對es不斷重復的執(zhí)行N次search after,而全量導出則是在短時間內執(zhí)行大量的重復查詢。

帶PIT

關于PIT

  • 在7.*版本中,ES官方不再推薦使用Scroll方法來進行深分頁,而是推薦使用帶PIT的search_after來進行查詢;

  • 從7.*版本開始,您可以使用SEARCH_AFTER參數通過上一頁中的一組排序值檢索下一頁命中。

  • 使用SEARCH_AFTER需要多個具有相同查詢和排序值的搜索請求。

  • 如果這些請求之間發(fā)生刷新,則結果的順序可能會更改,從而導致頁面之間的結果不一致。
    為防止出現這種情況,您可以創(chuàng)建一個時間點(PIT)來在搜索過程中保留當前索引狀態(tài)。

ES語句實現

1:生成pit

#keep_alive必須要加上,它表示這個pit能存在多久,這里設置的是1分鐘
POST wkl_test/_pit?keep_alive=1m

在這里插入圖片描述

2:在搜索請求中指定PIT:

在每個搜索請求中添加 keep_alive 參數來延長 PIT 的保留期,相當于是重置了一下時間

GET _search
{
  "query": {
    "match_all": {}
  },
  "pit":{
    "id":"t_yxAwEId2tsX3Rlc3QWU0hzbEJkYWNTVEd0ZGRoN0xsQVVNdwAWUGQtaXJpT0xTa2VUN0RGLXZfTlBvZwAAAAAACHG1fxY1UWNKX1RHOFMybXBaV20zbWx3enp3ARZTSHNsQmRhY1NUR3RkZGg3TGxBVU13AAA=",
    "keep_alive":"5m"
  },
  "sort": [
    {
      "seq": {
        "order": "asc"
      }
    }
  ],
  "size": 200
}

3:刪除PIT

DELETE _pit
{
 "id":"t_yxAwEId2tsX3Rlc3QWU0hzbEJkYWNTVEd0ZGRoN0xsQVVNdwAWUGQtaXJpT0xTa2VUN0RGLXZfTlBvZwAAAAAACHG1fxY1UWNKX1RHOFMybXBaV20zbWx3enp3ARZTSHNsQmRhY1NUR3RkZGg3TGxBVU13AAA="
}

總結

  • 如果數據量?。╢rom+size在10000條內),或者只關注結果集的TopN數據,可以使用from/size 分頁,簡單粗暴

  • 數據量大,深度翻頁,后臺批處理任務(數據遷移)之類的任務,使用 scroll 方式

  • 數據量大,深度翻頁,用戶實時、高并發(fā)查詢需求,使用 search after 方式

到此這篇關于elasticsearch集群查詢超10000解決方案的文章就介紹到這了,更多相關elasticsearch查詢超10000內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java Swing組件BoxLayout布局用法示例

    Java Swing組件BoxLayout布局用法示例

    這篇文章主要介紹了Java Swing組件BoxLayout布局用法,結合實例形式分析了Swing使用BoxLayout容器進行布局的相關方法與操作技巧,需要的朋友可以參考下
    2017-11-11
  • SpringBoot?調用外部接口的三種實現方法

    SpringBoot?調用外部接口的三種實現方法

    Spring Boot調用外部接口的方式有多種,常見的有以下三種方式:RestTemplate、Feign 和 WebClient,本文就詳細介紹一下,感興趣的可以了解一下
    2023-08-08
  • Java多線程模擬銀行系統(tǒng)存錢問題詳解

    Java多線程模擬銀行系統(tǒng)存錢問題詳解

    本文將利用Java多線程模擬一個簡單的銀行系統(tǒng),使用兩個不同的線程向同一個賬戶存錢。文中的示例代碼講解詳細,感興趣的可以了解一下
    2022-09-09
  • java中的Io(input與output)操作總結(三)

    java中的Io(input與output)操作總結(三)

    這一節(jié)我們來講Scanner類和PrintWriter類的用法,感興趣的朋友可以了解下
    2013-01-01
  • SpringBoot如何實現緩存預熱

    SpringBoot如何實現緩存預熱

    緩存預熱是指在 Spring Boot 項目啟動時,預先將數據加載到緩存系統(tǒng)(如 Redis)中的一種機制,本文主要介紹了SpringBoot如何實現緩存預熱,感興趣的可以了解下
    2024-12-12
  • Java線上CPU內存沖高問題排查解決步驟

    Java線上CPU內存沖高問題排查解決步驟

    這篇文章主要介紹了Java線上CPU內存沖高問題排查解決步驟的相關資料,Java程序在實際生產過程中經常遇到CPU或內存使用率高的問題,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • Java中反射動態(tài)代理接口的詳解及實例

    Java中反射動態(tài)代理接口的詳解及實例

    這篇文章主要介紹了Java中反射動態(tài)代理接口的詳解及實例的相關資料,需要的朋友可以參考下
    2017-04-04
  • SpringBoot整合EasyExcel進行大數據處理的方法詳解

    SpringBoot整合EasyExcel進行大數據處理的方法詳解

    EasyExcel是一個基于Java的簡單、省內存的讀寫Excel的開源項目。在盡可能節(jié)約內存的情況下支持讀寫百M的Excel。本文將在SpringBoot中整合EasyExcel進行大數據處理,感興趣的可以了解一下
    2022-05-05
  • Java實現定時器的4種方法超全總結

    Java實現定時器的4種方法超全總結

    對于一些特殊的代碼是需要定時執(zhí)行的,下面來看看定時器該如何編寫吧,下面這篇文章主要給大家介紹了關于Java實現定時器的4種方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • Java中不用第三個變量來互換兩個變量的值

    Java中不用第三個變量來互換兩個變量的值

    在程序運行期間,隨時可能產生一些臨時數據,應用程序會將這些數據保存在一些內存單元中,每個內存單元都用一個標識符來標識。這些內存單元被稱為變量,定義的標識符就是變量名,內存單元中存儲的數據就是變量的值
    2021-10-10

最新評論