【Elasticsearch學習】文檔搜索全過程

  在ES執行分佈式搜索時,分佈式搜索操做須要分散到全部相關分片,若一個索引有3個主分片,每一個主分片有一個副本分片,那麼搜索請求會在這6個分片中隨機選擇3個分片,這3個分片有多是主分片也多是副本分片,而後收集全部分片的查詢結果。因此ES的搜索過程分爲兩個階段,Query階段和Fetch階段;ES有兩種搜索類型:query_then_fetch,dfs_query_then_fetch。html

  1.Query階段java

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

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

  2.Fetch階段分佈式

  1)重排序。Coordinate節點收到數據節點返回的數據後,會按照返回的排序值對從全部分片取回的值從新進行排序,最終只選取客戶端須要的from+size個文檔的Id。性能

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

查詢結果解讀:fetch

{
    "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潛在的問題spa

1.深度分頁code

  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和內存。

相關文章
相關標籤/搜索