CreateField Blog

オープンソースを使って個人でWebサービスを開発・運営していたブログ

GroongaとElasticsearchの転置索引の違いと更新反映速度について

はじめに

こちらの記事では、GroongaElasticsearchの単純な検索性能、更新性能、 ディスク使用効率を比較しました。

その結果では、Groongaの検索速度がElasticsearchよりも数倍ほど速く、Elasticsearchの更新速度がGroongaよりも数倍ほど速かったです。

なお、前回の記事では、Elasticsearchでフレーズ検索がされていなかったり*1、punctuation、whiespaceが転置索引に入っていなかったため、追加検証結果を追記しています。

シーケンシャルなスクリプトではGroongaの更新速度のほうが遅かったですが、これは、GroongaとElasticsearchが利用しているLuceneの転置索引の作成方法や管理方法の違いによるものです。

Groongaにおける転置索引

Groongaでは、即時更新に強く更新にかかる処理コストが低くリアルタイムサーチであるという売り文句があります。

これは、転置索引更新の際に間に挿し込めるスペースがあるため、このスペースを使いつぶすまでの間、低負荷、且つ、高速な更新が見込めるとのことです。 このスペースに転置索引を差し込むことにより転置索引の断片化を防ぐことができ、検索性能の劣化を防ぐことができるようです。

http://qiita.com/tamano/items/663c2a958e897226e138
http://groonga.org/ja/blog/2011/07/28/innodb-fts.html

Groongaでは、カラムの値の更新の前に転置索引の更新が行われ、更新リクエストは転置索引とカラムの値の更新が完了した段階でレスポンスが返ってきます。 そのため、更新リクエストのレスポンスが返ってくればすでに全文検索可能な状態となっています。

たとえば、groonga-column-holeプラグインを使えば、転置索引を作った後カラムに値がセットされる前に値を削除して、カラムに値を更新させないなんてこともできます。

GroongaはOS資源関連(ファイルオープン数やmmap周り)のカーネルパラメータ以外はほとんどチューニングはいりません。というかありません。

Elasticsearchにおける転置索引

Elasticsearchでは、Nearリアルタイムサーチをうたっており、通常は1秒で検索可能になるということが書かれています。

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/_basic_concepts.html

Elasticsearchでは、ドキュメントの更新と転置索引の更新(リフレッシュ、フラッシュ、マージ)が独立しており、更新リクエストは、ドキュメントの更新が完了したタイミングでレスポンスが返ってくるみたいです。

デフォルトのインデックスのリフレッシュインターバルは、1sに設定されています。 おそらく、このリフレッシュによりドキュメントから作成された転置索引がメモリ上に乗って、その時点で検索が可能になると思われます。

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html

リフレッシュの後、フラッシュという処理が走るようです。フラッシュっていうのはマージと違って、新しくセグメントを作ってインデックスすることみたいです。 この状態では、ディスク上の転置索引が断片化し検索性能の劣化が生じていると思います。 デフォルトのフラッシュは、5sに設定されています。

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules-translog.html#index-modules-translog

その後、ディスク上に小分けに断片化した転置索引を所定のポリシーでマージしてディスク上に再配置する処理が走るみたいです。

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules-merge.html

さらっと読んだだけではデフォルトはtieredというポリシーでこのポリシーはlog_bytes_sizeと似たようなものだということはわかりましたが、どういったタイミングでマージが走るかよくわかりませんでした。 Elasticsearchは、設定項目がめちゃくちゃたくさんあってチューニングが大変そうですね。

optimizeをするとマージが明示的に行われるようです。

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-optimize.html

10/1追記
以下の記事には、Kyoto Cabinetにおける転置インデックスのバッファリング戦略が記載されています。
http://fallabs.com/blog-ja/promenade.cgi?id=127

前回のシーケンシャルな更新スクリプトの実行時間はGroongaの方ではデータ+転置索引の更新時間であり、Elasticsearchの方はデータの更新時間のみでした。

Elasticsearchは、Nearリアルタイムサーチということですが、使ったことがないので実際にどの程度で検索できるようになるのかよくわかりません。

そこで、GroongaとElasticsearchでデータ更新をしつつ実際に検索ができるまでの時間と、リアルタイム更新後の検索性能の違いを計測してみました。

