ここでは Mroonga におけるラッパーモードの利用方法を説明します。
ラッパーモードでは、既存のストレージエンジンをラップするかたちで Mroonga が動作します。ラップする対象となるストレージエンジンは、現在のところ SQL のコメントを利用して COMMENT = 'engine "InnoDB"' のように指定するようになっています。
ノート
現在のところ、ラッパーモードではテーブルに必ずプライマリーキーを設定する必要があります。ストレージモードはこの限りではありません。
ノート
ラッパーモードでは現在ストレージモードでサポートされていない、以下をサポートしています。
null値
トランザクション (ストレージエンジンがサポートしている場合。ロールバックするとインデックスの不整合が発生します。インデックスを使った検索に影響するので、その場合にはMroongaのインデックスを再作成します。)
インストールが確認できたら、テーブルを1つ作成してみましょう。 ENGINE = Mroonga とMroongaを指定するところがポイントです。:
mysql> CREATE TABLE diaries (
-> id INT PRIMARY KEY AUTO_INCREMENT,
-> content VARCHAR(255),
-> FULLTEXT INDEX (content)
-> ) ENGINE = Mroonga COMMENT = 'engine "InnoDB"' DEFAULT CHARSET utf8;
Query OK, 0 rows affected (0.52 sec)
INSERTでデータを投入してみましょう。
mysql> INSERT INTO diaries (content) VALUES ("It'll be fine tomorrow.");
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO diaries (content) VALUES ("It'll rain tomorrow");
Query OK, 1 row affected (0.00 sec)
全文検索を実行してみます。
mysql> SELECT * FROM diaries WHERE MATCH(content) AGAINST("fine");
+----+-----------------------------------------+
| id | content |
+----+-----------------------------------------+
| 1 | It'll be fine tomorrow. |
+----+-----------------------------------------+
1 row in set (0.00 sec)
おぉぉー。検索できましたね。
全文検索を行う際、指定したキーワードにより内容が一致するレコードを上位に表示したいというような場合があります。そうしたケースでは検索スコアを利用します。
検索スコアはMySQLの標準的な方法 [1] で取得できます。つまり、SELECTの取得するカラム名を指定するところやORDER BYのところにMATCH...AGAINSTを指定します。
それでは実際にやってみましょう。:
mysql> INSERT INTO diaries (content) VALUES ("It's fine today. It'll be fine tomorrow as well.");
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO diaries (content) VALUES ("It's fine today. But it'll rain tomorrow.");
Query OK, 1 row affected (0.00 sec)
mysql> SELECT *, MATCH (content) AGAINST ("fine") FROM diaries WHERE MATCH (content) AGAINST ("fine") ORDER BY MATCH (content) AGAINST ("fine") DESC;
+----+--------------------------------------------------------------+------------------------------------+
| id | content | MATCH (content) AGAINST ("fine") |
+----+--------------------------------------------------------------+------------------------------------+
| 3 | It's fine today. It'll be fine tomorrow as well. | 2 |
| 1 | It'll be fine tomorrow. | 1 |
| 4 | It's fine today. But it'll rain tomorrow. | 1 |
+----+--------------------------------------------------------------+------------------------------------+
3 rows in set (0.00 sec)
検索対象の文字列 晴れ をより多く含む、すなわち検索スコアの高い id = 3 のメッセージが上に来ていることが確認できます。また、SELECT句にMATCH AGAINSTを記述しているため、検索スコアも取得できています。
属性名を変更したい場合は AS を使って下さい。
mysql> SELECT *, MATCH (content) AGAINST ("fine") AS score FROM diaries WHERE MATCH (content) AGAINST ("fine") ORDER BY MATCH (content) AGAINST ("fine") DESC;
+----+--------------------------------------------------------------+-------+
| id | content | score |
+----+--------------------------------------------------------------+-------+
| 3 | It's fine today. It'll be fine tomorrow as well. | 2 |
| 1 | It'll be fine tomorrow. | 1 |
| 4 | It's fine today. But it'll rain tomorrow. | 1 |
+----+--------------------------------------------------------------+-------+
3 rows in set (0.00 sec)
MySQLは全文検索用のパーサ [2] を指定する以下のような構文を持っています。:
FULLTEXT INDEX (content) WITH PARSER parser_name
しかし、この構文を利用する場合は、あらかじめすべてのパーサをMySQLに登録しておく必要があります。一方、Groongaはトークナイザー(MySQLでいうパーサ)を動的に追加することができます。そのため、Mroongaでもこの構文を採用するとGroonga側に動的に追加されたトークナイザーに対応できなくなります。Groongaに動的に追加されるトークナイザーにはMeCabを用いたトークナイザーもあり、この制限に縛られることは利便性を損なうと判断し、以下のようなコメントを用いた独自の構文を採用することにしました。:
FULLTEXT INDEX (content) COMMENT 'parser "TokenMecab"'
ノート
FULLTEXT INDEX に COMMENT を指定できるのはMySQL 5.5からになります。MySQL 5.1を利用している場合は後述の mroonga_default_parser 変数を利用してください。
パーサに指定できるのは以下の値です。
トークナイズしません。"off"は``content``をそのまま扱いたい場合に使います。例えば、この値は前方一致検索のために指定します。
バイグラムでトークナイズする。ただし、連続したアルファベット・連続した数字・連続した記号はそれぞれ1つのトークンとして扱う。そのため、3文字以上のトークンも存在する。これはノイズを減らすためである。
デフォルト値。
MeCabを用いてトークナイズする。groongaがMeCabサポート付きでビルドされている必要がある。
バイグラムでトークナイズする。TokenBigramと異なり、記号が連続していても特別扱いして1つのトークンとして扱わず通常のバイグラムの処理を行う。
TokenBigramではなくTokenBigramSplitSymbolを利用すると「Is it really!?!?!?」の「!?!?!?」の部分に「!?」でマッチする。TokenBigramの場合は「!?!?!?」でないとマッチしない。
バイグラムでトークナイズする。TokenBigramSplitSymbolに加えて、連続したアルファベットも特別扱いせずに通常のバイグラムの処理を行う。
TokenBigramではなくTokenBigramSplitSymbolAlphaを利用すると「Is it really?」に「real」でマッチする。TokenBigramの場合は「really」でないとマッチしない。
バイグラムでトークナイズする。TokenBigramSplitSymbolAlphaに加えて、連続した数字も特別扱いせずに通常のバイグラムの処理を行う。つまり、すべての字種を特別扱いせずにバイグラムの処理を行う。
TokenBigramではなくTokenBigramSplitSymbolAlphaDigitを利用すると「090-0123-4567」に「567」でマッチする。TokenBigramの場合は「4567」でないとマッチしない。
バイグラムでトークナイズする。TokenBigramと異なり、空白を無視して処理する。
TokenBigramではなくTokenBigramIgnoreBlankを利用すると「み な さ ん 注 目」に「みなさん」でマッチする。TokenBigramの場合は「み な さ ん」でないとマッチしない。
バイグラムでトークナイズする。TokenBigramSplitSymbolと異なり、空白を無視して処理する。
TokenBigramSplitSymbolではなくTokenBigramIgnoreBlankSplitSymbolを利用すると「! !? ??」に「???」でマッチする。TokenBigramSplitSymbolの場合は「? ??」でないとマッチしない。
バイグラムでトークナイズする。TokenBigramSplitSymbolAlphaと異なり、空白を無視して処理する。
TokenBigramSplitSymbolAlphaではなくTokenBigramIgnoreBlankSplitSymbolAlphaを利用すると「I am a pen.」に「ama」でマッチする。TokenBigramSplitSymbolAlphaの場合は「am a」でないとマッチしない。
バイグラムでトークナイズする。TokenBigramSplitSymbolAlphaDigitと異なり、空白を無視して処理する。
okenBigramSplitSymbolAlphaDigitではなくTokenBigramIgnoreBlankSplitSymbolAlphaDigitを利用すると「090 0123 4567」に「9001」でマッチする。TokenBigramSplitSymbolAlphaDigitの場合は「90 01」でないとマッチしない。
空白区切りでトークナイズする。
「movie horror topic」をトークナイズすると「movie」、「horror」、「topic」になります。
null文字(\0)区切りでトークナイズする。
「movie\0horror\0topic」をトークナイズすると「movie」、「horror」、「topic」になります。
ユニグラムでトークナイズする。ただし、連続したアルファベット・連続した数字・連続した記号はそれぞれ1つのトークンとして扱う。そのため、2文字以上のトークンも存在する。これはノイズを減らすためである。
トリグラムでトークナイズする。ただし、連続したアルファベット・連続した数字・連続した記号はそれぞれ1つのトークンとして扱う。そのため、4文字以上のトークンも存在する。これはノイズを減らすためである。
デフォルトのパーサは configure の --with-default-parser オプションでビルド時に指定することができます。:
./configure --with-default-parser TokenMecab ...
また、my.cnfまたはSQL内で mroonga_default_parser 変数を指定することでも指定できます。my.cnfで指定するとMySQLを再起動しても値は変更されたままですが、反映させるために再起動しなければいけません。一方、SQLで指定した場合はすぐに設定が反映されますが、MySQLが再起動すると設定は失われます。
my.cnf:
[mysqld]
mroonga_default_parser=TokenMecab
SQL:
mysql> SET GLOBAL mroonga_default_parser = TokenMecab;
Query OK, 0 rows affected (0.00 sec)
Mroonga provides functionality to get keyword in context. It is implemented as 'mroonga_snippet' UDF.
詳細は mroonga_snippet を参照してください。
Mroongaではデフォルトでログの出力を行うようになっています。
ログファイルはMySQLのデータディレクトリ直下に groonga.log というファイル名で出力されます。
以下はログの出力例です。
2010-10-07 17:32:39.209379|n|b1858f80|mroonga 1.10 started.
2010-10-07 17:32:44.934048|d|46953940|hash get not found (key=test)
2010-10-07 17:32:44.936113|d|46953940|hash put (key=test)
ログのデフォルトの出力レベルはNOTICE(必要な情報のみ出力。デバッグ情報などは出力しない)となっております。
ログの出力レベルは mroonga_log_level というシステム変数で確認することができます(グローバル変数)。またSET文で動的に出力レベルを変更することもできます。
mysql> SHOW VARIABLES LIKE 'mroonga_log_level';
+-------------------+--------+
| Variable_name | Value |
+-------------------+--------+
| mroonga_log_level | NOTICE |
+-------------------+--------+
1 row in set (0.00 sec)
mysql> SET GLOBAL mroonga_log_level=DUMP;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW VARIABLES LIKE 'mroonga_log_level';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| mroonga_log_level | DUMP |
+-------------------+-------+
1 row in set (0.00 sec)
設定可能なログレベルは以下の通りです。
詳細は mroonga_log_level を参照してください。
またFLUSH LOGSでログの再オープンを行うことができます。MySQLサーバを停止せずにログのローテートを行いたいような場合には、以下の手順で実行すると良いでしょう。
groonga.log ファイルの名前を変更(OSコマンドのmvなどで)
MySQLサーバに対して"FLUSH LOGS"を実行(mysqlコマンドあるいはmysqladminコマンドにて)
一般的にMySQLでは"ORDER BY"はインデックス経由のレコード参照が行えればほぼノーコストで処理可能であり、"LIMIT"は検索結果が大量にヒットする場合でも処理対象を限定することでコストを一定に抑える効果があります。
しかし例えば全文検索のスコアの降順+LIMITのように"ORDER BY"の処理の際にインデックスが効かないクエリの場合、検索ヒット件数に比例したコストがかかってしまうため、特に大量の検索がヒットするようなキーワード検索においてクエリ処理に極端に時間がかかってしまうケースがあります。
Tritonnではこの問題に対して特に対応はできていませんでしたが、最新レポジトリではsen_records_sort関数を活用してSennaからの読み出しをスコアの降順に対応させることでSQLクエリからORDER BY句を取り除く(※スコア降順を指定していたケースに対してのみ有効)回避方法を導入しました。
Mroongaでも ORDER BY LIMIT を高速化するための仕組みを実装しています。
例えば以下のSELECT文では ORDER BY LIMIT は、Mroonga内で処理され、必要最小限のレコードだけをMySQLに返却しています。
SELECT * FROM t1 WHERE MATCH(c2) AGAINST("hoge") ORDER BY c1 LIMIT 1;
この最適化処理が行われたかどうかはステータス変数で確認することもできます。:
mysql> SHOW STATUS LIKE 'mroonga_fast_order_limit';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| mroonga_fast_order_limit | 1 |
+--------------------------+-------+
1 row in set (0.00 sec)
ORDER BY LIMIT 高速化の処理が行われる度に Mroonga_fast_order_limit ステータス変数がインクリメントされます。
備考:この高速化機能は、「select ... match against order by _score desc limit X, Y」を狙い撃ちした高速化で、現在のところ以下の条件が成立した場合に機能します。
where句がmatch...againstのみ
joinしていない
limitの指定がある
order byの指定がカラム(_id含む)またはwhere句に指定したmatch...againstである
脚注
[1] |
[2] | Groongaではトークナイザーと呼んでいる。 |