【Elasticsearch學習】文件搜尋全過程

xiaohuoshan發表於2020-05-10

  在ES執行分散式搜尋時,分散式搜尋操作需要分散到所有相關分片,若一個索引有3個主分片,每個主分片有一個副本分片,那麼搜尋請求會在這6個分片中隨機選擇3個分片,這3個分片有可能是主分片也可能是副本分片,然後收集所有分片的查詢結果。所以ES的搜尋過程分為兩個階段,Query階段和Fetch階段;ES有兩種搜尋型別:query_then_fetch,dfs_query_then_fetch。

  1.Query階段

  1)轉發請求。在Query階段客戶端向ES節點傳送,搜尋請求,Coordinate節點接受客戶端搜尋請求,Coordinate節點負責解析搜尋請求,並在索引的所有主副本分片中隨機選擇分片,並且傳送給分片所在的資料節點。

  2)執行查詢。接收到查詢請求的資料節點執行查詢操作,並對查詢結果進行排序,每個節點都會根據請求中引數返回from+size個排序後的文件Id和排序值給Coordinate節點。

  2.Fetch階段

  1)重排序。Coordinate節點收到資料節點返回的資料後,會按照返回的排序值對從所有分片取回的值重新進行排序,最終只選取客戶端需要的from+size個文件的Id。

  2)獲取文件資料。Coordinate節點根據選取的文件的Id,到相應的分片獲取詳細的文件資料,最終將查詢到的結果返回給客戶端。

查詢結果解讀:

{
    "took":3, 查詢所用的毫秒數
    "timed_out":false, 是否有分片超時,即是否只返回了部分結果
    "_shards":{
        "total":1, 一共查詢了多少分片
        "successful":1, 多少分片成功返回
        "skipped":0,跳過了多少分片
        "failed":0  多少分片查詢失敗
    },
    "hits":{  
        "total":{ 
            "value":1, 該搜尋請求中返回的所有匹配的數量
            "relation":"eq" 文件與搜尋值的關係,eq表示相等
        },
        "max_score":8.044733, 返回結果中文件的最大得分
        "hits":[  查詢結果的文件陣列
            {
                "_index":"kibana_sample_data_ecommerce", 查詢的索引
                "_type":"_doc",  查詢的型別
                "_id":"4X-j7XEB-r_IFm6PISqV", 返回文件的主鍵
                "_score":8.044733,  返回文件的評分
                "_source":{  文件的原始內容
                    "currency":"EUR",
                    "customer_first_name":"Eddie",
                    "customer_full_name":"Eddie Underwood",
                    "customer_gender":"MALE"
                    ......
                }
            }
        ]
    }
}    

 Query Then Fetch潛在的問題

1.深度分頁

  ES索引資料分佈在多個分片上,在查詢時,每個分片都要查詢from+size個文件,Coordinate節點會聚合所有的結果,所以Coordinate節點要處理查詢分片數*(from+size)個文件記錄,對這些記錄進行重新排序,需要的size個文件,from+size的值越大佔用記憶體越多,稱為深度分頁問題,ES預設限制分頁的深度不能超過10000條,可通過max_result_window設定。

  深度分頁解決辦法:

  1)Search After

  可以使用Search After避免深度分頁的效能問題,實時獲取下一頁的文件資訊,search_after根據上一頁最後一個文件的sort值來查詢下一頁,並且當索引資料有變化時,也可以同步被查到,是一個實時查詢的方法。

  例:http://127.0.0.1:9200/kibana_sample_data_ecommerce/_search

    查詢引數:在使用Search_After查詢時,第一步查詢時需要指定sort欄位,並且該sort欄位的排序結果是唯一的,建議使用_id來進行sort,可以指定多個sort欄位。

{
  "size": 1,
  "query": {
    "match": {
      "currency": "EUR"
    }
  },
  "sort": [
    {
      "order_id": {
        "order": "asc"
      }
    }
  ]
}

   返回中可以看到第一頁查詢返回的sort值,查詢下一頁時使用該sort值進行文件的定位,而後每個查詢都會返回一個sort值,供下一頁進行定位使用。

