こんにちは、
これまでのあらすじ
将来的な技術負債を残さない、
これまでの記事と、
- [実録] MySQL向け全文検索エンジン
「Tritonn」 から 「mroonga」 への移行ガイド (1) - Tritonnとmroongaそれぞれの紹介と移行時の要注意点
- 現在利用できる日本語全文検索ソリューションの比較
- Tritonnからmroongaへ移行する7つのメリット
- ストレージエンジンをMyISAMからmroongaへ移行する際に気をつけたいAuto Incrementの挙動の違い
- [実録] MySQL向け全文検索エンジン
「Tritonn」 から 「mroonga」 への移行ガイド (2) - Tritonnからmroongaへ移行する際の段階的移行を可能にするシステム構成
- MySQL 5.
0のレプリケーションマスタにMySQL 5. 6をスレーブとして追加する方法 - オラクル公式RPMパッケージを用いたCentOS 6.
4環境へのMySQL 5. 6インストール方法
- CentOS6でのRPMパッケージを用いた MySQL 5.
6 & mroonga & PHP 5. 4 環境の作り方 - RPMパッケージのビルド環境の作り方
- オラクル公式MySQL 5.
6と共存できるPHP 5. 4のRPMパッケージの作り方 - RPMパッケージを使ったMySQL 5.
6.12、 mroonga、 PHP 5. 4環境のインストール手順 - mroongaのnightly版のRPMパッケージの作り方
今回の流れ
前回までのオペレーションでは、
- mroongaのインストール方法
- Tritonnからmroongaへのテーブルスキーマ書き換えガイド
- Tritonnからmroongaへの全文検索クエリ書き換えガイド
- mroongaのプロダクション環境への投入
- Tritonnからmroongaへ移行する際の要注意ポイント
- mroongaのトラブルシューティング
- まとめ
なお、
- GRANT文を用いたMySQLユーザの追加
- MySQL 5.
6レプリケーションスレーブのクローン - 稼働試験用データベースの作成と、
それを用いたWebアプリケーションとの結合動作試験 - LVSを用いたMySQLのDSR構成化
ここまで完了すれば移行の準備はほぼ完了です。今回の事例では段階を追わずに一気にMySQL 5.
mroongaのインストール方法
前回までの記事では、
# groongaリポジトリをまだインストールしていない場合に実行 $ sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm # groongaリポジトリより、関連パッケージのインストール $ sudo yum install mecab mecab-devel mecab-ipadic groonga-libs groonga-tokenizer-mecab --enablerepo=groonga # MySQLが起動していることを確認 $ sudo /etc/init.d/mysql status # 自分でパッケージビルドしたmroongaのRPMパッケージをインストール $ sudo yum localinstall /home/build/rpmbuild/RPMS/x86_64/mysql56-mroonga-3.05-1.el6.x86_64.rpm # mroongaがインストールされていることをmroongaという行があることで確認 $ mysql -uroot -p -e "SHOW PLUGINS" | grep mroonga | mroonga | ACTIVE | STORAGE ENGINE | ha_mroonga.so | GPL | # インストールできたmroongaのバージョンや設定を確認 $ mysql -uroot -p -e "SHOW VARIABLES LIKE 'mroonga_%'" +------------------------------------+-------------+ | Variable_name | Value | +------------------------------------+-------------+ | mroonga_database_path_prefix | | | mroonga_default_parser | TokenBigram | | mroonga_default_wrapper_engine | | | mroonga_dry_write | OFF | | mroonga_enable_optimization | ON | | mroonga_libgroonga_version | 3.0.5 | | mroonga_log_file | groonga.log | | mroonga_log_level | NOTICE | | mroonga_match_escalation_threshold | 0 | | mroonga_version | 3.05 | +------------------------------------+-------------+
もしMySQLを停止した状態でmroongaのRPMパッケージをインストールした場合には、
Tritonnからmroongaへのテーブルスキーマ書き換えガイド
mroongaのインストールが完了したので、
NGRAMインデックスを作成する場合
Tritonnで言うNGRAMはいわゆるbigramで、
-- Tritonnでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX USING NGRAM `fulltext` (text)
) ENGINE = MyISAM DEFAULT CHARSET utf8;
-- mroongaストレージモードでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenBigram"'
) ENGINE = mroonga DEFAULT CHARSET utf8;
-- mroongaラッパーモード(MyISAM)での定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenBigram"'
) ENGINE = mroonga DEFAULT CHARSET utf8 COMMENT='engine "MyISAM"';
TritonnでUSING句を省略するとNGRAMが適用されていたように、
-- mroongaストレージモードでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text)
) ENGINE = mroonga DEFAULT CHARSET utf8;
-- mroongaラッパーモード(MyISAM)での定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text)
) ENGINE = mroonga DEFAULT CHARSET utf8 COMMENT='engine "MyISAM"';
そして、
また、
単語(MeCab)インデックスを作成する場合
形態素解析エンジンであるMeCabも、
-- Tritonnでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX USING MECAB (text)
) ENGINE = MyISAM DEFAULT CHARSET utf8;
-- mroongaストレージモードでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenMecab"'
) ENGINE = mroonga DEFAULT CHARSET utf8;
-- mroongaラッパーモード(MyISAM)での定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenMecab"'
) ENGINE = mroonga DEFAULT CHARSET=utf8 COMMENT='engine "MyISAM"';
DELIMITEDインデックスを作成する場合
空白で区切られた文字列単位でインデックスを作成します。これはスペース区切りで単語を入れて利用する、
-- Tritonnでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX USING DELIMITED (text)
) ENGINE = MyISAM DEFAULT CHARSET utf8;
-- mroongaストレージモードでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenDelimit"'
) ENGINE = mroonga DEFAULT CHARSET utf8;
-- mroongaラッパーモード(MyISAM)での定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenDelimit"'
) ENGINE = mroonga DEFAULT CHARSET utf8 COMMENT='engine "MyISAM"';
MySQLの素のfulltext indexを作成する(sennaインデックスを使用しない)場合
mroongaはTritonnのようなMyISAMに手を入れることは行っていないため、
-- Tritonnでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX USING NO SENNA (text)
) ENGINE = MyISAM DEFAULT CHARSET utf8;
-- mroonga時代の定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
FULLTEXT INDEX (text)
) ENGINE = MyISAM DEFAULT CHARSET utf8;
マルチセクション機能の使用/不使用(tritonn-1.0.4より対応)
mroongaではスキーマで定義せずとも、
-- Tritonnでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
text2 TEXT NOT NULL,
FULLTEXT INDEX USING MECAB, SECTIONALIZE (text, text2)
) ENGINE = MyISAM DEFAULT CHARSET utf8;
-- mroongaストレージモードでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
text2 TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text, text2) COMMENT 'parser "TokenMecab"'
) ENGINE = mroonga DEFAULT CHARSET utf8;
-- mroongaラッパーモード(MyISAM)での定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
text2 TEXT NOT NULL,
FULLTEXT INDEX `fulltext` (text, text2) COMMENT 'parser "TokenMecab"'
) ENGINE = mroonga DEFAULT CHARSET utf8 COMMENT='engine "MyISAM"';
正規化機能の使用/不使用
TritonnではNO NORMALIZE
とすることで、COLLATE=utf8_
としても良いでしょう。
-- Tritonnでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT NOT NULL,
text2 TEXT NOT NULL,
FULLTEXT INDEX USING NGRAM, NO NORMALIZE (text, text2)
) ENGINE = MyISAM DEFAULT CHARSET utf8;
-- mroongaストレージモードでの定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT utf8_bin NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenBigram"'
) ENGINE = mroonga DEFAULT CHARSET utf8;
-- mroongaラッパーモード(MyISAM)での定義
CREATE TABLE test (
id INTEGER AUTO_INCREMENT,
PRIMARY KEY (id),
text TEXT utf8_bin NOT NULL,
FULLTEXT INDEX `fulltext` (text) COMMENT 'parser "TokenBigram"'
) ENGINE = mroonga DEFAULT CHARSET utf8 COMMENT='engine "MyISAM"';
以上、
mroongaはTokenMecab, TokenNgramだけでなく合計約20種類のトークナイザを利用できます。詳細は次の記事が参考になります。
- groonga/
mroongaのトークナイザー(tokenizer)の挙動を追ってみる - 9.
2. 検索 ? groonga v3. 0.6ドキュメント - 3.
3.2. ラッパーモード ? mroonga v3. 06 documentation
Tritonnからmroongaへの全文検索クエリ書き換えガイド
Tritonnのユーザガイドにてまとめられている検索クエリは基本的にmroongaでもそのまま使えます。ただ一部異なる点があるため、
基本的な検索
このパターンのクエリは完全互換があり、
SELECT * FROM tbl WHERE MATCH(col) AGAINST("検索キーワード");
boolean modeの利用(演算子の利用)
Tritonnと同様に、
- AND検索
キーワードAを含み、
かつキーワードBを含む文書の検索 - OR検索
キーワードAまたはキーワードBを含む文書の検索
AGAINST句の括弧の末尾にIN BOOLEAN MODE
とつけることで演算子が利用できるようになります。例えば
SELECT * FROM t1 WHERE MATCH(document) AGAINST("+東京 +神奈川 -埼玉" IN BOOLEAN MODE);
Tritonnでは解釈のできない演算子や不正バイト文字列が渡された場合には、()~+><-*
記号を含む文字列を検索すると次のようなエラーが起きます。
ERROR 1064 (42000): failed to parse fulltext search keyword: <<<foo>>>: <Syntax error! (<foo>)>
演算子に使われる記号を含む検索を行う方法を紹介します。ポイントとしては、
-- mroongaで<foo>という検索をする例(動かない)
SELECT * FROM t1 WHERE MATCH(document) AGAINST("+<foo>" IN BOOLEAN MODE);
-- mroongaで<foo>という検索をする例(動かない)
SELECT * FROM t1 WHERE MATCH(document) AGAINST("+'<foo>'" IN BOOLEAN MODE);
-- mroongaで<foo>という検索をする例(動く)
SELECT * FROM t1 WHERE MATCH(document) AGAINST("+\"<foo>\"" IN BOOLEAN MODE);
-- mroongaで<foo>という検索をする例(動く)
SELECT * FROM t1 WHERE MATCH(document) AGAINST('+"<foo>"' IN BOOLEAN MODE);
さらに、\
」
-- mroongaで"foo"という検索をする例(動かない)
SELECT * FROM t1 WHERE MATCH(document) AGAINST("+"foo"" IN BOOLEAN MODE);
-- mroongaで"foo"という検索をする例(動かない)
SELECT * FROM t1 WHERE MATCH(document) AGAINST("+'\"foo\"'" IN BOOLEAN MODE);
-- mroongaで"foo"という検索をする例(動かない)
SELECT * FROM t1 WHERE MATCH(document) AGAINST('+"\"foo\""' IN BOOLEAN MODE);
-- mroongaで"foo"という検索をする例(動く)
SELECT * FROM t1 WHERE MATCH(document) AGAINST('+"\\"foo\\""' IN BOOLEAN MODE);
律儀にエスケープ処理する実装もできますが、
なお、
mroongaのプラグマ・演算子対応状況
TritonnではSennaのクエリーが使えますが、
プラグマの対応状況
Dプラグマ・
-- 東京 と アジア料理 を共に含むレコードを検索(Tritonn)
SELECT * FROM t1 WHERE MATCH(document) AGAINST("*D+ 東京 アジア料理" IN BOOLEAN MODE);
-- 東京 と アジア料理 を共に含むレコードを検索(mroonga)
SELECT * FROM t1 WHERE MATCH(document) AGAINST('*D+ 東京 アジア料理' IN BOOLEAN MODE);
演算子を含むワードの検索をする場合には次のようなクエリになります。検索語をダブルクォートで囲う必要があるため、
-- 東京 と <アジア料理> を共に含むレコードを検索(mroonga)
SELECT * FROM t1 WHERE MATCH(document) AGAINST('*D+ 東京 "<アジア料理>"' IN BOOLEAN MODE);
OR検索は次のように使います。
-- 東京 または アジア料理 を含むレコードを検索(Tritonn)
SELECT * FROM t1 WHERE MATCH(document) AGAINST("*DOR 東京 アジア料理" IN BOOLEAN MODE);
-- 東京 または アジア料理 を含むレコードを検索(mroonga)
SELECT * FROM t1 WHERE MATCH(document) AGAINST('*DOR 東京 アジア料理' IN BOOLEAN MODE);
演算子の対応状況
10種類の演算子のうち、
- +単語
- -単語
- OR 単語
- (単語・
演算子群) - 単語*
- "単語"
ただし、
- ~単語
- 単語
- *S[数値]"文字列"
- *N[数値]"文字列"
kwic関数
Tritonnのkwic関数は、
2ind機能(2インデックス同時使用機能)
Tritonnでは不要なデータアクセスを省略するための2indパッチが提供されていました。mroongaではこれが標準となり、
mroongaのプロダクション環境への投入
以前の記事で紹介した
第2段階
- MySQL 5.
6スレーブをの配下に、 MySQL 5. 6の孫スレーブを作る - 準同期レプリケーションの設定をMySQL 5.
6のレプリケーションマスタ・ スレーブに行う - 参照クエリ稼働試験として、
影響範囲と時間を限定した上で部分的にプロダクション環境へ投入する - プロダクション環境からの参照クエリを、
MySQL 5. 6スレーブに完全に切り替える
第3段階
- 更新系クエリを止めるため、
サイトをメンテナンス画面に切り替えてオフラインにする - MySQL 5.
6にバイナリログがすべて到達していることを確認した後に、 レプリケーションから切り離す - MySQL 5.
6のレプリケーションマスタに更新クエリが直接発行されるアプリケーションをデプロイする - メンテナンス画面を解除する
さて、
なお、set global read_
というクエリを発行しました。また、
振り返ってみると、
- トークナイザやノーマライザの組み合わせによる検索ヒット数の違いと最適な組み合わせの検討
- 検索クエリの動作確認のため、
アクセスログから抜き出したURL情報を用いて試験環境で走らせても落ちないことを確認 - MySQL 5.
6で改善されたオプティマイザにより、 逆に遅くなったクエリの調整 - MySQL 5.
6でより厳格となったSQLMODEへの追従
例えば3つめの遅くなったクエリの調整は事前の洗い出しが大変手間なため、
こういった大規模な切り替えにはリスクは伴いますが、
Tritonnからmroongaへ移行する際の要注意ポイント
移行中にハマった要注意箇所は次の通りです。
- 文字のエスケープ
Tritonnではこれまで緩い処理であったところが厳格になったため、
先ほどの 「Tritonnからmroongaへの全文検索クエリ書き換えガイド」 にて紹介したような、 ダブルクォートで囲うだけでなく必要に応じてエスケープする処理が必要になりました。 - TokenMecabを用いた際のハマリポイント
記号や半角数字等が4,097文字以上連続すると、
トークナイズできないためにINSERT時には無視され、 warningとなります。 - メモリチューニング
MySQL 5.
0時代から使っているmymemcheckを引き続き利用しました。mroongaテーブルのデータは空でもFULLTEXTインデックスを作成しただけで /var/
以下の*.mrnファイル群は相当な容量を使いますので、lib/ mysql 空きメモリを多めに確保しておきましょう。
mroongaのトラブルシューティング
運用中に遭遇したトラブルシューティングをまとめておきます。
- API version for STORAGE ENGINE plugin is too different
mroongaで
「API version for STORAGE ENGINE plugin is too different」 が参考になります。というエラーが起きたときの対処法 - .mrnファイルがlock failedとなる
- ERROR 1005 (HY000): already used name was assigned
存在しないはずのテーブルが作れない。その場合にはmroonga内の管理データに異常が起きていると推測できます。mroongaで
「ERROR 1005 (HY000): already used name was assigned」 が参考になります。エラーが起きた際の復旧手順
まとめ
Tritonnからmroongaへの移行プロジェクト、
なお、
近日開催のMySQL Casual Talks vol.
追記:
Tritonnからmroongaへの移行プロジェクトのご声援ありがとうございました。それではまた、
次回予告
次回はいよいよ最終回です。