検証環境

項目 バージョン/種類
CPU Intel(R) Xeon(R) CPU E5620 @ 2.40GHz 1CPU 4Core
Memory 32GB
HDD 2TB(SATA 7200rpm) * 2 Hardware RAID 0
OS CentOS 6.4
Groonga 4.0.3
Elasticsearch 1.1.2

検証準備

こちらの記事で紹介したスキーマを用意し、WikipediaのXMLを投入する準備をします。

これで、GroongaとElasticsearchはほぼ同じBigramのルールで転置索引が作られます。

厳密に言うと少し違います。GroongaのBigramでは前方一致検索*2を使えば1文字でも検索できるように末尾は1文字でトークナイズされます。 ElasticsearchのBigramでは末尾も2文字でトークナイズされます。

GroongaとElasticsearchの更新の反映速度

検証手順

以下のスクリプトを実行して、更新開始から全文検索可能になる時間を計測します。

(1) WikipediaのXMLをパースし、1件ずつ更新します。titleカラムには、短いタイトルでもユニークにするためidとtitleを結合して格納します。
(2) 更新後、更新したidとtitleでタイトルに対して全文検索します。
(3) 全文検索がヒットするまで10msec間隔でループします。
(4) (1)-(3)を1000件ループします。

検証結果

項目 平均
Elasticsearch 1.01sec
Groonga 0.09sec

このように、Groongaでは0.09secで検索可能となったのに対し、Elasticsearchではリフレッシュの間隔である約1secで検索可能となります。 ここはチューニングが可能なパラメータのようなので、おそらくindex.refresh_intervalを短くすれば速くすることができると思います。 ただし、その分処理負荷が増えると思います。

追記
リフレッシュも明示的に実行することができるようです。1s未満で検索したいときはこれを使えば検索可能になるようです。@johtaniさんが教えてくれました。ありがとうございます。

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-refresh.html#indices-refresh

ElasticsearchのoptimizeとGroongaの静的索引構築

Elasticsearchでは、以下のようにoptimizeすると明示的に転置索引をマージさせることができます。

% curl -XPOST 'http://localhost:9200/wikipedia/_optimize'

Groongaには、静的索引構築というものがあります。

http://groonga.org/ja/docs/reference/indexing.html

これを使えば、リアルタイムに更新を検索結果に反映させることはできませんが高速に転置索引を構築することができます。静的索引構築では転置索引の隙間が埋められるため、ディスク使用効率が良くなります。なお、静的索引構築中のカラムに対しても転置索引を使わない検索、データ出力は可能です。ただし、静的索引構築中は負荷が高いです。

前回の追加検証にGroongaの静的索引構築の結果を追加すると以下のようになります。

更新時間

Elasticsearch Groonga
(リアルタイム)
(参考) Groonga
(静的索引構築)
8853sec(2h27min) 21914sec(6h05min) データ:4271sec(1h11min)
インデックス:1685sec(28min)
合計:5956sec(1h39min)

※トータルの更新時間には、WikipediaのXMLパースが含まれています。前回と同様ですがElasticsearchには転置索引の構築完了の時間が考慮されていません。Elasticsearchはパース済みのデータをバルク投入させれば速くなると思います。Groonga(静的索引構築)の方もバルク投入させればデータ部分は数分もかからないと思います。

ディスクサイズ

Elasticsearch
(optimize前)
Elasticsearch
(optimize後)
Groonga
(リアルタイム)
Groonga
(静的索引構築)
19.3GiB 16.5GiB 18.9GiB 15.4GiB

全文検索時間

種別 Elasticsearch
(optimize前)
Elasticsearch
(optimize後)
Groonga
(リアルタイム)
Groonga
(静的索引構築)
トータル
(1千件)
216.161sec 141.701sec 50.062sec 52.236sec
平均 0.216sec 0.141sec 0.050sec 0.052sec
最長 4.313sec 1.037sec 0.339sec 0.401sec
最短 0.000960sec 0.00317sec 0.00215sec 0.00182sec

Groongaでは、リアルタイム更新でも静的索引構築でも検索性能がほとんど変わりません。 Elasticsearchでは、optimizeによって検索性能がかなり変わります。