"sort": [
     "550375"
]

  下一頁查詢:

{
  "size": 1,
  "query": {
    "match": {
      "currency": "EUR"
    }
  },
  "search_after": [
    550375
  ],
  "sort": [
    {
      "order_id": {
        "order": "asc"
      }
    }
  ]
}

  Search_After存在的限制:

    a.不能指定from值,即不能想翻到哪一頁就直接跳轉到那一頁,只能一頁一頁按照順序翻;

    b.只能往後翻頁,不能往前翻頁。

  2)Scroll API

  scroll api可以用於從單個搜尋請求中檢索大量的結果,其原理是建立索引在某個時間點的快照,當快照建立後,之後的每次搜尋都會在該快照上進行,對索引的所有新增操作都會被忽略,索引Scroll適合於處理大量資料,但是不能保證資料的實時性。

  POST http://127.0.0.1:9200/kibana_sample_data_ecommerce/_search?scroll=1m

  首次查詢時指定scroll=5m,表示當前搜尋過期時間為5分鐘,即查詢結果在搜到下一次請求之前會儲存多次時間,scroll的值不需要長到把整個快照的資料都處理完,只需保證下一次搜尋請求到來之前能處理完前一批查詢結果即可。

{
    "size": 2,
    "query": {
        "match" : {
            "currency" : "EUR"
        }
    }
}

  返回中可以看到_scroll_id,total.value,scroll_id用於獲取下一批的查詢結果,total.value表示該查詢有總共多少個結果。

{
   "_scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABAGUWdks0dUtFMHZTYmE1Rl9ucGp5X0hoUQ==", 
  "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 4675,
            "relation": "eq"
        },
   }
}

  下一頁:

  http://127.0.0.1:9200/_search/scroll

  下一頁查詢的時候不用指定索引和查詢引數,只需要指定scroll時間和上一次請求返回的scroll_id,因為快照已經建好,只需要在快照上往下翻頁即可。每次執行該請求都會往下進行翻頁,直到查詢的結果為空。

{
  "scroll":"5m",
 "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABAGUWdks0dUtFMHZTYmE1Rl9ucGp5X0hoUQ=="
}

  Scroll API存在的限制:當快照建立後,對索引有新的操作時,無法被查詢到,所以不適合做實時查詢。

不同查詢的使用場景

  一般查詢:需要獲取頂部的部分文件,查詢索引最新的資料。

  全量查詢:使用scroll,當需要匯出全部資料,且對資料的實時性要求不高時。

  分頁查詢:使用from+size,當from+size過大時,使用search after。

2.相關度評分不准問題

  當搜尋請求在多個shard進行資料查詢時,每個分片都會基於自己分片上的文件資料進行相關度的計算,計算方法為TD/IDF,

  TF:詞頻,表示詞條在一個文件中出現的頻率;IDF:逆文件頻率,log(全本文件數/詞條在所有文件中出現的次數),表示該term在所有文件中出現的頻率;如果查詢詞條在某一個文件中出現的頻率(即TF)高,在全部文件中出現的頻率低(即IDF)低,則表明該文件的相關性高。

  每個分片計算IDF的時候只會基於自己分片上的資料進行計算,並不會包含其他分片上的資料,所以這樣會導致相關性評分不準的情況;特別在文件總數很少情況下,主分片數越多,相關性算分會越不準。

  解決相關度評分不准問題的方法:

  1)合理設定分片數量,保證資料均勻分佈。

   當資料量不大時,可以考慮僅設定一個主分數;當資料量較大時,保證文件均勻的分佈在各個分片上。ES提供了routing_partition_size引數,routing_partition_size越大,資料的分佈越均勻(【Elasticsearch學習】之一圖讀懂文件索引全過程 中有提及)。

  2)使用dfs_query_then_fetch

  在搜尋時,指定搜尋的型別search_type=dfs_query_the_fetch,在搜尋的時候,每個分片會把每個分片的TF和IDF進行蒐集,然後綜合所有的資料進行一次完整的相關性評分計算,但是一般不推薦,因為這樣會耗費較多的CPU和記憶體。

 

 

相關文章