天天看點

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

本節書摘來自華章出版社《深入了解elasticsearch(原書第2版)》一書中的第2章,第2.4節,作者[美]拉斐爾·酷奇(rafal ku) 馬雷克·羅戈任斯基(marek rogoziski),更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視

接下來,我們一起認識一下elasticsearch提供的過濾功能。初看起來,過濾好像一個多餘的功能,因為幾乎每個過濾器在elasticsearch查詢dsl中都以一個與查詢代碼極其相似的方式呈現的。不過,這些過濾器一定有其獨到之處,不然它們就不會在查詢性能優化時被廣泛使用和推薦了。本節我們将着重探讨為什麼過濾器如此重要,它們的工作原理,以及elasticsearch都提供了哪些過濾器給我們使用。

2.4.1 過濾及查詢相關性

普通查詢和過濾的第一個差異在于它們對文檔打分的影響。讓我們舉例對比一下查詢和過濾的輸出。首先執行如下查詢:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理
《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

這個查詢的結果如下:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

這個查詢沒有任何特異之處。elasticsearch将傳回所有在title字段中包含“front”的文檔。需要指出的是,每個和查詢比對的文檔都會被計算得分,其中得分最高的一組文檔被作為查詢結果傳回給使用者。在本例中,該查詢傳回了一篇得分為0.11506981的文檔。以上這些就是查詢的一般行為。

接着我們來對比一下查詢和過濾。在一個同時包含查詢和過濾的例子中,我們将加入一段代碼片段,限制傳回文檔隻能有一個副本(copies字段取值為1)。不使用過濾的查詢方式如下:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理
《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

elasticsearch傳回的查詢結果和上一個查詢非常相似:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

上面這段查詢代碼中的bool查詢由兩個term查詢構成,每個結果文檔都需要同時比對這兩個term查詢。這個查詢傳回了和上一查詢相同的文檔,不過文檔得分變成了0.98976034。這和我們讀完2.1節後的期望一緻:每個詞項都會影響得分。

接下來我們來看看使用過濾的查詢方式,在titile字段比對“front”的查詢,同時針對copies字段進行過濾。

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

現在,我們構造好了一個term查詢,同時還添加了一個term過濾器。從下面的傳回代碼中可以看出,輸出的文檔和不使用過濾時一樣,不過文檔得分發生了變化。

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理
《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

這個文檔的得分為0.11506981,這和本節最開始的查詢結果一模一樣。通過得分對比我們得出結論:過濾不影響文檔得分。

 舊版elasticsearch使用“filter”而不是上述代碼中的“post_filter”來辨別查詢語句中的過濾片段。在1.x版本中,這兩種标記方式都可以正常使用,不過請注意,“filter”方式可能将在之後的版本中停用。

一般來說,查詢和過濾在工作過程中存在一個主要的差異。過濾的唯一目的是用特定篩選條件來縮小結果範圍。而查詢不僅縮小結果範圍,還會影響文檔的得分,這一點在強調文檔相關性時非常重要,不過需要付出一定的代價:需要額外的cpu消耗來計算文檔得分。當然,這不是查詢和過濾的唯一差別。本節剩餘部分将着重探讨過濾器的工作原理和elasticsearch提供的不同過濾方式之間的異同。

2.4.2 過濾器的工作原理

前一小節我們已經提到,過濾不影響所比對文檔的得分。基于兩個原因,這一點非常重要。第1個原因是性能。針對索引中的一組文檔進行過濾操作是非常簡單高效的。過濾器持有的關于文檔的唯一重要資訊是該文檔是否比對這個過濾器—僅僅一個标記而已。

下表展示了這些類是如何使用bits進行工作的。

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

lucene(以及elasticsearch)提供了docidset的多種實作來應對不同場景。不同實作的性能各不相同。不過,選擇合适的實作是lucene和elasticsearch的職責,我們一般不需要關心這一點,除非我們要針對它們進行功能擴充。

 不是所有的過濾器都使用bits結構,比如數值區間過濾器、腳本過濾器、以及基于地理位置的一組過濾器。這些特殊的過濾器選擇把資料記錄在字段緩存裡,然後再周遊所需處理的文檔集合,逐個進行過濾操作。這意味着過濾器鍊條中的下一個過濾器隻能擷取到比對前一個過濾器的文檔集合。是以,可以針對這些過濾器進行優化,比如把最重的(譯者注:比對文檔最多的,或者性能最差的)過濾器放到過濾器鍊的最後去執行。

布爾過濾器和與或非過濾器

我們在《elasticsearch server,second edition》一書中探讨了過濾器的有關知識,在這裡隻需要提醒讀者注意一點:與或非過濾器不使用bits,而布爾過濾器使用了bits。是以,請盡可能使用布爾過濾器。與或非過濾器一般在需要腳本過濾、地理位置過濾和數值區間過濾時使用。還需要注意的是,如果把任何不使用bits的過濾器嵌套在與或非過濾器中,它們同樣不會用到bits。

一般來講,在組合使用多個處理器時,如果其中包含不使用bits的處理器,則需要使用與或非處理器來對它們進行組合。而如果要組合的所有處理器都使用bits,則可以選擇使用布爾過濾器來組合它們。

2.4.3 性能考量

通常,過濾器都是很快的。這一點有多種原因。首先,最重要的一點是,由過濾器所處理的查詢部分不需要計算文檔得分。之前我們就提到過,打分過程與給定查詢和索引中的文檔集合密切相關。

 使用過濾器時需要注意一點:在elasticsearch 1.4.0版本釋出後,執行嵌套查詢時所使用的bitsets預設提前就加載好了。這樣做的目的是使嵌套查詢執行得更快,不過可能伴随記憶體使用問題。可以通過設定index.load_fixed_bitset_filters_eagerly配置項為false來禁用提前加載。如果需要檢視固定大小bitsets的記憶體占用情況可以執行以下指令:curl -xget ‘localhost:9200/_cluster/stats?human&pretty’,在響應中尋找fixed_bit_set_memory_in_bytes屬性即可。

在使用過濾器時,過濾結果不依賴于查詢,是以過濾結果可以被輕易地緩存起來供後續查詢使用。值得一提的是,每個lucene索引段都有一個過濾結果緩存。這意味着無需在每次commit時重建緩存,重建操作隻發生在段生成和合并時。

 當然,有得必有失,過濾器也有一些不好的地方。不是所有的過濾器都可以被緩存。考慮那些依賴于目前時間的過濾器,對它們做緩存不會有任何意義。在某些場景下不值得做緩存,因為可能存在非常多的唯一值,緩存命中率極低,比如基于地理位置過濾的場景。

2.4.4 後置過濾和過濾查詢

如果某人說過濾比實作相同功能的查詢執行更快,這不一定是真的。的确,過濾器需要考慮的東西更少,并且可以在後續查詢中複用,不過lucene早就針對查詢做了高度優化,以確定查詢能夠高速執行,甚至在考慮文檔評分的情境下。當然,如果比對結果數量極多,過濾器會執行得更快一些。不過,還有一些事我們沒有告訴你。某些時候,在使用後置過濾(post_filter)時,elasticsearch查詢的執行速度沒有我們期望的那麼快。假如我們執行如下查詢:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

下圖展示了查詢的執行過程:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

當然,針對大量資料的過濾是很有價值的。不過在本例中,我們使用現有的少量資料。從上圖可見,索引中包含4個文檔。例子中的terms查詢比對了3個文檔:doc1、doc3和doc4。每個比對的文檔都被計算得分并根據得分做了排序。之後,post_filter開始工作。在索引的所有文檔中,它隻通過了兩個文檔:doc1和doc4。可以看到,一共傳遞給過濾器3個文檔,而隻有其中的兩個被作為結果輸出。既然如此,還有必要對doc3計算得分嗎?本例我們浪費了一部分cpu時間來計算一個最終不比對的文檔的得分。如果類似的文檔數量很多,這将是一個性能問題。

讓我們修改一下這個查詢,讓文檔過濾操作發生在scorer計算文檔得分之前。修改後的查詢如下:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理
《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

在這裡我們使用了過濾查詢(filtered query)。傳回的查詢結果和前一個查詢一模一樣,不過執行過程稍微有一些變化,特别是在執行過濾操作時。下圖揭示了這個查詢在理論上的執行過程:

《深入了解Elasticsearch(原書第2版)》一2.4 過濾器的使用及作用原理

現在,最初的工作是由term過濾器完成的。如果這個過濾器在之前被使用過,它将從緩存中加載,整個文檔集合将被篩成隻剩兩個文檔。最後,這兩個文檔仍然需要被計算得分,不過評分子產品需要做的工作少了一些。當然,本例中,查詢和過濾後的文檔相比對,不過這一點并非在所有查詢場景下都滿足。

從技巧上看,我們讓過濾器被一個查詢所包裹,讓lucene庫能夠隻收集被過濾通過的結果。當然,過濾通過的結果還需要被傳遞給主查詢做進一步處理。多虧了過濾器,打分程式需要處理的文檔數量減少了。

2.4.5 選擇正确的過濾方式

讀了前述關于後置過濾和過濾查詢的解釋,你可能會在以後隻考慮使用過濾查詢并遠離後置過濾。這一規則在絕大多數情況下是正确的,不過在某些條件下,存在例外情況。經驗法則告訴我們,開銷最大的操作需要移動到查詢處理鍊條的尾部。如果過濾器執行很快,開銷很小,并且易于緩存,很簡單,直接選擇過濾查詢即可。相反,如果過濾器執行很慢,cpu開銷大,并且難于緩存(比如有大量唯一值的情況),請使用後置過濾,或者嘗試優化過濾器。優化途徑包括簡化過濾器和使得過濾器對緩存更友好,比如,可以降低時間區間過濾器的時間粒度。

繼續閱讀