null+****@clear*****
null+****@clear*****
2011年 6月 24日 (金) 15:27:14 JST
mooz 2011-06-24 06:27:14 +0000 (Fri, 24 Jun 2011)
New Revision: 206724a4f0b298c4c0e66897c4b1d997c94fe219
Log:
Write documentation for wrapper mode. fixes #1016.
Modified files:
doc/ja/source/userguide/wrapper.rst
Modified: doc/ja/source/userguide/wrapper.rst (+86 -194)
===================================================================
--- doc/ja/source/userguide/wrapper.rst 2011-06-24 06:25:19 +0000 (5dbc996)
+++ doc/ja/source/userguide/wrapper.rst 2011-06-24 06:27:14 +0000 (3908da3)
@@ -5,161 +5,140 @@
ここでは groonga ストレージエンジンにおけるラッパーモードの利用方法を説明します。
+ラッパーモードの利用方法
+------------------------
+
+ラッパーモードでは、既存のストレージエンジンをラップするかたちで groonga ストレージエンジンが動作します。ラップする対象となるストレージエンジンは、現在のところ SQL のコメントを利用して ``COMMENT = 'engine "innodb"'`` のように指定するようになっています。
+
+.. note::
+
+ 現在のところ、ラッパーモードではテーブルに必ずプライマリーキーを設定する必要があります。ストレージモードはこの限りではありません。
+
全文検索の利用方法
------------------
-インストールが確認できたら、テーブルを1つ作成してみましょう。 ::
+それでは早速 groonga ストレージエンジンのラッパーモードを利用して、テーブルを1つ作成してみましょう。 ::
- mysql> CREATE TABLE t1 (
- > c1 INT PRIMARY KEY,
- > c2 VARCHAR(255),
- > FULLTEXT INDEX (c2)
- > ) ENGINE = groonga DEFAULT CHARSET utf8;
- Query OK, 0 rows affected (0.22 sec)
+ mysql> CREATE TABLE weather_forecasts (
+ -> id INT PRIMARY KEY,
+ -> content VARCHAR(255),
+ -> FULLTEXT INDEX (content)
+ -> ) ENGINE = groonga COMMENT = 'engine "innodb"' DEFAULT CHARSET utf8;
+ Query OK, 0 rows affected (0.30 sec)
-INSERTでデータを投入してみましょう。 ::
+次に INSERT でデータを投入してみましょう。 ::
- mysql> INSERT INTO t1 VALUES(1, "明日の天気は晴れでしょう。");
- Query OK, 1 row affected (0.01 sec)
+ mysql> INSERT INTO weather_forecasts VALUES(1, "明日の天気は晴れでしょう。");
+ Query OK, 1 row affected (0.04 sec)
- mysql> INSERT INTO t1 VALUES(2, "明日の天気は雨でしょう。");
+ mysql> INSERT INTO weather_forecasts VALUES(2, "明日の天気は雨でしょう。");
Query OK, 1 row affected (0.04 sec)
-全文検索を実行してみます。 ::
+データの投入が終了したら、全文検索を実行してみます。 ::
- mysql> SELECT * FROM t1 WHERE MATCH(c2) AGAINST("晴れ");
+ mysql> SELECT * FROM weather_forecasts WHERE MATCH(content) AGAINST("晴れ");
+----+-----------------------------------------+
- | c1 | c2 |
+ | id | content |
+----+-----------------------------------------+
| 1 | 明日の天気は晴れでしょう。 |
+----+-----------------------------------------+
- 1 row in set (0.02 sec)
-
-おぉぉー。検索できましたね。
+ 1 row in set (0.00 sec)
+お、検索できましたね。
検索スコアの取得方法
--------------------
全文検索を行う際、指定したキーワードにより内容が一致するレコードを上位に表示したいというような場合があります。そうしたケースでは検索スコアを利用します。
-検索スコアを取得するためには、テーブル定義時に ``_score`` という名前のカラムを作成して下さい。 ::
+ストレージモードでは、検索スコアの取得を行うために ``_score`` という名前の仮想カラムを作成していました。一方、ラッパーモードではこの必要がありません。その代わりに WHERE 句へ指定したものと同じ match against を ORDER BY や SELECT 部分に指定することで、そのマッチの検索スコアを利用することができるようになっています。これは MySQL の全文検索機能における標準的な検索スコアの取得方法にならったものです( `MySQL 5.1 リファレンスマニュアル :: 11 関数と演算子 :: 11.7 全文検索関数`_ )。
- mysql> CREATE TABLE t1 (
- > c1 INT PRIMARY KEY,
- > c2 TEXT,
- > _score FLOAT,
- > FULLTEXT INDEX (c2)
- > ) ENGINE = groonga DEFAULT CHARSET utf8;
- Query OK, 0 rows affected (0.22 sec)
+.. _`MySQL 5.1 リファレンスマニュアル :: 11 関数と演算子 :: 11.7 全文検索関数`: http://dev.mysql.com/doc/refman/5.1/ja/fulltext-search.html
-_scoreカラムのデータ型はFLOATまたはDOUBLEである必要があります。
+それでは、実際に検索スコアを利用してみることにしましょう。まずはテーブルを作成します。 ::
-INSERTでテーブルにレコードを追加してみましょう。_scoreカラムは仮想カラムとして実装されており、更新は行えません。更新対象から外すか、値に ``null`` を使用する必要があります。 ::
+ mysql> CREATE TABLE messages (
+ -> id INT PRIMARY KEY,
+ -> message TEXT,
+ -> FULLTEXT INDEX (message)
+ -> ) ENGINE = groonga COMMENT = 'engine "innodb"' DEFAULT CHARSET utf8;
+ Query OK, 0 rows affected (0.28 sec)
- mysql> insert into t1 values(1, "aa ii uu ee oo", null);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> insert into t1 values(2, "aa ii ii ii oo", null);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> insert into t1 values(3, "dummy", null);
- Query OK, 1 row affected (0.00 sec)
-
-全文検索(MATCH...AGAINSTによる検索)を実行した場合、_scoreカラムを通じて検索スコアを取得できます。_scoreカラムをORDER BYに指定することで結果のソートも行うことができます。 ::
-
- mysql> select * from t1 where match(c2) against("ii") order by _score desc;
- +----+----------------+--------+
- | c1 | c2 | _score |
- +----+----------------+--------+
- | 2 | aa ii ii ii oo | 3 |
- | 1 | aa ii uu ee oo | 1 |
- +----+----------------+--------+
- 2 rows in set (0.00 sec)
+次に、検索対象となるデータを挿入します。 ::
-レコードIDの取得方法
---------------------
+ mysql> INSERT INTO messages VALUES(1, "aa ii uu ee oo");
+ Query OK, 1 row affected (0.04 sec)
-groongaではテーブルにレコードを追加した際にレコードを一意に識別するための番号が割当てられます。
+ mysql> INSERT INTO messages VALUES(2, "aa ii ii ii oo");
+ Query OK, 1 row affected (0.04 sec)
-groongaストレージエンジンではアプリケーションの開発を容易にするため、このレコードIDをSQLで取得できるようになっています。
+ mysql> INSERT INTO messages VALUES(3, "hoge huga hehe");
+ Query OK, 1 row affected (0.05 sec)
-レコードIDを取得するためには、テーブル定義時に ``_id`` という名前のカラムを作成して下さい。 ::
+ mysql> INSERT INTO messages VALUES(4, "foo bar baz");
+ Query OK, 1 row affected (0.04 sec)
- mysql> CREATE TABLE t1 (
- -> _id INT,
- -> foo INT,
- -> UNIQUE KEY (_id) USING HASH
- -> ) ENGINE = groonga;
- Query OK, 0 rows affected (0.01 sec)
+データが挿入し終わったので、実際に検索スコアを利用した検索を行なってみます。 ::
-_idカラムのデータ型は整数型(TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT)である必要があります。
+ mysql> SELECT * FROM messages WHERE MATCH(message) AGAINST("ii") ORDER BY MATCH(message) AGAINST("ii") DESC;
+ +----+----------------+
+ | id | message |
+ +----+----------------+
+ | 2 | aa ii ii ii oo |
+ | 1 | aa ii uu ee oo |
+ +----+----------------+
+ 2 row in set (0.00 sec)
-また_idカラムにはインデックスを作成することが可能ですが、HASH形式である必要があります。
+検索対象の文字列 ``ii`` をより多く含む、すなわち検索スコアの高い ``id = 2`` のメッセージが上に来ていることが確認できます。
-INSERTでテーブルにレコードを追加してみましょう。_idカラムは仮想カラムとして実装されており、また_idの値であるレコードIDはgroongaにより割当てられるため、SQLによる更新時に値を指定することはできません。
-更新対象から外すか、値に ``null`` を使用する必要があります。 ::
+ここで、SELECT 句に match against を記述することで、検索スコアの値自体を検索結果に含めることも可能です。 ::
- mysql> INSERT INTO t1 VALUES (null, 100);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> INSERT INTO t1 VALUES (null, 100);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> INSERT INTO t1 VALUES (null, 100);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> INSERT INTO t1 VALUES (null, 100);
- Query OK, 1 row affected (0.00 sec)
-
-レコードIDを取得するには、_idカラムを含むようにしてSELECTを行います。 ::
-
- mysql> select * from t1;
- +------+------+
- | _id | foo |
- +------+------+
- | 1 | 100 |
- | 2 | 100 |
- | 3 | 100 |
- | 4 | 100 |
- +------+------+
- 4 rows in set (0.00 sec)
-
-また直前のINSERTにより割当てられたレコードIDについては、last_insert_grn_id関数により取得することもできます。 ::
-
- mysql> INSERT INTO t1 VALUES (null, 100);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> SELECT last_insert_grn_id();
- +----------------------+
- | last_insert_grn_id() |
- +----------------------+
- | 5 |
- +----------------------+
- 1 row in set (0.00 sec)
+ mysql> SELECT *, MATCH(message) AGAINST("ii") FROM messages WHERE MATCH(message) AGAINST("ii") ORDER BY MATCH(message) AGAINST("ii") DESC;
+ +----+----------------+------------------------------+
+ | id | message | MATCH(message) AGAINST("ii") |
+ +----+----------------+------------------------------+
+ | 2 | aa ii ii ii oo | 3 |
+ | 1 | aa ii uu ee oo | 1 |
+ +----+----------------+------------------------------+
+ 2 rows in set (0.00 sec)
+
+属性名を変更したい場合は ``AS`` を使って下さい。 ::
-last_insert_grn_id関数はユーザ定義関数(UDF)としてgroongaストレージエンジンに含まれていますが、インストール時にCREATE FUNCTIONでMySQLに追加していない場合には、以下の関数定義DDLを実行しておく必要があります。 ::
+ mysql> SELECT *, MATCH(message) AGAINST("ii") AS score FROM messages WHERE MATCH(message) AGAINST("ii") ORDER BY MATCH(message) AGAINST("ii") DESC;
+ +----+----------------+-------+
+ | id | message | score |
+ +----+----------------+-------+
+ | 2 | aa ii ii ii oo | 3 |
+ | 1 | aa ii uu ee oo | 1 |
+ +----+----------------+-------+
+ 2 rows in set (0.00 sec)
- mysql> CREATE FUNCTION last_insert_grn_id RETURNS INTEGER SONAME 'ha_groonga.so';
+レコードIDの取得方法
+--------------------
-ご覧のように_idカラムやlast_insert_grn_id関数を通じてレコードIDを取得することができました。ここで取得したレコードIDは後続のUPDATEなどのSQL文で利用すると便利です。 ::
+ストレージモードでは ``_id`` という名前のカラムを作成することにより groonga 内部でのレコード ID 値を取得することが可能となっていました。
- mysql> UPDATE t1 SET foo = 200 WHERE _id = 5;
+他方、ラッパーモードでは groonga 内部でのレコード ID 値を取得することができません。これは「レコードを一意に識別するためにはより MySQL の作法に従ったプライマリキーを利用すべきである」という設計方針によるものです。
ログ出力
--------
groongaストレージエンジンではデフォルトでログの出力を行うようになっています。
-ログファイルはMySQLのデータディレクトリ直下に ``groonga.log`` というファイル名で出力されます。
+ログファイルはMySQLのデータディレクトリ(/var/lib/mysql/ など)直下に ``groonga.log`` というファイル名で出力されます。
以下はログの出力例です。 ::
- 2010-10-07 17:32:39.209379|n|b1858f80|groonga-storage-engine 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)
+ 2011-06-24 11:11:31.282121|n|6bdea740|groonga-storage-engine started.
+ 2011-06-24 11:11:31.282154|n|6bdea740|log level is 'NOTICE'
+ 2011-06-24 11:30:58.485508|n|3cda6700|DDL:table_create x
+ 2011-06-24 11:31:05.131690|n|cee84700|DDL:obj_remove x
+ 2011-06-24 13:37:31.692572|n|86ceb700|DDL:column_create t1_0001 c2
+ 2011-06-24 13:37:31.781556|n|86ceb700|DDL:set_source t1_0001.c2 t1.c2
+ 2011-06-24 13:49:27.767387|n|5cd1f700|DDL:obj_remove t1_0001
+ 2011-06-24 14:33:55.867480| |8cd59700|96a20c50|:18446744072478952540 filter(2)
-ログのデフォルトの出力レベルはNOTICE(必要な情報のみ出力。デバッグ情報などは出力しない)となっております。
+ログのデフォルトの出力レベルは NOTICE (必要な情報のみ出力。デバッグ情報などは出力しない)となっています。
ログの出力レベルは ``groonga_log_level`` というシステム変数で確認することができます(グローバル変数)。またSET文で動的に出力レベルを変更することもできます。 ::
@@ -199,90 +178,3 @@ groongaストレージエンジンではデフォルトでログの出力を行
1. ``groonga.log`` ファイルの名前を変更(OSコマンドのmvなどで)
2. MySQLサーバに対して"FLUSH LOGS"を実行(mysqlコマンドあるいはmysqladminコマンドにて)
-
-カラムの刈り込み
-----------------
-
-groongaでは各カラムごとにファイルを分けてデータを格納する「カラムストア方式」が採用されており、groongaストレージエンジンではこの特性を活かすためにテーブルアクセス時に必要なカラムに対してのみアクセスを行う実装を行っています。
-
-この高速化の仕組みはgroongaストレージエンジン内部で自動的に行われるため、特に設定などを行う必要はありません。
-
-例えば以下のようにカラムが20個定義されているテーブルが存在するものと仮定します。 ::
-
- CREATE TABLE t1 (
- c1 INT PRIMARY KEY AUTO_INCREMENT,
- c2 INT,
- c3 INT,
- ...
- c11 VARCHAR(20),
- c12 VARCHAR(20),
- ...
- c20 DATETIME
- ) ENGINE = InnoDB DEFAULT CHARSET utf8;
-
-この時、以下のようなSELECT文が発行される場合、groongaストレージエンジンではSELECT句およびWHERE句で参照しているカラムに対してのみデータの読み取りを行ってSQL文を処理します(内部的に不要なカラムに対してはアクセスしません)。 ::
-
- SELECT c1, c2, c11 FROM t1 WHERE c2 = XX AND c12 = "XXX";
-
-このケースではc1,c2,c11,c12に対してのみアクセスが行われ、SQL文が高速に処理されることになります。
-
-行カウント高速化
-----------------
-
-COUNT(\*)などの行カウントを行う場合と通常のSELECTによるデータ参照を行う場合に対して、従来よりMySQLではストレージエンジンの呼び出しを行う部分(=ストレージエンジンインタフェース)における区別が存在していないため、行数をカウントするだけで良いような場合にもレコードアクセス(SELECTの結果には含まれないデータへのアクセス)が行われる問題があります。
-
-groongaストレージエンジンの前身であるTritonn(MySQL+Senna)ではこの問題に対して"2indパッチ"という不要なレコードアクセスを省略する仕組みを独自に実装してこの性能問題を回避していました。
-
-これに引き続き、groongaストレージエンジンでも行カウントを高速化するための仕組みを実装しています。
-
-例えば以下のSELECT文では不要なカラムデータの読み取りは省略され、必要最小限のコストで行カウントの結果を返すことができます。 ::
-
- SELECT COUNT(*) FROM t1 WHERE MATCH(c2) AGAINST("hoge");
-
-行カウント高速化の処理が行われたかどうかはステータス変数で確認することもできます。::
-
- mysql> show status like 'groonga_count_skip';
- +--------------------+-------+
- | Variable_name | Value |
- +--------------------+-------+
- | groonga_count_skip | 1 |
- +--------------------+-------+
- 1 row in set (0.00 sec)
-
-行カウント高速化の処理が行われる度に ``groonga_count_skip`` ステータス変数がインクリメントされます。
-
-備考:この高速化機能はインデックスを用いて実装されています。現在のところインデックスアクセスのみでレコードが特定できるパタンでのみ有効に機能します。
-
-全文検索時の ORDER BY LIMIT 高速化
-----------------------------------
-
-一般的にMySQLでは"ORDER BY"はインデックス経由のレコード参照が行えればほぼノーコストで処理可能であり、"LIMIT"は検索結果が大量にヒットする場合でも処理対象を限定することでコストを一定に抑える効果があります。
-
-しかし例えば全文検索のスコアの降順+LIMITのように"ORDER BY"の処理の際にインデックスが効かないクエリの場合、検索ヒット件数に比例したコストがかかってしまうため、特に大量の検索がヒットするようなキーワード検索においてクエリ処理に極端に時間がかかってしまうケースがあります。
-
-Tritonnではこの問題に対して特に対応はできていませんでしたが、最新レポジトリではsen_records_sort関数を活用してSennaからの読み出しをスコアの降順に対応させることでSQLクエリからORDER BY句を取り除く(※スコア降順を指定していたケースに対してのみ有効)回避方法を導入しました。
-
-groongaストレージエンジンでも行カウントを高速化するための仕組みを実装しています。
-
-例えば以下のSELECT文では ORDER BY LIMIT は、groonga内で処理され、必要最小限のレコードだけをMySQLに返却しています。 ::
-
- SELECT * FROM t1 WHERE MATCH(c2) AGAINST("hoge") ORDER BY c1 LIMIT 1;
-
-ORDER BY LIMIT 高速化の処理が行われたかどうかはステータス変数で確認することもできます。::
-
- mysql> show status like 'groonga_fast_order_limit';
- +--------------------------+-------+
- | Variable_name | Value |
- +--------------------------+-------+
- | groonga_fast_order_limit | 1 |
- +--------------------------+-------+
- 1 row in set (0.00 sec)
-
-ORDER BY LIMIT 高速化の処理が行われる度に ``groonga_fast_order_limit`` ステータス変数がインクリメントされます。
-
-備考:この高速化機能は、「select ... match against order by _score desc limit X, Y」を狙い撃ちした高速化で、現在のところ以下の条件が成立した場合に機能します。
-
-* where句がmatch...againstのみ
-* joinしていない
-* limitの指定がある
-* order byの指定がカラムである(_score、_id含む)