ElasticSearch SearchType詳解
一篇關於ElasticSearch SearchType的詳情
學習了
SearchType詳解
es在查詢時,可以指定搜尋型別為
QUERY_THEN_FETCH,QUERY_AND_FEATCH,DFS_QUERY_THEN_FEATCH和DFS_QUERY_AND_FEATCH(SACN,COUNT都已不建議使用)。那麼這4種搜尋型別有什麼區別?
elasticsearch java api中還有個default
public static final SearchType DEFAULT = QUERY_THEN_FETCH;
分散式搜尋背景介紹:
ES天生就是為分散式而生,但分散式有分散式的缺點。比如要搜尋某個單詞,但是資料卻分別在5個分片(Shard)上面,這5個分片可能在5臺主機上面。因為全文搜尋天生就要排序(按照匹配度進行排名),但資料卻在5個分片上,如何得到最後正確的排序呢?ES是這樣做的,大概分兩步。
step1. ES客戶端會將這個搜尋詞同時向5個分片發起搜尋請求,這叫Scatter,
step2. 這5個分片基於本Shard獨立完成搜尋,然後將符合條件的結果全部返回,這一步叫Gather。
客戶端將返回的結果進行重新排序和排名,最後返回給使用者。也就是說,ES的一次搜尋,是一次scatter/gather過程(這個跟mapreduce也很類似).
然而這其中有兩個問題:
第一、數量問題。比如,使用者需要搜尋”雙黃連”,要求返回最符合條件的前10條。但在5個分片中,可能都儲存著雙黃連相關的資料。所以ES會向這5個分片都發出查詢請求,並且要求每個分片都返回符合條件的10條記錄。當ES得到返回的結果後,進行整體排序,然後取最符合條件的前10條返給使用者。這種情況,ES5個shard最多會收到10*5=50條記錄,這樣返回給使用者的結果數量會多於使用者請求的數量。
第二、排名問題。上面搜尋,每個分片計算分值都是基於自己的分片資料進行計算的。計算分值使用的詞頻率和其他資訊都是基於自己的分片進行的,而ES進行整體排名是基於每個分片計算後的分值進行排序的,這就可能會導致排名不準確的問題。如果我們想更精確的控制排序,應該先將計算排序和排名相關的資訊(詞頻率等)從5個分片收集上來,進行統一計算,然後使用整體的詞頻率去每個分片進行查詢。
這兩個問題,估計ES也沒有什麼較好的解決方法,最終把選擇的權利交給使用者,方法就是在搜尋的時候指定query type。
1、query and fetch
向索引的所有分片(shard)都發出查詢請求,各分片返回的時候把元素文件(document)和計算後的排名資訊一起返回。這種搜尋方式是最快的。因為相比下面的幾種搜尋方式,這種查詢方法只需要去shard查詢一次。但是各個shard返回的結果的數量之和可能是使用者要求的size的n倍。
2、query then fetch(預設的搜尋方式)
如果你搜索時,沒有指定搜尋方式,就是使用的這種搜尋方式。這種搜尋方式,大概分兩個步驟,第一步,先向所有的shard發出請求,各分片只返回排序和排名相關的資訊(注意,不包括文件document),然後按照各分片返回的分數進行重新排序和排名,取前size個文件。然後進行第二步,去相關的shard取document。這種方式返回的document可能是使用者要求的size的n倍,此處勘誤,這是原部落格中的錯誤,經測試 query
then fetch
方式返回的數量就是 查詢是 setSize()的數量
3、DFS query and fetch
這種方式比第一種方式多了一個初始化散發(initial scatter)步驟,有這一步,據說可以更精確控制搜尋打分和排名。這種方式返回的document與使用者要求的size是相等的。同樣勘誤 DFS
query and fetch 返回結果的數量是 分片數*size
4、DFS query then fetch
比第2種方式多了一個初始化散發(initial scatter)步驟。這種方式返回的document與使用者要求的size是相等的。
結論:
QUERY_AND_FETCH 和 QUERY_AND_FETCH 的排序方式沒有本質區別 都是各個分片取size片 統一排序,區別在於 QUERY_AND_FETCH 是將所有資料取回再進行排序 只訪問一次索引 而QUERY_AND_FETCH 是先將所有資料的id和score取回 排序後取前size的id 再根據id到各個分片召回對應的文件 訪問了兩次索引 所以二者的排序結果一致,但這二者存在同一個缺陷 各個分片計分規則不同(各個分片的詞頻等會影響計分結果)相關性排序上會有些誤差 而DFS_QUERY_AND_FETCH 和 DFS_QUERY_THEN_FETCH 是統一了計分規則 保證查到的資料是索引中最最相關的,但時效性較前二者有劣勢DSF是什麼縮寫?初始化散發是一個什麼樣的過程?
從es的官方網站我們可以指定,初始化散發其實就是在進行真正的查詢之前,先把各個分片的詞頻率和文件頻率收集一下,然後進行詞搜尋的時候,各分片依據全域性的詞頻率和文件頻率進行搜尋和排名。顯然如果使用DFS_QUERY_THEN_FETCH這種查詢方式,效率是最低的,因為一個搜尋,可能要請求3次分片。但,使用DFS方法,搜尋精度應該是最高的。
至於DFS是什麼縮寫,沒有找到相關資料,這個D可能是Distributed,F可能是frequency的縮寫,至於S可能是Scatter的縮寫,整個單詞可能是分散式詞頻率和文件頻率散發的縮寫。
總結一下,從效能考慮QUERY_AND_FETCH是最快的,DFS_QUERY_THEN_FETCH是最慢的。從搜尋的準確度來說,DFS要比非DFS的準確度更高。
最後來看下原始碼:
public enum SearchType {
/**
* Same as {@link #QUERY_THEN_FETCH}, except for an initial scatter phase which goes and computes the distributed
* term frequencies for more accurate scoring.
*/
DFS_QUERY_THEN_FETCH((byte) 0),
/**
* The query is executed against all shards, but only enough information is returned (not the document content).
* The results are then sorted and ranked, and based on it, only the relevant shards are asked for the actual
* document content. The return number of hits is exactly as specified in size, since they are the only ones that
* are fetched. This is very handy when the index has a lot of shards (not replicas, shard id groups).
*/
QUERY_THEN_FETCH((byte) 1),
/**
* Same as {@link #QUERY_AND_FETCH}, except for an initial scatter phase which goes and computes the distributed
* term frequencies for more accurate scoring.
*/
DFS_QUERY_AND_FETCH((byte) 2),
/**
* The most naive (and possibly fastest) implementation is to simply execute the query on all relevant shards
* and return the results. Each shard returns size results. Since each shard already returns size hits, this
* type actually returns size times number of shards results back to the caller.
*/
QUERY_AND_FETCH((byte) 3),
/**
* Performs scanning of the results which executes the search without any sorting.
* It will automatically start scrolling the result set.
* @deprecated will be removed in 3.0, you should do a regular scroll instead, ordered by `_doc`
*/
@Deprecated
SCAN((byte) 4),
/**
* Only counts the results, will still execute aggregations and the like.
* @deprecated does not any improvements compared to {@link #QUERY_THEN_FETCH} with a `size` of {@code 0}
*/
@Deprecated
COUNT((byte) 5);
/**
* The default search type ({@link #QUERY_THEN_FETCH}.
*/