モニタリング方法
WEBブラウザから確認したい場合
http://<solr_host>:<solr_port>/solr/#/<Core or Collection>/plugins?type=cache
http://<solr_host>:<solr_port>/solr/<Core or Collection>/admin/mbeans?cat=CACHE&stats=true
項目 | 説明 | 単位 |
---|---|---|
evictions | キャッシュからのエントリ追放数 | エントリ数 |
hitratio | キャッシュのヒット率(hits/lookup) | 割合※パーセンテージではない(0.00〜1.00) |
hits | キャッシュのヒット回数 | 回数 |
inserts | キャッシュへのエントリ挿入回数 | 回数 |
lookups | キャッシュに対するエントリ検索回数 | 回数 |
size | キャッシュサイズ | エントリ数 |
warmupTime | キャッシュのauto-warming時間 | ミリ秒 |
cumulative_xxxは前回のコアのリロード等のキャッシュクリア時からの累積値です。
これに加えて後述の showItems
を属性に設定している場合はキャッシュ中身が表示されます。
パフォーマンスはデータ特性やシステムリソースによって最適値が異なるという大前提の上、特に注目すべき指標は evictions
と hitratio
です。evictionsが増えているということはキャッシュが小さすぎるという可能性もあります。hitratioが低すぎる場合
、キャッシュオフの方がキャッシュ処理のオーバーヘッドが無くなる分早いという可能性もあります。
キャッシュの実装の種類
LRUCache と FastLRUCache
Least Recently Usedのアルゴリズムでキャッシュからevictionされるドキュメントを決定します。
FastLRUCacheはキャッシュに保存(put)するドキュメントのKey,Valueをjava.util.concurrent.ConcurrentHashMapで実装したものになります。
設定ファイル内のヘルプによると、FastLRUCacheはgetが早く、putが遅くなります。hitratioが高い環境、マルチコアCPUの環境など速度向上が見込めるとあります。
LRUCacheとFastLRUCacheの違いは上記のパフォーマンスだけでなく設定可能な値にもあります、例えば LRUCacche
ではstats画面でキャッシュ内のアイテムが表示可能になる showItems
設定が使えません。この辺りは後述します。
LFUCache
Least Frequently Usedでキャッシュからevictionされるドキュメントを決定します。キャッシュアルゴリズム以外(設定値の項目など)はFastLRUCacheに近いです。
LRUとLFUのアルゴリズムの違い
両方とも基本的なロジックですが、動きどんなだっけという時には以下のテストコードがわかりやすいです。
lucene-solr/solr/core/src/test/org/apache/solr/search/TestFastLRUCache.java
public void testOldestItems() { ConcurrentLRUCache<Integer, String> cache = new ConcurrentLRUCache<>(100, 90); for (int i = 0; i < 50; i++) { cache.put(i + 1, "" + (i + 1)); } cache.get(1); cache.get(3); Map<Integer, String> m = cache.getOldestAccessedItems(5); //7 6 5 4 2 assertNotNull(m.get(7)); assertNotNull(m.get(6)); assertNotNull(m.get(5)); assertNotNull(m.get(4)); assertNotNull(m.get(2)); m = cache.getOldestAccessedItems(0); assertTrue(m.isEmpty()); //test this too m = cache.getLatestAccessedItems(0); assertTrue(m.isEmpty()); cache.destroy(); }
lucene-solr/solr/core/src/test/org/apache/solr/search/TestLFUCache.java
public void testItemOrdering() { ConcurrentLFUCache<Integer, String> cache = new ConcurrentLFUCache<>(100, 90); try { for (int i = 0; i < 50; i++) { cache.put(i + 1, "" + (i + 1)); } for (int i = 0; i < 44; i++) { cache.get(i + 1); cache.get(i + 1); } cache.get(1); cache.get(1); cache.get(1); cache.get(3); cache.get(3); cache.get(3); cache.get(5); cache.get(5); cache.get(5); cache.get(7); cache.get(7); cache.get(7); cache.get(9); cache.get(9); cache.get(9); cache.get(48); cache.get(48); cache.get(48); cache.get(50); cache.get(50); cache.get(50); cache.get(50); cache.get(50); Map<Integer, String> m; m = cache.getMostUsedItems(5); //System.out.println(m); // 50 9 7 5 3 1 assertNotNull(m.get(50)); assertNotNull(m.get(9)); assertNotNull(m.get(7)); assertNotNull(m.get(5)); assertNotNull(m.get(3)); m = cache.getLeastUsedItems(5); //System.out.println(m); // 49 47 46 45 2 assertNotNull(m.get(49)); assertNotNull(m.get(47)); assertNotNull(m.get(46)); assertNotNull(m.get(45)); assertNotNull(m.get(2)); m = cache.getLeastUsedItems(0); assertTrue(m.isEmpty()); //test this too m = cache.getMostUsedItems(0); assertTrue(m.isEmpty()); } finally { cache.destroy(); } }
キャッシュの種類
種類
- filterCache/フィルタキャッシュ
- queryResultCache/検索結果キャッシュ
- documentCache/ドキュメントキャッシュ
- fieldValueCache/フィールド値キャッシュ
キャッシュの画面上には fieldCache
というのもありますが、これはLucene側のキャッシュでSolrの管理化にはありませんので、ここでは除外します。
ただ、Solr 6系使っていて、fieldのdocValuesが true
であればdocValuesの方が使われるので、fieldCacheは使われないはずです。 docValuesがfalseの場合は使われていたとしても以下のようなクラスが格納されていることがキャッシュの管理画面から見られると思います。(これはDOUBLEの場合)
long,org.apache.lucene.uninverting.FieldCache.LEGACY_DOUBLE_PARSER=>org.apache.lucene.uninverting.FieldCacheImpl$LongsFromArray
これはLuceneの UninvertingReader
というクラスを経由しているのですが、このクラス自体も含めて Deprecated で 7.0系では消えるという話もあります。
[LUCENE-7283] Move SlowCompositeReaderWrapper and uninverting package to solr sources - ASF JIRA
lucene-solr/UninvertingReader.java at releases/lucene-solr/6.6.0 · apache/lucene-solr · GitHub
DocValuesについてはこちらに調べたこと書きました
設定項目
属性 | 説明 | デフォルト値 | LRU | FastLRU | LFU |
---|---|---|---|---|---|
class | キャッシュの実装を指定 | solr.LRUCache | ○ | ○ | ○ |
size | 上限サイズ(エントリ数指定) | 1024 | ○ | ○ | ○ |
initialSize | 初期サイズ(エントリ数指定) | size で指定した値 |
○ | ○ | ○ |
autowarmCount | 自動ウォームアップ対象のエントリ数(LFUCache以外はパーセント指定も可能 "90%" ) |
0(disable) | ○ | ○ | ○ |
maxRamMB | ヒープメモリサイズの上限値(MB) | Long.MAX_VALUE | ○ | - | - |
minSize | 最小サイズ(エントリ数指定) | size で指定した値の90% |
- | ○ | ○ |
acceptableSize | キャッシュのクリーンアップ閾値(詳細は後述) | size で指定した値の95% |
- | ○ | ○ |
cleanupThread | キャッシュのクリーンアップ処理を別スレッドで行う(true or false) | false | - | ○ | ○ |
showItems | statsで指定したエントリ数のキャッシュの中身を表示 | アイテム数を指定。FastLRUCacheとLFUCacheのみ指定可 | - | ○ | ○ |
timeDecay | クリーンアップ処理時に既存のエントリのLFUで使われるヒットカウントを下げる(入れ替えが早まる) | true | - | - | ○ |
FastLRUCacheとLFUCacheのキャッシュサイズ制御の動き
FastLRUCache
と LFUCache
は設定項目が多いためキャッシュ制御の動きの概要を整理しておきます。
initialSize
でキャッシュ確保- 最大で
size
までキャッシュにエントリを保存 - エントリをキャッシュに保存する際に
size
を超えてしまっていた場合クリーンアップを処理を実行 cleanupThread
がtrue
の場合クリーンアップ処理は専用の別スレッドで行われる- クリーンアップ処理は
minSize
をターゲットに進められるが十分なエントリが削除できなかった場合はacceptableSize
をターゲットに更にクリーンアップ処理を進める(ソース見る限りにおいてはこれが機能してるのはFastLRUのみな気がします)
filterCache
フィルタキャッシュはフィルタクエリ(fq)の絞り込み条件と、条件に合うドキュメントID(int)の集合をキャッシュ。facet.methodが enum
の場合などのファセット処理にも使われます。何がキャッシュされているかは showItems
で確かめるとわかりやすいです。
- キャッシュ内部の例
item_price:[1000.0 TO 4999.0] :org.apache.solr.search.DocSet
- 設定例
<filterCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="128"/>
useFilterForSortedQuery
Scoreを利用しないSortにはFilterCacheを適用する設定らしいです。デフォルトでコメントアウトされていますが、特種なシチュエーションでは役に立つかもです。
<!-- Use Filter For Sorted Query A possible optimization that attempts to use a filter to satisfy a search. If the requested sort does not include score, then the filterCache will be checked for a filter matching the query. If found, the filter will be used as the source of document ids, and then the sort will be applied to that. For most situations, this will not be useful unless you frequently get the same search repeatedly with different sort options, and none of them ever use "score" --> <!-- <useFilterForSortedQuery>true</useFilterForSortedQuery> -->
コードにも注意書きがある。
// OK, so now we need to generate an answer. // One way to do that would be to check if we have an unordered list // of results for the base query. If so, we can apply the filters and then // sort by the resulting set. This can only be used if: // - the sort doesn't contain score // - we don't want score returned. // check if we should try and use the filter cache boolean useFilterCache = false; if ((flags & (GET_SCORES | NO_CHECK_FILTERCACHE)) == 0 && useFilterForSortedQuery && cmd.getSort() != null && filterCache != null) { useFilterCache = true; SortField[] sfields = cmd.getSort().getSort(); for (SortField sf : sfields) { if (sf.getType() == SortField.Type.SCORE) { useFilterCache = false; break; } } }
queryResultCache
クエリ内の q
sort
fq(filtered query)
の3パラメータ取得したハッシュをキーとして、検索結果のドキュメントID(int)をリスト構造(DocList)でキャッシュに保持します。並び順も保持されます。
- キャッシュ内部の例
org.apache.solr.search.QueryResultKey : org.apache.solr.search.DocList
- 設定例
<queryResultCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="128" maxRamMB="1000"/>
他にも設定項目あります。
queryResultMaxDocsCached
solrconfig.xmlに設定します。
<!-- Maximum number of documents to cache for any entry in the queryResultCache. --> <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
ここで設定した値以上の結果セットはキャッシュされないようです。
// lastly, put the superset in the cache if the size is less than or equal // to queryResultMaxDocsCached if (key != null && superset.size() <= queryResultMaxDocsCached && !qr.isPartialResults()) { queryResultCache.put(key, superset); }
documentCache
filterCache
と queryResultCache
がドキュメントID(int)をキャッシュするのに対して、documentCacheはドキュメントIDの先にあるLuceneのドキュメントオブジェクトをキャッシュします。このオブジェクトはインデックス更新のタイミングで変化するので、Auto Warming機能は利用できません。(他のキャッシュはautowarm時に保持してあったドキュメントIDを利用してLuceneオブジェクトをディスクからフェッチしている)
<documentCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0"/>
- キャッシュ内部の例
item_12345 : Luceneオブジェクト
fieldValueCache
Lucene管理化のstoredのfieldの値のキャッシュで、ファセット等に使われる情報がキャッシュされるようです。docValuesがTrueだと使われません。
この fieldValueCache
は設定を明示的に入れなくても自動的にキャッシュの設定が有効かされstats画面にが表示されます。
このfieldValueCacheですが、SolrのWikiには記載があります。
しかし、ソースによっては記載が無いところもあります。
Query Settings in SolrConfig - Apache Solr Reference Guide - Apache Software Foundation
ここにも無い。
docValuesをfalseにしてもstatsの数値変化無く、このあたり見ても使われて無さそうだけど、どうなんでしょう。
lucene-solr/SolrIndexSearcher.java at branch_6_0 · apache/lucene-solr · GitHub
使われなくなっているという情報もあるようです。
Solr 5系はファセットが遅い? – NaviPlus Engineers' Blog
上記の通り使われてないのではと思い、今のところ気にしてません。 fieldCache
との違いも良くわかってないです。Solr In Action
の関連の項目見ると同じものなのでは。
その他・注意点
キャッシュそのものはCPU負荷の削減に繋がるものです。しかし、キャッシュのクリーンアップ処理はそれなりに負荷がかかります。 別スレッドで実行することも可能ですが、特にCPUボトルネックの場合はシステム全体のスループットに影響が出る場合があります。クエリやデータ、evictionsの数値が高くなってたら要注意です。キャッシュのサイズを増やすか、場合によってはキャッシュを切るほうが有効な時もあります。
キャッシュのSizeは基本的にアイテム数での設定になるますが、1アイテムあたりの容量はデータの内容により異なります。minimumで設定した値まで減らしてもHeapの空き領域を十分に確保できない場合、設定によってはGCが永遠と走り続けるような事象に陥る場合もありますので、設定値はよく検証して算出する必要があります。
参考
URL
関連ソースコード
lucene-solr/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java lucene-solr/solr/core/src/java/org/apache/solr/search/SolrCache.java lucene-solr/solr/core/src/java/org/apache/solr/search/SolrCacheBase.java lucene-solr/solr/core/src/java/org/apache/solr/search/LFUCache.java lucene-solr/solr/core/src/java/org/apache/solr/search/LRUCache.java lucene-solr/solr/core/src/java/org/apache/solr/search/FastLRUCache.java lucene-solr/solr/core/src/java/org/apache/solr/util/ConcurrentLFUCache.java lucene-solr/solr/core/src/java/org/apache/solr/util/ConcurrentLRUCache.java
参考書籍
[改訂第3版]Apache Solr入門 ―オープンソース全文検索エンジン
- 作者: 打田智子,大須賀稔,大杉直也,西潟一生,西本順平,平賀一昭
- 出版社/メーカー: 技術評論社
- 発売日: 2017/04/27
- メディア: Kindle版
- この商品を含むブログを見る
- 作者: Surendra Mohan
- 出版社/メーカー: Packt Publishing
- 発売日: 2014/03/25
- メディア: ペーパーバック
- この商品を含むブログを見る