Solrのキャッシュについて調査したことまとめ

モニタリング方法

WEBブラウザから確認したい場合

http://<solr_host>:<solr_port>/solr/#/<Core or Collection>/plugins?type=cache

jsonxml等で取得したい場合

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 を属性に設定している場合はキャッシュ中身が表示されます。

パフォーマンスはデータ特性やシステムリソースによって最適値が異なるという大前提の上、特に注目すべき指標は evictionshitratio です。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の管理化にはありませんので、ここでは除外します。

https://wiki.apache.org/solr/SolrCaching#The_Lucene_FieldCache

設定項目

属性 説明 デフォルト値 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のキャッシュサイズ制御の動き

FastLRUCacheLFUCache は設定項目が多いためキャッシュ制御の動きの概要を整理しておきます。

  • initialSize でキャッシュ確保
  • 最大で size までキャッシュにエントリを保存
  • エントリをキャッシュに保存する際に size を超えてしまっていた場合クリーンアップを処理を実行
  • cleanupThreadtrue の場合クリーンアップ処理は専用の別スレッドで行われる
  • クリーンアップ処理は 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"/>

queryResultCache

クエリ内の q sort fq(filtered query) の3パラメータ取得したハッシュをキーとして、検索結果のドキュメントID(int)をリスト構造(DocList)でキャッシュに保持します。並び順も保持されます。

* <field count per user query

  • キャッシュ内部の例
org.apache.solr.search.QueryResultKey : org.apache.solr.search.DocList
  • 設定例
<queryResultCache class="solr.LRUCache"
                  size="512"
                  initialSize="512"
                  autowarmCount="128"
                  maxRamMB="1000"/>

documentCache

filterCachequeryResultCache がドキュメントID(int)をキャッシュするのに対して、documentCacheはドキュメントIDの先にあるLuceneのドキュメントオブジェクトをキャッシュします。このオブジェクトはインデックス更新のタイミングで変化するので、Auto Warming機能は利用できません。(他のキャッシュはautowarm時に保持してあったドキュメントIDを利用してLuceneオブジェクトをディスクからフェッチしている)

<documentCache class="solr.LRUCache"
               size="512"
               initialSize="512"
               autowarmCount="0"/>
  • キャッシュ内部の例
item_12345 : Luceneオブジェクト

fieldValueCache

ファセットに使われる情報がキャッシュされるようです。docValuesがTrueだと使われません。

この fieldValueCache は設定を明示的に入れなくても自動的にキャッシュが取られます。

docValuesについてはここがわかりやすかったです。

DocValues aka. Column Stride Fields in Lucene 4.0 - By Willnauer Simon

ただ、このfieldValueCacheですが、SolrのWikiには記載があります。

SolrCaching - Solr Wiki

しかし、ソースによっては記載が無いところもあります。

Query Settings in SolrConfig - Apache Solr Reference Guide - Apache Software Foundation

ここにも無い。

lucene-solr/performance-statistics-reference.adoc at releases/lucene-solr/6.6.0 · apache/lucene-solr · GitHub

docValuesをfalseにしてもstatsの数値変化無く、このあたり見ても使われて無さそうだけど、どうなんでしょう。

lucene-solr/SolrIndexSearcher.java at branch_6_0 · apache/lucene-solr · GitHub

使われなくなっているという情報もあるようです。

Solr 5系はファセットが遅い? – NaviPlus Engineers' Blog

参考

URL

SolrCaching - Solr Wiki

関連ソースコード

github.com

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

lucene-solr/performance-statistics-reference.adoc at releases/lucene-solr/6.6.0 · apache/lucene-solr · GitHub

参考書籍

[改訂第3版]Apache Solr入門 ―オープンソース全文検索エンジン

[改訂第3版]Apache Solr入門 ―オープンソース全文検索エンジン

Apache Solr High Performance: Boost the Performance of Solr Instances and Troubleshoot Real-time Problems

Apache Solr High Performance: Boost the Performance of Solr Instances and Troubleshoot Real-time Problems