初めまして、
MySQLで完結する日本語対応の全文検索プロダクトである
そこで、
1ヶ月間の検証期間で見つかった総計16にも及ぶ機能追加や不具合改善を、
数回に渡り、
Tritonnが実現できたことと、残された課題
MySQLが標準で備える全文検索機能は簡素なもので、
Tritonnは、
- 実現できたこと
- MeCabやN-gramでのトークナイズに対応
- 完全転置インデックスを用いた高速な検索を実現
- 転置インデックスの一部をmmapすることで、
データ更新速度の大幅改善を実現 - MeCabを用いてトークナイズした形態素を単語として転置インデックスとすることで、
高精度を実現 - SQLならではの複雑な問い合わせにも対応できる柔軟性を維持
- 残された課題
- パフォーマンス問題
- LIMIT句, COUNT(*), OR条件追加時の検索が遅い
- MySQLが持つ1テーブル1インデックスという制約を受け、
全文検索以外の条件を加えて絞り込むと遅い - MyISAMを利用しているため、
更新中はテーブルロックとなり参照クエリを発行できない
- 信頼性の問題
- ストレージエンジンがMyISAMのため、
トランザクションに対応していない
- ストレージエンジンがMyISAMのため、
- パフォーマンス問題
これらの課題を解決すべく、
次の記事も参照ください。
Tritonn, mroonga以外のMySQLで完結する全文検索プロダクト
mroonga移行を検討している際に見つけた、
InnoDB fulltext search (InnoDB FTS)
MySQL 5. そのため、 より詳細な情報は次の記事を参照ください。 MySQL-ftppcとは、 これは5つのトークナイザに対応しており、 パフォーマンスとしてはXeon L5520のマシンで400万行、 より詳細な情報は次の記事を参照ください。 これまでに取り上げた4つのプロダクト、 mroongaのN-gramについては、 Tritonnから乗り換えるならば、 それでは早速、 もはやレガシーとなったMySQL 5. より詳細な情報は次の記事を参照ください。 ストレージモードでは、 mroongaのラッパーモードを利用すれば、 InnoDBの主な特徴としては次の通りです。 MySQL 5. アップデートが容易なプラガブルストレージエンジン、 Tritonnではmecab、 デフォルトは Tritonnでは、 mroongaでは全文検索機能を完全に外部で持つため、 しかしながら、 より具体的な挙動の違いに関しては、 mroongaのストレージモード・ テーブルのスキーマは次のように定義します。 データの登録・ ただし、 汎用的に矩形での位置情報検索をするなら、 ストレージモードとは、 そういった事情もあり、 mroongaと間接的に関わる話として、 具体的にどのような挙動の違いがあるか、 InnoDBの場合、 具体例とともに解説します。 この挙動となる仕組みについては、 なお、 その他、 InnoDBラッパーモードそのものでは、 具体的な方法に関しては、 今回はTritonnとmroongaそれぞれの紹介と移行時の要注意点、 次回は、
MySQL-ftppc
機能比較
Tritonn
mroonga
MySQL-ftppc
InnoDB FTS
InnoDB
×
○
×
○
MyISAM
○
○
○
×
MeCab
○
○
○
×
N-gram
○
◎
○
×
Dプラグマ
○
○
×
×
Eプラグマ
○
×
×
×
Wプラグマ
○
○
×
×
MySQL 5.
○
×
×
×
MySQL 5.
×
○
○
×
MySQL 5.
×
○
○
×
MySQL 5.
×
○
○
○
mysql-mroonga-3.
にて、Tritonnからmroongaへ移行する7つのメリット
MySQL 5.
参照ロックフリー
ストレージエンジンにInnoDBを利用できること
プラガブルストレージエンジンである
bigram以外の豊富なN-gramトークナイザが利用可能
parser "TokenBigram"
が指定されているものとして動作しますが、CREATE TABLE search (
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(255),
FULLTEXT INDEX (content) COMMENT 'parser "TokenUnigram"'
) ENGINE=mroonga DEFAULT CHARSET=utf8;
より強化された文字列正規化機能が利用可能
NO NORMALIZE
と指定することで無効化される挙動でした。groonga-normalizer-mysql
パッケージをインストールの上、COMMENT 'normalizer "NormalizerMySQLGeneralCI"'
のように指定しましょう。CREATE TABLE search (
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(255),
FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerMySQLGeneralCI"'
) ENGINE=mroonga DEFAULT CHARSET=utf8;
位置情報検索に対応
-- ストレージモードでは、POINT型を指定する
CREATE TABLE shops (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255),
location POINT NOT NULL,
SPATIAL INDEX (location)
) ENGINE = mroonga;
-- InnoDBラッパーモードでは、GEOMETRY型を指定する
CREATE TABLE shops (
id INT PRIMARY KEY AUTO_INCREMENT,
name TEXT,
location GEOMETRY NOT NULL,
SPATIAL INDEX location_index (location)
) ENGINE = mroonga comment = 'engine "innodb"';
-- データの登録例
-- GeomFromText()関数を利用し、文字列からPOINT型に変換します
INSERT INTO shops VALUES (null, 'Naniwaya', GeomFromText('POINT(139.796234 35.730061)'));
-- データの検索例
-- 池袋駅(139.7101 35.7292)を左上の点、東京駅(139.7662 35.6815)を右下の点とした長方形内にあるお店を探す場合のクエリです
SELECT id, name, AsText(location) AS location_text FROM shops
WHERE MBRContains(GeomFromText('LineString(139.7101 35.7292, 139.7662 35.6815)'), location);
移行する際に気をつけたいポイント
ストレージモードとラッパーモード
ラッパーモードで使うストレージエンジン
COMMENT='engine "innodb"'
のように、CREATE TABLE search (
id INT PRIMARY KEY AUTO_INCREMENT,
content VARCHAR(255),
FULLTEXT INDEX (content)
) ENGINE=mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"';
ストレージエンジンをMyISAMからInnoDBへ切り替える際の注意
-- テーブルを作成
CREATE TABLE test (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10),
UNIQUE INDEX (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
IGNORE INTO文の動作の違い
INSERT IGNORE INTO ...
を用いて解説します。なお、INSERT INTO ... ON DUPLICATE KEY UPDATE ...
の挙動も同一です。-- 1つめのレコードを入れます
mysql> INSERT IGNORE INTO test (name) VALUES('トマト');
Query OK, 1 row affected (0.00 sec)
-- 1件目なので、1が返ります
mysql> SELECT LAST_INSERT_ID()\G
*************************** 1. row ***************************
LAST_INSERT_ID(): 1
1 row in set (0.00 sec)
-- 重複のためスキップされるレコードを入れます
mysql> INSERT IGNORE INTO test (name) VALUES('トマト');
Query OK, 0 rows affected (0.00 sec)
-- 重複のため、1のままです
mysql> SELECT LAST_INSERT_ID()\G
*************************** 1. row ***************************
LAST_INSERT_ID(): 1
1 row in set (0.00 sec)
-- 重複とならないデータを追加します
mysql> INSERT IGNORE INTO test (name) VALUES('トマト2');
Query OK, 1 row affected (0.00 sec)
-- MyISAMではidが2となるのですが、InnoDBでは3となります
mysql> SELECT LAST_INSERT_ID()\G
*************************** 1. row ***************************
LAST_INSERT_ID(): 3
1 row in set (0.00 sec)
-- InnoDBのテーブル内容は以下の通りです
mysql> select * from test;
+----+------------+
| id | name |
+----+------------+
| 1 | トマト |
| 3 | トマト2 |
+----+------------+
2 rows in set (0.00 sec)
データを削除した時のプライマリキーの挙動の違い
-- まずは1件目のデータを挿入します
mysql> INSERT INTO test (name) VALUES('トマト1');
Query OK, 1 row affected (0.02 sec)
-- 続けて2件目のデータを挿入します
mysql> INSERT INTO test (name) VALUES('トマト2');
Query OK, 1 row affected (0.02 sec)
-- 期待通り、idは2となりました
mysql> SELECT LAST_INSERT_ID()\G
*************************** 1. row ***************************
LAST_INSERT_ID(): 2
1 row in set (0.00 sec)
-- idが2のデータを削除します
mysql> DELETE FROM test WHERE name IN('トマト2');
Query OK, 1 row affected (0.03 sec)
-- 続けて3件目のデータを挿入します
mysql> INSERT INTO test (name) VALUES('トマト3');
Query OK, 1 row affected (0.03 sec)
-- 期待通り、idは3となりました
mysql> SELECT LAST_INSERT_ID()\G
*************************** 1. row ***************************
LAST_INSERT_ID(): 3
1 row in set (0.00 sec)
-- idが3のデータを削除します
mysql> DELETE FROM test WHERE name IN('トマト3');
Query OK, 1 row affected (0.02 sec)
-- ここで、MySQLを再起動し、再度接続後に以下の行を挿入します
mysql> INSERT INTO test (name) VALUES('トマト4');
Query OK, 1 row affected (0.03 sec)
-- 再起動により、MyISAMでは4となる一方、InnoDBでは2となりました
mysql> SELECT LAST_INSERT_ID()\G
*************************** 1. row ***************************
LAST_INSERT_ID(): 2
1 row in set (0.00 sec)
-- InnoDBのテーブル内容は以下の通りです
mysql> select * from test;
+----+------------+
| id | name |
+----+------------+
| 1 | トマト1 |
| 2 | トマト4 |
+----+------------+
2 rows in set (0.01 sec)
COMMENT='engine "myisam"'
と指定することで、パーティショニング
まとめと次回予告