2015年3月10日火曜日

MySQL 5.7.6のInnoDB日本語全文検索 MeCab Plugin

MySQL :: MySQL 5.7 Reference Manual :: 12.9.9 InnoDB MeCab Full-Text Parser Plugin の内容のおさらい。

まず、基本的なライブラリーと辞書は(この記事を書いている時点では).tar.gzバイナリーに同梱されているっぽいのでそちらを使う。Oracle公式のyumリポジトリー からインストールできるrpmには含まれていないように見えるので、その場合は別途突っ込まないといけないはずだけど、libpluginmecab.soが何かにダイナミックリンクしているわけではないので、辞書だけ取ってきてmecabrcに設定すればいけるような気がする。詳しく調べてない。


この環境はバイナリーの.tar.gzを取ってきて、/usr/local/mysqlに展開したとして、


$ ll /usr/local/mysql/lib/plugin/*mecab*
-rwxr-xr-x 1 root root 3988451 Feb 10 20:28 /usr/local/mysql/lib/plugin/libpluginmecab.so

$ ll -R /usr/local/mysql/lib/mecab
/usr/local/mysql/lib/mecab/:
total 8
drwxr-xr-x 5 root root 4096 Feb 17 10:54 dic
drwxr-xr-x 2 root root 4096 Feb 23 21:59 etc
..

plugin_dirにあたるlib/pluginにlibpluginmecab.soが、その他InnoDB MeCab Pluginに必要な辞書(dic)とか設定ファイル(etc)をおさめたディレクトリがlib/mecabにある。

続いてmy.cnfをゴニョる。


$ vim /etc/my.cnf
..
[mysqld]
loose-mecab-rc-file= /usr/local/mysql/lib/mecab/etc/mecabrc
innodb_ft_min_token_size= 1
..

mecab-rc-fileはlib/mecab/etc/mecabrcのパスを絶対パスで[mysqld]セクションに記述する。loose-接頭辞をつけておかないとMySQLが起動しなくなるので注意(INSTALL PLUGIN前にこのオプションを渡そうとすると、"unknown option"って言われてmysqldが起動してくれない)

参考: MySQL の unknown option エラーはオプションに loose- プレフィックスをつけると回避できる - かみぽわーる

innodb_ft_min_token_sizeはこのサイズより小さい文字列はトークンにしないというオプションだが、暗黙のデフォルトは3。英語で"a"とか"to"とかそういう頻出単語をトークナイズしないようにするためと書いてある。CJKでは1にセットしてね、とも。

my.cnfの次はmecabrc(↑のmy.cnfに記述したパスにあるもの)をゴニョる。



$ vim /usr/local/mysql/lib/mecab/etc/mecabrc
..
dicdir =  /usr/local/mysql/lib/mecab/dic/ipadic_utf-8

dicdirに、使いたい辞書の入っているディレクトリを指定する。


# ll lib/mecab/dic/
total 12
drwxr-xr-x 2 root root 4096 Feb 17 10:54 ipadic_euc-jp
drwxr-xr-x 2 root root 4096 Feb 17 10:54 ipadic_sjis
drwxr-xr-x 2 root root 4096 Feb 17 10:54 ipadic_utf-8

5.7.6現在、euc-jp, sjis, utf-8の3つが入ってる。5.7.7のリリースノート を見ると eucjpms, cp932, utf8mb4に対応したよ! と書いてあって、マニュアルのページには同じ辞書を使うよ、と書いてある。

この状態で起動してやると、


$ bin/mysqld_safe &
$ less data/error.log
..
2015-03-04T02:30:12.628925Z 0 [Warning] unknown variable 'loose-mecab-rc-file=/usr/local/mysql/lib/mecab/etc/mecabrc'
..

まだINSTALL PLUGINしてないので、unknown variableとして扱われる。


mysql> INSTALL PLUGIN mecab SONAME 'libpluginmecab.so';
Query OK, 0 rows affected (0.20 sec)

$ tail data/error.log
2015-03-04T03:21:46.236551Z 2 [Note] Mecab: Trying createModel(--rcfile=/usr/local/mysql/lib/mecab/etc/mecabrc)
2015-03-04T03:21:46.436600Z 2 [Note] Mecab: Loaded dictionary charset is utf-8

認識したぽい。


mysql> SHOW CREATE TABLE articles;
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                                      |
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| articles | CREATE TABLE `articles` (
  `seq` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `title` text,
  `content` longtext,
  `timestamp` datetime DEFAULT NULL,
  UNIQUE KEY `seq` (`seq`),
  KEY `timestamp` (`timestamp`)
) ENGINE=InnoDB AUTO_INCREMENT=1914065 DEFAULT CHARSET=utf8 |
+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

こんな感じのWikipediaのデータを食わせたテーブルに


mysql> ALTER TABLE articles ADD FULLTEXT KEY (title, content) WITH PARSER MeCab;
Query OK, 0 rows affected (1 hour 2 min 12.62 sec)
Records: 0  Duplicates: 0  Warnings: 0

$ tail data/error.log
2015-03-04T03:39:02.484153Z 0 [ERROR] Mecab:
2015-03-04T03:40:06.299126Z 0 [ERROR] Mecab:
2015-03-04T03:45:13.146195Z 0 [ERROR] Mecab:
2015-03-04T03:56:10.793337Z 0 [ERROR] Mecab:
2015-03-04T03:56:21.149414Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.385256Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.421553Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.470875Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.600808Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.632828Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.941143Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.969369Z 0 [ERROR] Mecab:
2015-03-04T03:59:34.986076Z 0 [ERROR] Mecab:
2015-03-04T03:59:35.056543Z 0 [ERROR] Mecab:
2015-03-04T03:59:35.352131Z 0 [ERROR] Mecab:
2015-03-04T03:59:36.754206Z 0 [ERROR] Mecab:

なんかダイイングメッセージみたいに不明なエラー吐いてるけど(Mroongaと比較した感じでは"too long sentence"エラーのはず)取り敢えず無視して、

【2015/03/18 13:33】
バグレポートしましたが、5.7.8でFixedとのこと。
MySQL Bugs: #76164: InnoDB FTS with MeCab parser prints empty error message


mysql> SELECT COUNT(*) FROM articles WHERE match(title, content) against('データベース');
+----------+
| COUNT(*) |
+----------+
|     3013 |
+----------+
1 row in set (0.01 sec)

mysql> explain SELECT COUNT(*) FROM articles WHERE match(title, content) against ('データベース');
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                        |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
|  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
1 row in set, 1 warning (0.01 sec)

mysql> SHOW WARNINGS;
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                               |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select count(0) AS `COUNT(*)` from `wikipedia`.`articles` where (match `wikipedia`.`articles`.`title`,`wikipedia`.`articles`.`content` against ('データベース'))       |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.02 sec)

引けてるっぽい。


mysql> explain SELECT * FROM articles WHERE match(title, content) against ('データベース') LIMIT 10;
+----+-------------+----------+------------+----------+---------------+-------+---------+-------+------+----------+-------------------------------------------+
| id | select_type | table    | partitions | type     | possible_keys | key   | key_len | ref   | rows | filtered | Extra                                     |
+----+-------------+----------+------------+----------+---------------+-------+---------+-------+------+----------+-------------------------------------------+
|  1 | SIMPLE      | articles | NULL       | fulltext | title         | title | 0       | const |    1 |   100.00 | Using where; Ft_hints: sorted, limit = 10 |
+----+-------------+----------+------------+----------+---------------+-------+---------+-------+------+----------+-------------------------------------------+
1 row in set, 1 warning (0.02 sec)

mysql> SELECT * FROM articles WHERE match(title, content) against ('データベース') LIMIT 10;
..
10 rows in set (0.01 sec)

バッファプールに載ってて単一条件ならまあまあ動くんだけど


mysql> SELECT * FROM articles WHERE match(title, content) against ('データベース') ORDER BY timestamp DESC LIMIT 10;
..
10 rows in set (0.62 sec)

スコア以外のところでソートするとやっぱり死ねるねぇ。。


【2015/03/10 18:57】
Ngramの方も書きました => 日々の覚書: MySQL 5.7.6のInnoDB日本語全文検索 ngram

0 件のコメント :

コメントを投稿