es查詢-統計總數以及深度分頁


es查詢-統計總數以及深度分頁

     一、查詢總數

      1.  ES 查詢 hits 統計總數不准?

      當我們使用 ES 的時候,有時會比較關心匹配到的文檔總數是多少,所以在查詢得到結果后會使用 hits.total.value 這個值作為匹配的總數,如下

     

 

                                               圖一

     說明:這是因為,es官方默認限制索引查詢最多只能查詢10000條數據。

     2.  track_total_hits

    平常數據量不大的情況下,這個數值沒問題。但是當超出 10000 個數據量的時候,這個 value 將不會增長了,固定為 10000。這個時候很顯然數量統計就不准了。

    ES 為我們准備了這樣一個參數來開啟精確匹配 track_total_hits

    這個時候返回值將是精確的:

      

                                            圖二

       3.  track_total_hits的使用場景

       如果你的業務需求在超過 10000 這個閾值之后就不需要精准的計算的話,就不需要設置該值,畢竟匹配大量的文檔是一個成本較高的操作,同樣的如果你並不需要統計數量,那么將該值設置為 false "track_total_hits": false 也是一種優化檢索效率的操作。

       4.  hits.total.relation

      通過圖一和圖二,可以發現設置 track_total_hits 為 true 的時候返回 relation 的值是不一樣的,gte 表示 hits.total.value 是查詢匹配總命中數的上限。 eq 則表示hits.total.value 是准確計數。

      因此,我們可以通過設置 track_total_hits 為整數,來自定義上限如:

      

 

 

     二、查詢數據

     1.  查詢超過10000條數據,開始報錯

    上面解決了需要統計超過10000條數據總數的問題,但是在查詢具體數據的時候依然存在類似的問題。es官方默認限制索引查詢最多只能查詢10000條數據,查詢第10001條數據開始就會報錯:

     result window is too large, from + size must be less than or equal to

     解決方案:

     1)第一種辦法:在kibana中執行,解除索引最大查詢數的限制

put _all/_settings
{
  "index.max_result_window":200000
}

   _all表示所有索引,針對單個索引的話修改成索引名稱即可。

    2)第二種辦法:在創建索引的時候加上

1 "settings":{
2   "index":{
4      "max_result_window": 500000
5    }
7 }

    三、深度分頁

    做分頁查詢的時候,我們需要考慮兩個問題

  •    性能要求,在硬件和查詢效率中作出權衡
  •    對大部分請求來說,都不會超過10000條,即不需要深度分頁,當需要請求超過10000條時,就需要考慮我們的查詢語句是否恰當

    下面有幾種分頁方式:

    1、方式一: from+size淺分頁

   "淺"分頁可以理解為簡單意義上的分頁。它的原理很簡單,就是查詢前20條數據,然后截斷前10條,只返回10-20的數據。這樣其實白白浪費了前10條的查詢

 1 GET test_alias/_search
 2 {
 3 "query": {
 4     "match_all": {
 5     }
 6   },
 7   "sort": [
 8     {
 9       "add_time": {
10         "order": "desc"
11       }
12     }
13   ],
14   "from":0,
15   "size": 2
16 }

 

    其中,from定義了目標數據的偏移值,size定義當前返回的數目。默認from為0,size為10,即所有的查詢默認僅僅返回前10條數據。

    from/size的原理

    因為es是基於分片的,假設有5個分片,from=100,size=10。則會根據排序規則從5個分片中各取回100條數據數據,然后匯總成500條數據后,選擇最后面的10條數據

    做過測試,越往后的分頁,執行的效率越低。總體上會隨着from的增加,消耗時間也會增加。而且數據量越大,就越明顯。

    說明:from+size查詢在10000-50000條數據(1000到5000頁)以內的時候還是可以的,但是如果數據過多的話,就會出現深分頁問題。

    2、方式二: scroll深分頁

   為了解決上面的問題,elasticsearch提出了一個scroll滾動的方式。

   scroll 類似於sql中的cursor,使用scroll,每次只能獲取一頁的內容,然后會返回一個scroll_id。根據返回的這個scroll_id可以不斷地獲取下一頁的內容,所以scroll並不適用於有跳頁的情景。

    

 

    2.1 參數說明:

  •     scroll=5m表示設置scroll_id保留5分鍾可用
  •     size決定后面每次調用_search搜索返回的數量

    2.2  scroll刪除

   根據官方文檔的說法,scroll的搜索上下文會在scroll的保留時間截止后自動清除,但是我們知道scroll是非常消耗資源的,所以一個建議就是當不需要了scroll數據的時候,盡可能快的把scroll_id顯式刪除掉。

   1)清除指定的scroll_id:

   DELETE _search/scroll/DnF1ZX...

   2)清除所有的scroll:
    DELETE _search/scroll/_all

    然后我們可以通過數據返回的_scroll_id讀取下一頁內容,每次請求將會讀取下10條數據,直到數據讀取完畢或者scroll_id保留時間截止。

    說明:scroll 的方式,官方的建議用於實時的請求(一般用於數據導出),因為每一個 scroll_id 不僅會占用大量的資源,而且會生成歷史快照,對於數據的變更不會反映到快照上。

   3、方式三:search_after 深分頁

   search_after是ES5.0 及之后版本提供的新特性,search_after有點類似scroll,但是和scroll又不一樣,它提供一個活動的游標,通過上一次查詢最后一條數據來進行下一次查詢。

   比如第一次查詢如下:

   

 

    注意到返回結果中有一個sort字段,所以下一次查詢的時候,只需要將本次查詢最后一條數據中的排序字段加入查詢即可:

    

    實現原理:

    search_after 分頁的方式是根據上一頁的最后一條數據來確定下一頁的位置,同時在分頁請求的過程中,如果有索引數據的增刪改查,這些變更也會實時的反映到游標上。

    說明:

  •     為了找到每一頁最后一條數據,每個文檔必須有一個全局唯一值,官方推薦使用 _uid 作為全局唯一值,其實使用業務層的 id 也可以。
  •     說明:使用search_after查詢需要將from設置為0或-1,當然也可以不寫。

    需要注意的是:

    1)sort字段的選擇

   如果search_after中的關鍵字為***,那么***123的文檔也會被搜索到,所以在選擇search_after的排序字段時需要謹慎,可以使用比如文檔的id或者時間戳等。另外,search_after並不是隨機的查詢某一頁數據,而是並行的滾屏查詢;search_after的查詢順序會在更新和刪除時發生變化,也就是說支持實時的數據查詢。

    2)無法跳頁請求

    因為每一頁的數據依賴於上一頁最后一條數據,所以無法跳頁請求。

    4、方式四: 由前端傳入上次查詢的最小ID

    由於業務需要,我這邊實現的分頁列表由前端配合一起完成,這樣也解決了跳頁的問題,供參考:

 1 <?php
 2 
 3 class Test
 4 {
 5 
 6     public function queryBaiduJobList(array $fileds, int $minId, int $limit)
 7     {
 8         // 為了解決es查詢默認1w條的上限,由前端傳入上一次查詢返回列表中最小id:minId
 9         $query = JobParams::build()
10             ->term('is_check', 1)
11             ->select($fileds)
12             ->trackHits(true)
13             ->sort('id')
14             ->limit($limit);
15 
16         if ($minId) {
17             $query = $query->range('id', ['<', $minId]);
18         }
19 
20         return $this->all($query);
21     }
22 }

 

 

    參考鏈接:https://blog.csdn.net/andybegin/article/details/83864171


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM