ElasticSearch QueryCache漫談

Query Cache是什麼

首先 ES Query Cache是例項級別的,作用域是Node例項。其次,ES Query Cache 本質是快取Query裡面的子Query的查詢結果。他是按照子Query來確定是否被Cache。 Cache的結果是DocIdSet,可以簡單理解為布隆過濾器。

如何判定一個子Query是否會被Cache住

ES已經比較智慧,你可以寫一個非常複雜的Query,但是他會自動挑選裡面某一部分進行Cache。那麼我們如何知道一個子Query是否會被Cache住呢?遺憾的是現在沒有文件做羅列,不過我們根據LRUQueryCache實現類,觀察到如下邏輯:

檢查這個Query自身提供的isCacheable方法,如果可以的話繼續做下一步判定

判定對應的Segment是否支援Cache,如果ok,執行快取動作,進一步判定

執行QueryCachePolicy相應的策略方法。該方法做了一些列舉,比如TermQuery,MatchAllDocsQuery等是不快取的,然後會根據使用頻次來確定是否加入到快取。

所以,我們可以簡單的找到對應的Query實現,檢視相應的isCacheable方法。

比如BinaryDocValuesRangeQuery,也就是Range查詢(Int,Double等),對應的isCache方法如下:

public boolean isCacheable(LeafReaderContext ctx) { return DocValues。isCacheable(ctx, fieldName); } /** * Returns {@code true} if the specified docvalues fields have not been updated */ public static boolean isCacheable(LeafReaderContext ctx, String。。。 fields) { for (String field : fields) { FieldInfo fi = ctx。reader()。getFieldInfos()。fieldInfo(field); if (fi != null && fi。getDocValuesGen() > -1) return false; } return true; }

從上面程式碼可以知道,通常簡單的DocValues欄位,做Range查詢都是支援Cache的。那麼TermRangeQuery呢?其實也是可以cache的。對應的程式碼就更簡單了:

public boolean isCacheable(LeafReaderContext ctx) { return true; }

我們繼續看,termQuery呢?開啟TermQuery你會發現他的isCacheable也是返回true,但是因為在第三步的的QueryCache策略裡的shouldNeverCache方法中被判定為不能快取,所以不會進行快取。

一般而言QueryCache只會快取細粒度的結果,比如BoolQuery之類肯定是不會快取的。

常見的一些配置

首先,是快取肯定會做快取條數和記憶體的限制。記憶體限制透過引數:

indices。queries。cache。size = 10%

控制。

條數則是透過引數:

indices。queries。cache。count=1000

這裡的cache count指是query的條數。但是在node stats APi 你並不能看到這個值的情況,你會看到的兩個讓人迷惑的指標:

cache_count cache_size

前面我們提及,雖然我們cache一個子query,但其實因為這個query對應的segment是很多的,所以系統需要快取多個segment查詢後對應的bitset結果。於是我們有了公式:

cacheSize = 當前符合cache條件的segment * 符合cache條件的子query數量

也就是一個子query會快取多份資料,每份資料來源於相應的segment。 cacheCount 則是歷史所有發生的cache行為。

一個符合條件的子query作用於某個segment時,這個segment如果滿足以下任一條件,則會被cache住:

記錄數> 10000

記錄數佔所在索引總文件數比例 > 3%

通常我們認為MultiTermQuery,MultiTermQueryConstantScoreWrapper,TermInSetQuery,PointQuery建立過程是比較重的,所以在快取策略裡,他們訪問的較少也會被快取。

Query Cache索引結構

有一個ES Node級別的Cache,該Cache你可以理解為一個Map,該Map又會針對每個Segment有一個LeafCache,LeaCache的key是query,value則是DocIdSet。這樣是不是清晰了很多。新增cache的時候,會註冊一個回撥,如果Segment被合併或者刪除,那麼就會被移除快取。

過期策略

每次新新增一個cache都會檢測下是否需要過期一些query。如果Segment被合併或者刪除,那麼也會清理掉對應的快取。

UsageTrackingQueryCachingPolicy

在ES裡,QueryCacheingPolicy的預設實現是UsageTrackingQueryCachingPolicy。該Policy的基本思路是根據使用頻率決定是否快取。對於構建成本較高的索引,比如MultiTermQuery,MultiTermQueryConstantScoreWrapper,TermInSetQuery,PointQuery 最近使用超過2次(維護了一個256大小的環狀使用歷史記錄)則會被索引。而且其他的一些query則需要5次。 同時,UsageTrackingQueryCachingPolicy 還維護了一個不使用cache的Query列表,比如TermQuery,MatchAllDocsQuery,MatchNoDocsQuery,以及子query為空的BooleanQuery,DisjunctionMaxQuery。

ElasticSearch QueryCache漫談