Groongaでは、転置索引の断片化が抑制されているためリアルタイム更新だけしていても検索性能にはほとんど影響がありません。

したがって、Groongaでは、通常使用時には原則、静的索引構築し直す必要が有りません。バックアップからの戻しでは、静的索引構築すれば高速にリストアが可能となっています。静的索引構築は、リアルタイム更新に比べて、転置索引の更新時間だけで言うと10倍近く速いです。

まとめ

Groongaは、カラムが更新されると即時、転置索引が更新されます。そのため、リアルタイムに更新を検索結果に反映させることができます。 また、転置索引の間に隙間が設けられているため、転置索引の断片化による検索性能の劣化がほとんどありません。

このように、Groongaは検索速度や即時更新性という点が優れています。

Groongaには、ほとんど設定項目はありませんが、ファセット(ドリルダウン)、サジェスト、スニペット等、最低限、全文検索に必要な機能は一通り揃っています*3

たとえば、よりリアルな状態を反映したいECサイトではGroongaの方が向いているのではないでしょうか。 ユーザがECサイトで在庫有のものを絞り込んで全文検索をし、クリックしてアクセスしたら実際は在庫切れだったということを防ぐことができます。 また、ニュースサイトなど情報の鮮度が重要な場合にも向いていそうです。

一方、Elasticsearchは、Nearリアルタイムサーチを実現するために、リフレッシュやフラッシュ、マージ処理といった転置索引の管理処理が裏で走っています。 このため、検索可能となる時間はリフレッシュ間隔に依存します。フラッシュの転置索引の断片化により検索性能が劣化するため、マージによる転置索引のディスク上の再配置が必要です。

しかし、Elasticsearchは、非常に多機能で多数の設定項目があります。また、Elasticsearchは、容易にシャーディング、レプリケーションさせることができます。

Elasticsearchは、TBクラス、PBクラスの解析が必要な大規模分散環境におけるログ解析や統計解析に向いていそうです。 分散環境での反映速度はわかりませんが、1sで検索可能なら単純な全文検索エンジンの用途としてもほとんどの環境で問題がなさそうです。

Javaの知識が豊富なのでJavaで作られたElasticsearchの方が親和性が高くカスタマイズし易そう、 Elasticsearchの方が多数機能、多数設定項目があるのでリッチな全文検索システムがつくりやすそう、 Elasticsearchの方が世界で使われているし仕事の需要がありそう、 Elasticsearchの方がオシャレ、JVMで運用するのは特に気にならない、 という理由でElasticsearchを選ぶのもありかもしれません。

この他、SolrAmazon CloudSearchTokyo Cabinet/Kyoto Cabinetを利用した全文検索システムなど、それぞれの特性や制限事項*4、コスト等を検討して用途や環境にフィットするものを使えばいいと思います。

私は単純な全文検索エンジンとして利用するのであれば、Groongaがおすすめです。

7/28追記
GroongaをKVSとして利用した場合のベンチマークについて記事を書きました。Groongaは単純なKVSとして利用しても非常に高速です。よければ、こちらもご参照ください。
GroongaとTokyoCabinetのHash表のベンチマークについて

おわりに

Groongaは、国産のオープンソースで開発者も日本人で、且つ、かなり親切なので貢献に対する敷居も低いです。
私は仕事でプログラムを書いたことがなく、Gitも使ったことがありませんでしたが、Groongaを使うことによりGitの使い方や開発手法などたくさんのことを学ぶことができました。

*1:たとえば、Elasticsearchでは空白が含まれていなくともダブルクォーテーションでくくらないと、「東京都」のBigramが東京 OR 京都で検索されます。

*2:文書全体ではなく転置索引のトークンに対する前方一致です。たとえば、「日本の首都は東京です」という文書に対してBigramにすると「す*」でヒットします。Oracle TextのNgramなどでも同様に末尾は1文字でトークナイズされるらしいです。

*3:アナライザーやフィルターのラインナップは、Elasticsearch(というかLucene)の方が豊富ですが、文字列処理ですしGroongaでもトークナイザやノーマライザをカスタマイズ可能となっているので多少工夫すればなんとかなります。

*4:現状のGroongaには、カラム256GiB制限やレコード2億6千万という制限があります。