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實現類,觀察到如下邏輯:

  1. 檢查這個Query自身提供的isCacheable方法,如果可以的話繼續做下一步判定
  2. 判定對應的Segment是否支持Cache,如果ok,執行緩存動作,進一步判定
  3. 執行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住:

  1. 記錄數> 10000
  2. 記錄數佔所在索引總文檔數比例 > 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漫談


分享到:


相關文章: