CreateField Blog

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

全文検索エンジンGroongaユーザ勉強会@神戸を主催しました

2014/06/27(金)に全文検索エンジンGroongaユーザ勉強会@神戸を主催しました。

開催のきっかけ

草の根Groongaイベントのお誘いを受けて、関西圏でもGroongaのイベントがあるといいなと思い、神戸でも開催してみることにしました。

会議室の確保

人の集まり具合がどうなるかわからなかったので、三宮近辺でできるだけ費用が抑えられるというポイントで会場を探しました。WordBench神戸などが利用されているような貸会議室ビジネスしているところだと、平日夜間でも1万円を超えるところが多かったです。人が数十人来ることがほぼ確定しているプロダクトの勉強会なら千円徴収すればなんとかなりますが、小規模が想定される勉強会での利用は難しかったです。

そこで、公民館など市営の施設の会議室をこちらから探してみました。公民館等の場合、だいたい10数名規模の部屋で数千円といったところでした。

その他、インターネットで探して見ると、KIITOが1時間につき500円で時間単位の貸し出しOKで、且つ、比較的、予約も空いていたので利用することにしました。無料で備品も貸出しており、なかなか穴場なんじゃないかなぁと思いました。ただ、費用は安く抑えられましたが、駅からやや遠いのが難点でした。

告知

DoorkeeperやTwitter、メーリングリストなどで告知しました。また、近隣のRuby関西GDG神戸のAngularJSの勉強会などで簡単な開催のお知らせをしました。

当日の様子

19:00を超えると、入り口がclosedって札が立つのを知らなくて、入るのに戸惑った人がいて申し訳ありませんでした。

平日ということもあって、19:30ぐらいになるまでなかなか人が集まりませんでしたが、最終的には、当日キャンセルなしで合計10名が参加していただけました。ありがとうございました。

今回の勉強会では、Groongaの紹介と事例紹介を発表しました。

Groongaの紹介では、私が説明しながら、Groongaの開発に携わっている須藤(@ktou)さんが適宜、補足や質問に答える形で進めました。開発に携わっている方から直接、詳細な情報が聞けるいい機会になったんじゃないかなと思います。

事例紹介では、特許の全文検索サービスPatentFieldを紹介しました。当サービスでは、PHPからMroongaを使っていますが、Groongaの機能をすべて使いたかったので、全文検索はUDFを使ってGroongaのコマンドを使っています。基本的にGroongaのほぼすべての機能を使っているので、実際に使っている様子と工夫した点を紹介しました。

参加していただいた@shigeponさんが、ブログに勉強会の感想を書いてくれています。よければ、こちらもご参照ください。

懇親会

勉強会終了後、終電間近まで懇親会が行われました。懇親会では、Groonga関連の話もGroongaに関係ない話も盛り上がってた思います。@soundkitchenさんからdocker-mroongaについて紹介がありました。MroongaをDockerを使って簡単にイメージ作るにはこれしかない!と熱弁されていました。Dockerを使って、Mroongaを簡単にサーバにデプロイしたい方は是非使ってみるといいのではないでしょうか。

感想・反省点

進行がなかなかうまくいかず、発表を用意していただいた方もいらっしゃったのですが、時間を確保できなくて申し訳ありませんでした。

実際にコマンドをうちながら説明したいなぁと思ったのですが、現場では案外パッとでてこないので、事前に準備しておいたほうがよかったなぁとおもいました。

また、平日ということもあり、なかなか19時丁度に全員集まるのは難しかったので、今後、開催することがあれば、できるだけ休日に開催してみようかなと思っています。

勉強会の主催経験がなく拙い進行でしたが、参加いただいた皆様、および、わざわざ東京から来ていただいた須藤(@ktou)さん、どうもありがとうございました!

Groongaの今後への期待

当日できなかったGroongaの今後への期待の資料です。この資料は、メーリングリストで案内があった[groonga-dev,02439] Groongaの今後の開発への協力のお願いへの回答です。

61st Ruby/Rails勉強会@関西でLTしました

以下は、LTの資料です。

MacとRabbitの操作に戸惑ってしまい、デモの検証が一部しかできませんでした。デモができなかった分の検証結果を追記しています。

2014/6/27(金)19:00~神戸でGroongaの勉強会をします。Doorkeeperで募集していますので、興味がある方は是非ご参加ください。

http://koberoonga.doorkeeper.jp/events/11578

「Mroongaを使ったときの MySQLの制限との戦い」という内容で初LTしました

MySQL勉強会 in 大阪(第6回)でLTっていうのをはじめてやってみました。

内容は、「Mroongaを使ったときの MySQLの制限との戦い」です。

資料づくりにほとんど時間をかけられなくてすごい雑です。また、現場でちゃんとLTを見たこともなかったので、趣旨や作法があっているのかもよくわかりません。

うまくしゃべられなかった上に制限時間の5分を数十秒超えちゃってた気がします。

あまり人前での発表は得意ではないですが、無料で普段得られない経験値が得られるので大変お得だと思いました。

質問事項

LTの後、機材の調整の関係で少し時間があって、いくつか質問があったので覚えている範囲で書いておきます。

Q.ソースをいじってMroongaではいけるけど、その状態でInnoDBはどうなる?
A.ストレージエンジン側の制約をうけると思います。3072バイト制限はInnoDBでは3500バイトって書いてました。インデックス64個制限は、普段InnoDB使っていないのでわからないです。複数インデックス云々は、完全にGroongaのレイヤーで処理させる話なのでInnoDBには関係ないです。

Q.GroongaのコマンドはMySQLとは別にHTTPサーバたててる?
A.Groongaに直接コマンドを発行できるUDF(mroonga_command)があるのでそれを使っています。

Q.最終的にほぼGroongaでやるってことだけどMySQLを介す意味はある?
A.ないです。結局は、Groongaがはやいですよってことだけです。まぁでもやっぱりSQLで操作できるのはすごく楽で、GroongaでJSONをつくってloadさせるのは大変なんでINSERTやUPDATEはSQLでやっています。

国産の全文検索エンジンGroonga vs 世界的流行のElasticsearch

2014年4月21日は、第4回Elasticsearch勉強会ですね!

http://elasticsearch.doorkeeper.jp/events/8865

第4回Elasticsearch勉強会は、参加希望者が約200名の大反響なようです。

私は勉強会に参加できないので、C言語で書かれた国産の高速な全文検索エンジンGroongaと、Javaで書かれた世界的に勢いのあるElasticsearchについて性能の比較をしたいと思います。

  • 注意事項

今回の検証では1台あたりの馬力を比較するためにサーバ1台での全文検索性能について比較しています。

私は、Groonga(Mroonga)の利用暦が約2年であるのに対し、Elasticsearchの利用暦は2日です。このため、Elasticsearchに対するチューニングの不備や公平な比較になっていない点が含まれている可能性があります。

Elasticsearchでの言葉の扱いに慣れていないため、誤った語句を使っている可能性があります。

Groongaとは

GroongaはC言語で書かれた国産の高速な全文検索エンジンです。Groongaは、C言語から呼び出すことが可能な全文検索ライブラリとしての機能を有し、また、単体で全文検索サーバとしての機能も有します。  

Groongaでは、MySQLのストレージエンジンとしてSQLベースで容易に高速な全文検索が可能なMroongaが提供されています。GroongaやMroongaは、それ単体ではスケールしませんがfluentdのプラグインとして動作するDroongaSpiderストレージエンジンを使うことによりデータベースシャーディングが可能です。

Elasticsearchとは

Elasticsearchは、Javaで書かれた全文検索ライブラリApache Luceneを用いた全文検索、解析サーバです。Elasticsearchは、RESTfullで容易にアクセスすることができ、データを容易にスケールさせることができます。

また、Elasticsearchは、全文検索サーバSolrでも使われている歴史あるApache Luceneを使っていることもあり、非常に高度な全文検索機能、集計機能、豊富なプラグインを有し、多種多様な運用ツールとの連携の実績があります。

最近では、Elasticsearchとfluentdとkibanaを使ったログの可視化等が非常に流行っているようです。

検証データ

  • 日本語Wikipedia - Articles, templates, media/file descriptions, and primary meta-pages.

http://dumps.wikimedia.org/jawiki/20131119/ *1

XMLデータサイズ レコード数
7.3GiB 1,766,247

Wiki記法等は除去せずそのまま利用します。

7/19追加検証

http://dumps.wikimedia.org/jawiki/20140714/

検証環境

  • さくらのクラウド ×1台
CPU メモリ ディスク
4Core 16GB SSD 100GB

7/19追加検証

  • さくらの専用サーバ ×1台
CPU メモリ ディスク
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz 1CPU 4Core 32GB HDD 2TB

Elasticsearchの検証手順

環境構築

CentOSにElasticsearchをインストールする方法の手順に沿って、Elasticsearch1.1.1をインストールします。 ヒープサイズを8GiBに設定し、JVM起動時にメモリを確保するように設定しています。 また、デフォルトのシャード数を1に設定しています。

% vi /etc/init.d/elasticsearch
ES_HEAP_SIZE=8g
MAX_OPEN_FILES=65535
MAX_LOCKED_MEMORY=unlimited
% vi config/elasticsearch.yml
http.port: 9200
index.number_of_shards: 1
bootstrap.mlockall: true

ElasticSearchの運用とか (2)を参考にさせていただきました。ありがとうございます。

スキーマ

以下のような簡潔なテーブル構造を作成することを想定します。

id(integer) title(string) text(string)
150813 スポーツのプロリーグ一覧 '''スポーツのプロリーグ一覧'''(スポーツの...
150815 Category:スポーツ競技大会 {{Pathnav|主要カテゴリ|文化|娯楽|スポーツ|...

上記のテーブル構造に相当するスキーマを作成します。 アナライザーは、Groongaに合わせるためにngram_tokenizerでmin_grammax_gramを2にしています*2。 なお、Elasticsearchでは文字の正規化やタグ除去、ストップワード除去等フィルタがかなり豊富に用意されていますが、今回は単純な全文検索性能を比較するため、それらを使用しません。

% vi mapping.json
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ngram_analyzer": {
          "tokenizer": "ngram_tokenizer"
        }
      },
      "tokenizer": {
        "ngram_tokenizer": {
          "type": "nGram",
          "min_gram": "2",
          "max_gram": "2",
          "token_chars": [
            "letter",
            "digit",
            "punctuation",
            "whitespace",
            "symbol"
          ]
        }
      }
    }
  },
  "mappings": {
    "text": {
      "properties": {
        "id": {
          "type": "integer"
        },
        "title": {
          "type": "string",
          "analyzer": "ngram_analyzer"
        },
        "text": {
          "type": "string",
          "analyzer": "ngram_analyzer"
        }
      }
    }
  }
}

スキーマファイルをPOSTします。

curl -XPOST localhost:9200/wikipedia -d @mapping.json

これにより、wikipediaという名前のindexのスキーマが設定されました。なお、Elasticsearchは、スキーマレスでも動作します。

7/19追加検証
最初の検証では、punctuationとwhitespaceが入っておらず、Groongaと転置索引のトークンの量に違いがありました。追加検証では、Groongaと合わせるためにpunctuationとwhitespaceを有効にしています。

更新手順

更新は、たとえば、以下のようなPUTアクセスで行います。

% curl -XPUT 'http://localhost:9200/wikipedia/text/1' -d '
 { "id": "1", "title": "title1", "text": "text1" }
'

Gistに上げたPHPスクリプトを用いて、WikipediaのXMLデータを1件ずつPUTで更新します。

Elasticsearchでは、バルクアクセスのAPIや各スクリプト言語のライブラリを用いることもできるようですが、今回は単純に1件ずつCURLPUTするだけにしました。

検索手順

検索は、たとえば、以下のようなGETアクセスで行います。titleフィールドとtextフィールドに対してsearchqueryを全文検索し、一致するidを0件出力します。ここでは、全文検索性能のみを比較するため、出力数は0にしています。

curl -XGET http://localhost:9200/wikipedia/text/_search -d'
{
  "from" : 0, "size" : 0,
  "fields": ["id"],
  "query":
    {
      "multi_match" : {
        "query":    "\"searchquery\"",
        "fields": [ "title", "text" ]
      }
    }
}'

7/19追加検証
以前の検証結果では、"でくくっていなかったため、OR検索となっていました。追加検証では、"でくくってフレーズ検索としています。Elasticsearchでは、空白が含まれていなくとも、"でくくらないと、アナライザーによって分割されたトークンがOR検索されます。たとえば、「東京都」は、「東京」OR「京都」で検索されます。

Wikipediaのカテゴリのうち5文字以上の日本語のみのカテゴリからランダムに1万件を抽出して全文検索します。

Gistに上げたPHPスクリプトを用いて、1万件のカテゴリを1件ずつGETで全文検索します。更新と同様に、単純にCURLGETするだけです。

なお、Elasticsearchでの名前体系や扱い方に慣れておらず、不平をつぶやいていたら、@johtaniさんがフィールドの指定方法やカウントの見方を教えてくれました。ありがとうございました。Elasticsearchについて無知なにも関わらず、色々不平をつぶやいて大変失礼しました。

GroongaやDroongaでの名前体系に慣れていると、Elasticsearchの名前体系に慣れるのがなかなか大変でストレスを感じます。たぶん、Elasticsearchに慣れている方がGroongaやDroongaを触ろうとしてもそう感じるんじゃないかなと思います。

Groongaの検証手順

環境構築

Groonga4.0.1とnginxベースのgroonga-httpdをインストールします。

% rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
% yum makecache
% yum install -y groonga
% yum install -y groonga-tokenizer-mecab
% yum install -y groonga-normalizer-mysql
% yum install groonga-httpd

スキーマ

上記と同様のテーブル構造に相当するテーブル定義を作成します。 トークナイザーはTokenBigramを設定し、ノーマライザーは設定していません。 こうすると、Groongaでは、一般的なバイグラムのルールに従ってテキストがトークナイズされます。

% vi table.ddl
table_create text TABLE_PAT_KEY UInt32
column_create text text COLUMN_SCALAR LongText
column_create text title COLUMN_SCALAR LongText
table_create text-text TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram
column_create text-text index COLUMN_INDEX|WITH_SECTION|WITH_POSITION text title,text

Groongaのテーブルを作成します。

% groonga -n /var/lib/groonga/db/db < table.ddl
[[0,1397980056.85291,0.00288963317871094],true]
[[0,1397980056.85586,0.00232267379760742],true]
[[0,1397980056.8582,0.00235915184020996],true]
[[0,1397980056.86058,0.00229644775390625],true]
[[0,1397980056.86289,0.00667881965637207],true]

コマンドによってデータベースが新規作成された場合、groonga-httpdから操作できるようにファイルオーナーをgroonga:groongaに修正します。groonga-httpdが起動している場合、すでにgroonga:groongaでデータベースがつくられていると思います。

% chown groonga:groonga /var/lib/groonga/db/db*
% service groonga-httpd restart

これにより、textという名前のテーブルが作成されました。Groongaでは、MySQLなどと同様に明示的なテーブル定義を必要とします。

更新手順

更新は、たとえば、以下のようなPOSTアクセスで行います。

% curl -X POST 'http://localhost:10041/d/load?table=text' -d '
 [{"_key":1,"text":"text1","title":"title1"}]
'

Gistに上げたPHPスクリプトを用いて、WikipediaのXMLデータを1件ずつPOSTで更新します。

Groongaではデータのみを追加した後に全文インデックスを追加することにより最適化された静的インデックス構築ができますが、今回は単純にCURLで1件ずつPOSTするだけです。

なお、普段はGroongaをMySQLから利用できるMroongaを利用しており、GroongaのHTTPサーバに対してPOSTでうまく更新できないなぁとつぶやいていたら、@ktouさんがnginxベースのgroonga-httpdを使えばいいことを教えてくれました。ありがとうございました。

検索手順

検索は、たとえば、以下のようなGETアクセスで行います。titleカラムとtextカラムに対してsearchqueryを全文検索し0件出力します。ここでは、Elasticsearch同様、全文検索性能のみを比較するため、出力数は0にしています。また、Groongaでは、ヒット数が0件の場合、自動的に前方一致検索にエスカレーションするという機能があるためこれを抑制しています。

% curl 'http://localhost:10041/d/select?table=text&match_columns=title||text&query=searchquery&limit=0&match_escaltion_threshold=-1'

Wikipediaのカテゴリのうち5文字以上の日本語のみのカテゴリから上記と同じカテゴリ1万件を全文検索します。

Gistに上げたPHPスクリプトを用いて、1万件のカテゴリを1件ずつGETで全文検索します。更新と同様に、単純にCURLで1件ずつGETするだけです。

性能比較

更新時間

Elasticsearch Groonga
8853sec(2h27min) 21914sec(6h05min)

いつインデックスが更新されているかまでは追っていないので単純な比較はできないかもしれませんが、シーケンシャルなスクリプトの実行時間で比較すると、Groongaの方が2.5倍ほど遅かったです。

追記
Groongaの方はデータと転置索引の更新の両方が含まれた時間であり、Elasticsearchの方は転置索引の更新の時間は含まれていません。Elasticsearchは、リフレッシュ、フラッシュ、マージなどの転置索引の更新処理がデータの更新とは別に裏で走ります。

※トータルの更新時間には、XMLのパースが含まれています。

7/19追加検証

Elasticsearch Groonga
6689sec(1h51min) 20958sec(5h49min)

ディスク使用量

Elasticsearch Groonga
12.7GiB 18.9GiB

ディスク使用量は、Elasticsearchの方がコンパクトでGroongaの方が1.5倍ほど大きいですね。 Elasticsearchの方は設定をミスってしまって空白と所定の区切り文字が転置インデックスに入っていないですが、それを差し引いてもGroongaの方が大きいでしょう。

なお、Groongaの場合、インデックスのみを後で静的インデックス構築をすれば、ディスク使用効率が上がり検索性能がさらに向上します。なお、この方法はリアルタイム更新ではないのでここでは比較しません。

6/30追記
Groongaドキュメント読書会1で学んだ事のメモ - Qiitaを参考にすると、Groongaは、インデックスの即時更新のため、インデックス各データの間に隙間があり、データを間に差し込めるようになっているようです。このため、Elasticsearchに比べ、Groongaの方がディスク使用量が大きくなっているものと思われます。その分、インデックス更新にかかる処理コストが低いというメリットがあるようです。

http://groonga.org/ja/blog/2011/07/28/innodb-fts.html

7/19追加検証

Elasticsearch
(optimize前)
Groonga
19.3GiB 18.9GiB

punctuationとwhitespaceを有効にしたらElasticsearchの方が大きくなってしまいました。 おそらく、これはマージが終わっていないのでしょう。

以下のようにoptimizeをするとサイズが減少しました。

% curl -XPOST 'http://localhost:9200/wikipedia/_optimize'
Elasticsearch
(optimize後)
Groonga
16.5GiB 18.9GiB

検索時間

種別 Elasticsearch Groonga
トータル(1万件) 1050.737sec 424.797sec
平均 0.105sec 0.042sec
最長 0.792sec 0.368sec
最短 0.000992sec 0.000842sec

上記を比較すると、全文検索速度はGroongaの方が約2.5倍ほど速いことが判ります。 経験則から言うと、Groongaはこの数倍~10倍ぐらいのデータサイズであっても1台で十分に高速に全文検索できると思います*3

ただし、Groongaは、単体ではシャーディングすることができません。一方、Elasticsearchは、デフォルトのシャード数が5であることからもわかるように、複数台構成が前提となっており容易にシャーディングすることができます。

また、今回は単一アクセスの検索性能しか試していません。同時実行性能を含めるとどちらが勝っているかは判りません。

7/19追加検証
フレーズ検索でもGroongaの方がElasticsearch(optimize後)よりも約2.8倍ほど速いことが判ります。Elasticsearch(optimize前)であれば約4.3倍ほど速いことが判ります。ちなみにGroongaでは、リアルタイム更新をしても速度劣化はほとんどないため、optimizeという処理はありません。詳しくはこちら

種別 Elasticsearch
(optimize前)
Elasticsearch
(optimize後)
Groonga
トータル
(1千件)
216.161sec 141.701sec 50.062sec
平均 0.216sec 0.141sec 0.050sec
最長 4.313sec 1.037sec 0.339sec
最短 0.000960sec 0.00317sec 0.00215sec

おわりに

以上、サーバ1台、単一アクセスにおけるGroongaとElasticsearchの性能を比較しました。 まとめると以下のような比率になります。数値が高い方が性能が良いことを示しています。

性能種別 Elasticsearch Groonga
更新性能 2.5 1
ディスク使用効率 1.5 1
検索性能 1 2.5

7/19追加検証

性能種別 Elasticsearch
(optimize前)
Groonga
更新性能 3.13 1
ディスク使用効率 1 1.02
検索性能 1 4.31


性能種別 Elasticsearch
(optimize後)
Groonga
更新性能 3.13 1
ディスク使用効率 1.14 1
検索性能 1 2.83

上記の比較例では、更新性能、ディスク使用効率ではElasticsearchが勝っており、検索性能ではGroongaが勝っていました。なお、あくまで今回のテストケースによる結果であり、他のテストケースではどうかわかりません。

Elasticsearchは、今、世界的に勢いがあり、容易なスケール機能、豊富な機能、豊富なプラグイン、運用ツールとの連携等、様々なメリットがあります。 アナライザーやフィルターのラインナップは、歴史が長いApache Luceneのライブラリを使っていることもあり、現状、GroongaよりもElasticsearchの方が優れているでしょう。 MoreLikeThisや類似画像検索は、現在のGroongaにはない機能です。

現状、大規模なWebサービス等でサーバを何十台、何百台とたくさん並べて運用する場合は、Elasticsearchの方が利用しやすいかもしれません。

しかし、私は、サーバ1台あたりの全文検索自体の馬力ではGroongaの方が勝っていると考えています。また、弱点だったスケール機能は、2014年2月にDroongaがメジャーリリースされ解消されつつあります。 さらに、Groongaは、MySQLのストレージエンジンであるMroongaを利用することによりSQLベースで非常に容易に高速な全文検索をすることもできます。私はMroongaがあったので、データベースも全文検索の知識もまったくなかった状態からある程度Groongaが扱えるようになりました。

kuromojiトークナイザの検索モードやフィルタ機能程度の利点であれば、多少工夫すればGroongaやMroongaでも補えます。今、Mroongaを使っていて、高度な検索式や豊富な全文検索機能を追加したいと考えている方は、まずは、GroongaやDroongaを検討してもいいと思います。Mroongaをストレージモードで使っているならデータベースそのままでGroongaのコマンドが利用できますし、MySQLの制約からくるインデックスが使用できないことによる速度劣化であれば、Groongaのコマンドに置き換えるだけで10倍以上の検索速度になったりすると思います。詳しくはこちらを参照してください。

上記の比較はあくまで一例にすぎませんが、そこそこ大規模なデータベースに対して少ないサーバ台数で高速な全文検索を実現したいならば、私はGroongaがおすすめだと思っています。

私はGroonga(Mroonga)を使うことにより、データサイズが400GiB超のデータベースを専用サーバ1台でそこそこ実用的な全文検索速度を実現させることができました*4

データベースの規模や特性に応じて、ElasticsearchとGroongaを使い分けるのも良いかもしれません。

最近、Mroongaを使って全文検索Webサービスを作ったときにはまったことについて、いくつかブログ記事を書きました。GroongaやMroongaに興味があれば、こちらも参考になるかもしれません。

Mroongaを使って全文検索Webサービスを作ったときにはまったこと(第1回)
Mroongaのラッパーモードからストレージモードに変えた理由
数百GiBの全文検索用データベースをMroongaのストレージモードにしてはまったこと
Groongaがあまり得意でない類似文書検索に連想検索エンジンGETAssocを使った話

今後は、Groonga(Mroonga)を使って工夫した点等を書いていきたいと思っています。

7/22追記
GroongaとElasticsearchの転置索引の違いと更新反映速度の差、およびGroongaの静的索引構築とリアルタイム更新時の性能差について検証した記事を追加しました。よければ、こちらもご参照ください。
GroongaとElasticsearchにおける転置索引の違いと更新反映速度について

7/28追記
GroongaをKVSとして利用した場合のベンチマークについて記事を書きました。Groongaは単純なKVSとしてもかなり高速な性能を有しています。
GroongaとTokyoCabinetのHash表のベンチマークについて

9/9追記
ちなみに、Groongaの場合、トークナイザーをTrigramにし、さらに効率化した自作トークナイザ―を使うと検索速度が8倍ほど速くなります(平均 0.0063 sec)。

https://github.com/naoa/groonga-tokenizer-yangram

Groongaの自作トークナイザーの紹介 - Qiita

2015/3/9追記
最新のGroongaで以下のパッチを適用すると、さらに検索速度が1.5倍〜2倍ほど速くなります。

頻出トークンとレアトークンを一緒に検索したときの性能向上パッチ (groonga-dev,03095) - Groonga - fulltext search engine. (グルンガ) - SourceForge.JP

*1:データがやや古いのは、以前、Mroongaの性能をいろいろ検証したときと同じものを使ったためです。

*2:厳密にGroongaに合わせるためには、token_charsにwhitespaceとpunctuationも指定すべきでした。

*3:インデックスが適正に使われていることが前提です。Mroongaの場合、MySQLによる制約でインデックスが利かないケースが多々あります。

*4:アクセスが増えると参照分散させるために台数を増やす必要があると思います。実用的な速度かどうかは、アプリの要求次第によって変わると思います。デフォルトのトークナイザTokenBigramではこのサイズはさばけないと思います。自前でトークナイザを改修したり工夫しています。

ブログタイトルを変更しました。

旧タイトル:独学Webサービス開発ブログ
新タイトル:CreateField Blog

インターネット上には、たくさんの技術情報やソフトウェアを無償で提供されている方々がたくさんいます。
人から教えを請うていて、独学っていうのは独りよがりな感じがしたので辞めました。

数百GiBの全文検索用データベースをMroongaのストレージモードにしてはまったこと

前回は、全文検索Webサービスを作ったときにはまったことの第2回として、 Mroongaのラッパーモードからストレージモードに変えた理由という記事を書きました。

今回は、Mroongaのストレージモードにしたことによってはまったことについて書きたいと思います。

Spiderストレージエンジンを使ったMroongaの分散構成の検討

当初、データベースを複数に分割し、Spiderストレージエンジンを使ってデータベースシャーディングする予定でした。

Spiderストレージエンジンは、ストレージエンジンの種類を問わずデータベースを水平分散させることができるMySQL/Mariadbのストレージエンジンです。 Spiderストレージエンジンは、国産で個人の方が開発されたストレージエンジンでMariaDB10.0系にすでにバンドルされています。 Spiderストレージエンジンの開発者の方は、全文検索Mroongaストレージエンジンの開発にも携わられています。

Spiderストレージエンジンは、非常に簡単にデータベースシャーディングすることができ、既存のSQLをそのまま使うことができます。

前回説明したように、ドリルダウン検索*1が非常に便利でWebアプリの機能として組み込みたいと考え、Mroongaをラッパーモードからストレージモードに変更し、全文検索はGroongaのコマンドを使うことにしました。

しかし、SpiderストレージエンジンはSQLベースで分散するため、Groongaのデータベースに直接コマンドを発行することはできません。 当時のGroongaには、分散機能がありませんでした*2

そこで、約400GiBのデータベースを分散させずに良好なパフォーマンスが得られるかという無謀な挑戦がはじまります。

データベースの統合によるカラムサイズ制限

Groongaには、いくつかの制限事項が設けられています。

当時のドキュメントには記載がありませんでしたが、Groongaには1カラムに格納可能なデータサイズの上限値が設けられており上限値は256GiBです。

したがって、約400GiBのデータベースを1カラムに格納することができませんでした。

そこで、苦肉の策として1カラムを3カラムに分割させます。 テーブルには、年度を示すカラムがあったため、1999年以前、2000年~2009年、2010年以降の3つのカラムを作成し、 年度に応じてアプリケーション側で切り替えるようにしました*3

なお、次期GroongaであるGrnxxでは、上記の制限が撤廃できるように設計が検討されているようです。

https://docs.google.com/presentation/d/1R5YqedpDyI9NVNn6f_EkCBZ8miQAldrAxu5-AwjmPas/edit#slide=id.p

データベースの統合によるインデックス構築失敗再び

上記のようにカラムを分割することにより、カラムの上限による制限は回避することができました。

しかしながら、今度はまたインデックス構築に失敗するようになります。

第1回に書いたインデックス構築の失敗とは、また別の原因によるものです。

この事象は、データベースサイズが100GiBぐらいにならないと再現せず、簡単な再現セットを作るのが困難でした。

メーリングリストで相談すると、デバッグ手法を一から教えていただいたり、バックトレースログから推測されるバグの修正パッチなどきめ細やかな対応をしていただきました。

http://sourceforge.jp/projects/groonga/lists/archive/dev/2013-August/001718.html
http://sourceforge.jp/projects/groonga/lists/archive/dev/2013-August/001725.html

しかし、再現に半日かかることもありバックトレースログから原因をなかなか突き止めることができませんでした。

そうしていると、わざわざテストマシンを用意していただけることとなり、当方の100GiB以上の再現可能なデータベースを直接提供して解析していただけることになりました。

http://sourceforge.jp/projects/groonga/lists/archive/dev/2013-September/001739.html

これによりオーバーフローしている箇所を突き止め、バグを修正していただけました。こうして、数百GiBのデータベースでも正常にインデックス構築ができるようになりました。

http://sourceforge.jp/projects/groonga/lists/archive/dev/2013-October/001866.html

以上のようなきめ細やかな対応を全て無償で行っていただきました。

個別環境による検証等は、有償のサポートサービスを契約すべきなのかもしれません。無償にも関わらず、ここまできめ細やかなご対応どうもありがとうございました。

おわりに

以上のようにして、Mroongaのストレージモードで400GiB超のデータベースのインデックス構築ができるようになりました。

しかし、この後の検証によりデフォルトのトークナイザのTokenBigramでは、全文検索のパフォーマンスがあまり芳しくないことが判明します。サイズがでかすぎるので当然といえば当然です。

そこで、400GiB超のデータベースの全文検索のパフォーマンスをできるだけ改善できないかを試行錯誤することになります。

次回は、全文検索のパフォーマンスをできるだけ良くするために試行錯誤したことについて書こうと思っています。投稿まで少し時間が空くと思います。

6/26 記事を追加しました。
Groongaがあまり得意でない類似文書検索に連想検索エンジンGETAssocを使った話

2014-11-29(土)13:30 - 17:30
年に1度のGroongaに関するイベントがあります。Groongaを使っている人、興味がある人は参加してみてはいかがでしょうか。

全文検索エンジンGroongaを囲む夕べ5 - Groonga | Doorkeeper

*1:他の全文検索エンジンやDroongaではファセット検索と呼ばれています。

*2:2014年2月には、Droongaと呼ばれるGroongaの分散システムがメジャーリリースされています。

*3:非常に不格好なのでお勧めしません。

Mroongaのラッパーモードからストレージモードに変えた理由

前回は、全文検索Webサービスを作ったときにはまったことの第1回という記事を書きました。

今回は、Mroongaを使って全文検索Webサービスを作ったときにはまったことの第2回として、ラッパーモードからストレージモードに変えた理由について書きたいと思います。

なお、かなり長く、MySQL、Groongaについて前提知識がないと理解できない部分が多々含まれている可能性があります。

ラッパーモードとは

全文検索Mroongaストレージエンジンでは、全文検索するためにラッパーモードとストレージモードの2つのモードが用意されています。

(引用) ラッパーモードでは全文検索機能のみGroongaの機能を利用し、データストアはInnoDBなど既存のストレージエンジンを利用します。ラッパーモードを利用することにより、ストレージエンジンとして多くの利用実績のあるInnoDBに全文検索エンジンとして実績のあるMroongaを組み合わせて、高速な全文検索機能付きの信頼性のあるデータベースとして利用できるという特長があります。

