再・Solr のパフォーマンスチューニング

三年ほど前、Solr のパフォーマンスチューニングの記事を書いた。

aoking.hatenablog.jp

これは古くなっており、現在の Solr では通用しない部分が多い。
改めて、現在の Solr のパフォーマンスチューニングに対する知見をここに記す。

はじめに

僕は日々 Solr のお世話をせっせとしており、今では扱っているインデクスのサイズが数テラバイト程度になっている。性能問題にぶつかることも何度もあり、そのたびに Solr のパラメーターのチューニングを行ってきており、時には自社向けのパッチを当てて高速化したり省メモリ化することもあった。僕の知見はきっと Solr ユーザーの皆様に役立つものでしょう。

さて、Solr のチューニングをしたい人は下記の公式 Wiki を穴が空くほど読むべし。
SolrPerformanceProblems - Solr Wiki

結論から言えば、はっきり言ってマシンのスペックアップが一番効果的で、特にストレージの高速化が行えるのならそれ以外のチューニングを頑張ったところで効果は微々たるものである。

Solr は本来とても高速で、インデクスデータのサイズが通常の範囲内であるならば、ほとんどのクエリは高速に処理できる。 SQL のようにクエリのチューニングで 1000 倍高速化するようなシーンは幸か不幸か Solr ではなかなかお目にかかれない。故に、クエリのチューニングやパラメーターの修正を四苦八苦するより、ストレージの高速化にお金をつぎ込んだほうが効果的、というのが僕の持論だ。

遅い原因

あらためて Solr が遅い原因について記そう。Solr が遅い原因は 99.9% がストレージにある。よって、究極的には検索を高速にするには下記3つが我々が取るべき手段となる。

  1. ストレージを高速なものに置き換える
  2. 不要な IO を減らす
  3. インデクスサイズを小さくする

考えることは基本的にこの3つを如何にして満たすか、ということに尽きる。

チューニングその1 : ストレージを高速なものに置き換える

前述の Wiki をよく読むととても目を引く文章が現れる。

Solid State Disks are amazing.

https://wiki.apache.org/solr/SolrPerformanceProblems

というわけで、SSD 化するだけで絶大な効果がある。これだけでほとんどの問題が解決するはずだ。用途やマシンスペックにもよるが、メモリが 8GB や 16GB 程度の今どきのデスクトップマシンの上で、単体の Solr 単一のコアで数百 GB 程度のインデクスデータは扱えるでしょう。

SSD は高価であるが、僕の知見からすると、ディスクの遅さに振り回されてあれこれチューニングして四苦八苦するよりはさくっと SSD 化してしまうのが結果的に金銭面でも効率高いように感じます。人件費高いので。

既存の仕組みに振り回されず新規にハードウェアからシステムを作れるなら、ストレージは NVMe を用いた SSD にすべき。 2500MB/s という圧倒的すぎる速度のストレージが安価に使える時代がやってきている。下記でベンチをとった人の報告によるとなんと 70万 IOPS を捌けるとのこと。d.hatena.ne.jp

測定方法や製品にもよるが、HDD は数百 IOPS 程度。数十万IOPS が 10 万円で手に入ることを考えると、複数台の HDD でディスクアレイ組んで IOPS 稼ぐのがなんとも涙ぐましい努力に見えてきます。

しかもこの NVMe, 性能の割に安い。同程度の容量の SSD と比べても、値段に遜色は無く、モノによっては NVMe タイプのほうが安いことすらある。

容量の点が問題なければ NVMe を選択しない手は無く、ここ10年や20年我々が悩まされてきたストレージの遅さから開放される未来がすぐそこにやってきている。きっと来年あたりには AWS で超高速ストレージがストレージ系のラインナップに名を連ね、再来年頃には Macbook なんかに NVMe モデルが登場し、そんじょそこらのデータセンターを凌ぐ IOPS 性能を持つマシンがスタバで使われているのでしょう。

チューニングその2 : メモリを増やす

メモリを増やせばその分カーネルがキャッシュしてくれるので、IO が減る。よってメモリを増やすべし。

実メモリ量と Solr のヒープ量との割合は悩ましいところであるが、実はこの辺は割と適当で大丈夫。というのも、ヒープが多ければドキュメントキャッシュやクエリキャッシュが効きやすくなるもののページキャッシュが効きにくくなり、ヒープが少なければその逆になるので、どちらにとっても Solr にとって良い効果を与えるものなのであまり極端な差はでないだろうという目論見による。OutOfMemory が出たらヒープ量を増やす程度で問題なく運用出来ている。

チューニングその3 : Solr のバージョンアップをする

古い Solr を使っていたらバージョンアップすること。Solr は 4.1 からインデクスデータを圧縮して保存するようになっており、古い Solr と比べて 10〜30% 程度はデータ量の削減が期待できる。その他様々なチューニングが施されているので、なるべく最新の Solr を使うべし。

チューニングその4 : マージ時の IO を減らす

インデクスデータは複数ファイルに分かれており、定期的にマージ処理が発動する。
このマージする際、マージ対象のファイルが大きいとそれだけ巨大な IO が発生するので、デフォルトの 5GB から少し抑えて小さくしておこう。
僕が見てきた環境では、この値は 1024MB にしておけば、ファイル数が増えすぎることもなく、IO が多すぎて検索できなくなることも無い。

<mergePolicy class="org.apache.lucene.index.TieredMergePolicy">
  <double name="maxMergedSegmentMB">1024</double>
  <int name="maxMergeAtOnce">10</int>
  <int name="segmentsPerTier">10</int>
</mergePolicy>

チューニングその5 : インデクス作り直し

稀ではあるが、インデクスデータが不要に巨大化してしまう現象を観測した。我々が扱う環境では、場合によっては同じドキュメントでも 3 倍程度まで膨れ上がる現象があった。一度巨大化するとシュリンクする方法はインデクスデータの作り直しが以外に無い。インデクスデータを作りなおしている最中の検索機能の提供はどうするんだという問題はあるが、それを回避できる機構があるのならインデクスの作り直しはオススメである。

落とし穴

重要な落とし穴について。Solr 4.0 以降では、optimize を手動で発行してはいけません。というのも、前述したように同じドキュメントでインデクスサイズが膨れ上がった現象のうち1つのきっかけは optimize クエリであった。この時は optimize クエリ発行後、インデクスデータのサイズが 3 倍まで膨れ上がってしまった。

また、optimize 処理は solrconfig.xml の maxMergedSegmentMB, maxMergeAtOnce, segmentsPerTier 等のマージポリシーを無視してマージ処理を行い、デフォルトでは単一のインデクスファイルにマージしてしまうという罠がある。

Solr のコミッタである関口さんによると、optimize はあまり重要では無くなったとのことなので、手動で optimize を発行することは止め、Solr に任せるべきである。

まとめ

Solr は高速である。遅くなったら、第一にストレージの高速化。第二にストレージの高速化。第三にストレージの高速化。次にメモリの増強、その次に各種パラメーターやクエリのチューニング。という順序で進めましょう。

補足であるが、下記の本を読んで自作検索エンジンを作ったところ、Solr のコードリーディングにも大変役立っているので、オススメ。

検索エンジン自作入門 ~手を動かしながら見渡す検索の舞台裏

検索エンジン自作入門 ~手を動かしながら見渡す検索の舞台裏

定番の教科書。

[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン (Software Design plus)

[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン (Software Design plus)