ラッパーモードを使うことにより、たとえば、トランザクション対応のInnoDBを使うことができます。全文検索も高速にできてトランザクションも使えるとなるといいことづくめのように見えます。

ラッパーモードとストレージモードの違い

ドキュメントを比較すると、ラッパーモードは、ストレージモードに比べて、位置情報検索と、レコードIDの取得と、Groongaコマンドの実行と、カラムの刈り込みと、行カウント高速化がないぐらいです。

また、私が使いはじめたときは、割とよくmysqldがクラッシュしてデータベースが破損しやすかったこともあり、ストレージモードではデータファイルがたくさんできて取り扱いにくかったのでMyISAMのラッパーモードでデータベースを作りはじめました。

しかしながら、ストレージモードでは、以下の点が優れていることに気づきラッパーモードからストレージモードに変更しました。

ストレージモードの利点その1 所定の条件の場合に全文インデックス以外の複数インデックスを使用した高速な絞込みができる

ストレージモードでは、所定の条件の場合、ORDER BY LIMITで全文インデックス以外のインデックスを複数使用して高速に絞込み操作を行うことができます。

なお、この所定の条件は、条件の数だけソースを追加しなければいけないというデメリットがあります。 以前、ユーザが要望したこの条件わずか一日で追加されて、検索速度が10倍改善されたというエピソードがあります。 Groonga開発チームのこの対応速度は素晴らしいですね。Groonga開発チームのこの対応の良さがMroongaを使う最も大きな利点かもしれません。

ちなみにこの所定の条件に関わらず複数インデックスを使うちょっとした裏技みたいな方法があります。 ただし、これは今のところソースを変更する必要があるというのと、AGAINST句の中でGroongaのクエリ構文を使う必要があります。

ここに試した結果と方法を記載しています。インデックスが使われない場合は、数秒かかっていた検索クエリが0.数秒になっています。

この方法もGroonga開発チームにメーリングリストで教えてもらいました。

ストレージモードの利点その2 インデックスが使用されないときのレコード操作が高速

インデックスが使われないときのカウントやレコード操作が他のストレージエンジンに比べてストレージモードは段違いに速いです。

具体的にInnoDBのラッパーモードとストレージモードで比較してみましょう。

データベースのデータサイズは20GiB強、レコード数は数十万ぐらいです。

まず、以下のように全文インデックスのみでの絞込みが7万8千件ぐらいの場合を比較します*1

mysql> SELECT COUNT(*) FROM ftext WHERE MATCH(title,abstract,description) AGAINST("+画像" in boolean mode);
+----------+
| COUNT(*) |
+----------+
|    78467 |
+----------+
1 row in set (0.04 sec)
  • InnoDBラッパーモード (一時結果が7万8千の場合)
mysql>  SELECT COUNT(*) FROM ftext WHERE MATCH(title,abstract,description) AGAINST("+画像" in boolean mode) AND date LIKE '2010%';
+----------+
| COUNT(*) |
+----------+
|    69644 |
+----------+
1 row in set, 1 warning (19.55 sec)
  • ストレージモード (一時結果が7万8千の場合)
mysql> SELECT COUNT(*) FROM ftext WHERE MATCH(title,abstract,description) AGAINST("+画像" in boolean mode) AND date LIKE '2010%';
+----------+
| COUNT(*) |
+----------+
|    69644 |
+----------+
1 row in set, 1 warning (2.24 sec)

InnoDBラッパーモードに比べると、ストレージモードの方がだいぶ速いですね。 もう少し全文インデックスでの絞り込み件数を少なくし、2万件ぐらいの場合を比較してみます。

mysql> SELECT COUNT(*) FROM ftext WHERE MATCH(title,abstract,description) AGAINST("+画像処理" in boolean mode);
+----------+
| COUNT(*) |
+----------+
|    20742 |
+----------+
1 row in set (0.19 sec)
  • InnoDBラッパーモード (一時結果が2万の場合)
mysql>  SELECT COUNT(*) FROM ftext WHERE MATCH(title,abstract,description) AGAINST("+画像処理" in boolean mode) AND date LIKE '2010%';
+----------+
| COUNT(*) |
+----------+
|    19448 |
+----------+
1 row in set, 1 warning (4.09 sec)
  • ストレージモード (一時結果が2万の場合)
mysql> SELECT COUNT(*) FROM ftext WHERE MATCH(title,abstract,description) AGAINST("+画像処理" in boolean mode) AND date LIKE '2010%';
+----------+
| COUNT(*) |
+----------+
|    19448 |
+----------+
1 row in set, 1 warning (0.68 sec)

このように、ストレージモードはインデックスが使われなくともInnoDBラッパーモードに比べてかなり速いレコード操作を行うことができます。 全文インデックスを使った一時絞込み結果が数万件ぐらいでは、十分実用レベル*2であることがわかります。

さらに、InnoDBのラッパーモードでは、オフラインでのインデックス構築に非常に時間がかかります。 たとえば、数十GiBのテーブルでALTER TABLE ftext ENABLE KEYS;すると、ストレージモードの場合は26分に対し、InnoDBのラッパーモードだと3時間21分かかりました

4/17 追記
少しだけソースを読みました。ストレージモードの場合、ha_mroonga::storage_create_indexgrn_obj_set_infogrn_ii_buildなのに対し、ラッパーモードの場合、ha_mroonga::wrapper_fill_indexesgrn_column_index_updategrn_ii_column_updategrn_ii_update_oneとなっていました。ラッパーモードの場合このアップデートがたくさん走るからストレージモードに比べて遅いような気がしました。

ストレージモードの利点その3 Groongaコマンドが使える

(引用) ストレージモードでは、全文検索機能だけではなくデータストアも含めてGroongaの機能を利用します。ストレージエンジンのすべての機能をGroongaで実現するため、Groongaが得意としている集計操作が高速です。また、Groongaコマンドで直接データベースを操作できるという特長もあります。

私にとっては、ストレージモードにするとGroongaコマンドで直接操作できるという点が非常に大きかったです。Groongaでは、ドリルダウンという高速な集計操作を行うことができます。

Groongaでは、以下のようにドリルダウン検索することで、全文検索結果に含まれる他のカラムの件数を簡単、且つ、割と*3高速に取得することができます。

mysql> SELECT mroonga_command('select ftext --match_columns title||abstract||description --query データベース --output_columns id --limit 0 --drilldown applicants --drilldown_sortby -_nsubrecs --drilldown_limit 10') as result;
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| result                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| [[[17701],[["id","ShortText"]]],[[4330],[["_key","ShortText"],["_nsubrecs","Int32"]],  
["F株式会社",495],  
["N株式会社",478],  
["株式会社H",459],  
["M株式会社",330],  
["株式会社T",323],  
["NT株式会社",319],  
["S株式会社",314],  
["C株式会社",308],  
["P株式会社",303],  
["Q株式会社",274]]] |  
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.45 sec)

SQLで同じことをしようとするとかなり大変なクエリになると思います。 なお、同じことをするSQLをぱっと思いつかなかったので比較していません。

ストレージモードの利点その4 ベクターカラムが使える

Groongaでは、ベクターカラムという1カラムに複数の値を格納できる仕組みがあります。このベクターカラムは、Mroongaでも作ることができます。すなわち、1対nの関係を別テーブルにせずに1テーブルで表現することができます。これによりJOINやGROUP BYが不要になります。

たとえば、MySQLでは、以下のようなテーブル定義があったとします。 1つのIDに対して、複数の値がぶら下がる形です。

  • ベクターカラムなしの場合のテーブル定義
CREATE TABLE `ftext` (
  `id` varchar(20) NOT NULL,
  `date` date NOT NULL,
  `title` text NOT NULL,
  `abstract` text NOT NULL,
  `description` longtext NOT NULL,
  PRIMARY KEY (`id`),
  KEY `date` (`date`),
  FULLTEXT INDEX `ftext` (`title`,`abstract`,`description`)
) ENGINE=mroonga DEFAULT CHARSET=utf8;

CREATE TABLE applicants (
  `id` varchar(20) NOT NULL,
  `name` varchar(80),
  PRIMARY KEY (`id`,`name`)
) ENGINE=mroonga DEFAULT CHARSET=utf8;

f:id:naoa_y:20140413160705p:plain

上記のテーブル定義をベクターカラムを使うと以下のように表現することができます。

  • ベクターカラムありの場合のテーブル定義
CREATE TABLE applicants (
  `name` varchar(80) PRIMARY KEY
) ENGINE=mroonga DEFAULT CHARSET=utf8
  COLLATE=utf8_bin
  COMMENT='default_tokenizer "TokenDelimit"';

CREATE TABLE `ftext` (
  `id` varchar(20) NOT NULL,
  `date` date NOT NULL,
  `title` text NOT NULL,
  `abstract` text NOT NULL,
  `description` longtext NOT NULL,
  `applicants` TEXT NOT NULL COMMENT 'flags "COLUMN_VECTOR", type "applicants"',
  PRIMARY KEY (`id`),
  KEY `date` (`date`),
  FULLTEXT INDEX `ftext` (`title`,`abstract`,`description`),
  FULLTEXT INDEX applicants (applicants) COMMENT 'table "applicants"'
) ENGINE=mroonga DEFAULT CHARSET=utf8;

ベクターカラムの場合、半角スペースで区切られた値を渡すことでカラムを更新することができます*4

  • 更新例
mysql > INSERT INTO ftext VALUES("1","2001-01-01","title","abstract","description","name1 name2 name3");

title,abstract,descriptionカラムのいずれかに"装置"が含まれ、且つ、applicantsに"S株式会社"が含まれる件数を集計してみましょう。 上記同様、データベースのデータサイズは20GiB強、レコード数は数十万ぐらいです。

"装置"だけの一時検索結果数は23万7435件です。

mysql> SELECT COUNT(*) FROM ftext WHERE MATCH(title,abstract,description) AGAINST("+装置" in boolean mode) ;
+----------+
| COUNT(*) |
+----------+
|   237435 |
+----------+
1 row in set (0.10 sec)
  • ベクターカラムなしの場合のSQLでのSELECT
mysql> SELECT COUNT(*) FROM ftext
    -> INNER JOIN applicants ON applicants.id = ftext.id
    -> WHERE MATCH(title,abstract,description) AGAINST("+装置" in boolean mode)
    -> AND applicants.name = 'S株式会社';
+----------+
| COUNT(*) |
+----------+
|     3259 |
+----------+
1 row in set (24.81 sec)

ベクターカラムなしのSQLの場合、JOINをする必要があり検索速度が遅いですね。 また、applicants.nameが1IDで複数ヒットする条件ならば、GROUP BYをする必要もありそうです。

  • ベクターカラムありの場合のSQLでのSELECT
mysql> SELECT COUNT(*) FROM ftext
    -> WHERE MATCH(title,abstract,description) AGAINST("+装置" in boolean mode)
    -> AND MATCH(applicants) AGAINST("+S株式会社" in boolean mode);
+----------+
| COUNT(*) |
+----------+
|     3259 |
+----------+
1 row in set (29.49 sec)

ベクターカラムに対しては、MATCH ... AGAINSTで検索しないといけません。これでは、後ろ側のインデックスが使われず遅いですね。

  • ベクターカラムありの場合のSQLでのSELECT(AGAINST句でGroongaのクエリ構文使用) 上記の複数インデックスが使えるちょっとした裏技を使ってみます。
mysql> SELECT COUNT(*) FROM ftext
    -> WHERE MATCH(title,abstract,description) AGAINST("+装置 +applicants:@S株式会社" in boolean mode);
+----------+
| COUNT(*) |
+----------+
|     3259 |
+----------+
1 row in set (0.08 sec)

この方法ならSQLでも複数インデックスが使えて速いですね。 ただし、ソースをいじる必要があり、Groongaのクエリ構文が含まれるというイレギュラーな形になってしまいます。

  • ベクターカラムありの場合のGroongaのselectコマンド
mysql> SELECT mroonga_command("select ftext --match_columns title||abstract||description --query '装置 +applicants:@S株式会社' --output_columns id --limit 0") as result;
+---------------------------------+
| result                          |
+---------------------------------+
| [[[3259],[["id","ShortText"]]]] |
+---------------------------------+
1 row in set (0.29 sec)

Groongaのselectコマンドでは、結果がJSONで返ってきます。複数インデックスが使われていて高速に結果が取得されていますね。

MySQLのクエリチューニングは非常に奥が(闇が)深いのでSQLの方は、もっと速くする方法があるのかもしれません。 Groongaのselectコマンドでは、さほどチューニングが不要ということも利点かもしれません。

このように、ストレージモードは速度的なメリットとGroongaコマンドが使えるというメリットがあります。

ここで、MroongaストレージモードのSQLのSELECT構文とGroongaのselectコマンドを比較するとさらに以下の利点があります。

Groongaの利点その1 自由に複数インデックスを使って高速な絞込みができる

Groongaのselectコマンドでは、上記の所定の条件に縛られずに複数インデックスを使うことができます。 したがって、多数の絞込み条件を自由に追加して、簡単に高速に全文検索することができます。

Groongaの利点その2 カウントが検索クエリの結果と同時に取得できる

Groongaでは、上記のように結果セットがJSONとなるので、検索結果とカウントを同時に取得することができます。なお、これは、既存のアプリをそのまま適用できないというデメリットともとらえることができます。

Groongaの利点その3 オフセットがストレージモードよりもかなり速い

MySQLを使っている方だとわかると思うのですが、MySQLのオフセットは数が大きくなると検索速度が顕著に劣化します。 このため、MySQLではBETWEENで絞り込んだりすると思います。

Groongaのselectコマンドでは、オフセット処理がMroongaストレージモードのSELECT構文よりもかなり速いです。 約1000万件のオフセット速度を比較してみます。

mysql> SELECT COUNT(*) FROM ftext;
+----------+
| COUNT(*) |
+----------+
| 11549665 |
+----------+
1 row in set (0.00 sec)
  • MroongaストレージモードのSELECT構文*5
mysql> SELECT app_id FROM ftext LIMIT 5 OFFSET 11549660;
+---------------+
| app_id        |
+---------------+
| JP20130513403 |
| JP20130513406 |
| JP20130513423 |
| JP20130513425 |
| JP20130513426 |
+---------------+
5 rows in set (8.09 sec)
  • Groongaのselectコマンド
mysql> SELECT mroonga_command("select ftext --output_columns app_id --limit 5 --offset 11549660") as result;
+-----------------------------------------------------------------------------------------------------------------------------------+
| result                                                                                                                            |
+-----------------------------------------------------------------------------------------------------------------------------------+
| [[[11549665],[["app_id","ShortText"]],  
["JP20130513403"],  
["JP20130513406"],  
["JP20130513423"],  
["JP20130513425"],  
["JP20130513426"]]] |  
+-----------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.25 sec)

Groongaのselectコマンドでは、約1000万件の末尾でも0.数秒で取得できますね。これなら、わざわざBETWEENで絞り込む必要はありません。

Groongaの利点その4 カウントがストレージモードよりもわずかに速い

これは、かなり多数のレコードにならないと差がでてきませんが、検索結果が数百万レコードぐらいになると1秒ぐらいの差がでてきます。 ただ、数百万レコードで1秒ぐらいなのであまり影響はない範囲だと思います。

Qiitaに試した結果があります。

おわりに

かなり長くなってしまいました。あまりまとまっておらず、判りづらかったと思います。

種類 利点
ストレージモード 所定の条件の場合に全文インデックス以外の複数インデックスを使用した高速な絞込みができる (ソースをいじってAGAINST句にGroongaのクエリ構文を使うと自由に複数インデックスが使える)
ストレージモード インデックスが使用されないときのレコード操作が他のストレージエンジンに比べて高速 全文インデックスでの一時絞込み結果が数万件ぐらいであれば実用レベル
ストレージモード Groongaコマンドが使える ドリルダウンが使える
ストレージモード ベクターカラムが使える ベクターカラムを使うとJOINやGROUP BYが不要
Groonga 自由に複数インデックスを使って高速な絞込みができる
Groonga 結果がJSONなのでカウントが検索クエリの結果と同時に取得できる
Groonga オフセットがストレージモードよりもかなり速い 1000万件の末尾でも0.数秒
Groonga カウントがストレージモードよりもわずかに速い 数百万件で1秒

以上のような利点から私はMroongaをストレージモードにし、更新やメンテナンスでの簡単な検索はMroongaのSQLで楽にデータ操作しつつ、全文検索はGroongaのselectコマンドというスタイルが最もフィットしました。

最初は、Groongaは敷居が高く入りづらかったですが、Mroongaをストレージモードで使っているうちにGroongaについても慣れてきました。Groongaは、いろいろいじれて面白いです。

私は個人で開発していたのでGroongaのコマンドを使うのは特に問題がなかったですが、Groongaコマンドまでいってしまうと完全にSQLを逸脱してしまうので、 保守性や属人性を考えると、一般的なシステム構築現場では、採用できない・提案できないことがあるかもしれません。 また、Groongaコマンドは、結果セットがJSONになるので既存のアプリをそのまま使うこともできません。

それでも私は、単純なカラムを全文検索するという場合を除き*6、がっつり全文検索したいならば、ストレージモードをおすすめします。 堅牢性と高速性を両方狙ってもあまりいいことはありません。この2つはベクトルの向きが違います。Mroongaは、正直割と壊れるときは壊れます*7。 全文検索用のデータベースは、壊れても泣かないぐらいの心づもりでいると良いかもしれません。 私は、全文検索用のデータベースの他にデータ保持用のデータベースを作るか他のストレージエンジンとレプリケーションをしたほうがいいと思っています。

次回は、ストレージモードにしたことによってはまったことや改良点について書く予定です。

4/15 記事を追加しました。
ストレージモードにしてはまったことについて説明しています。
数百GiBの全文検索用データベースをMroongaのストレージモードにしてはまったこと - CreateField Blog

4/16 追記
MroongaのInnoDBラッパーモードの採否について以下の記事も参考になると思います。転置インデックスにはトランザクションが効きません。
日々の覚書: MroongaのラッパーモードでInnoDBを使う落とし穴

6/26 記事を追加しました。
Groongaがあまり得意でない類似文書検索に連想検索エンジンGETAssocを使った話

2014-11-29(土)13:30 - 17:30
年に1度のGroongaに関するイベントがあります。Groongaを使っている人、興味がある人は参加してみてはいかがでしょうか。

全文検索エンジンGroongaを囲む夕べ5 - Groonga | Doorkeeper

*1:全てのクエリにおいて、クエリキャッシュは効いておらず、毎クエリごとにmysqldを再起動しています。

*2:検索速度はサーバスペック、ディスク速度に応じて変わると思います。

*3:ドリルダウン検索は割と高速ですが、全文検索の速度と比べると検索結果がかなり多い(数百万件クラス)と件数の積み上げに結構時間がかかります。

*4:半角スペース以外にしたい場合は、default_tokenizerを変えればよいです。

*5:InnoDBのオフセットはストレージモードのオフセットよりもさらに遅いと思います。

*6:全文インデックスのみを使った全文検索ならラップしているストレージエンジンは関係なく高速に全文検索できると思います。

*7:とはいっても、動きはじめて安定すれば、更新もせずに勝手に壊れるというのはないです。