Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

20160929_InnoDBの全文検索を使ってみた by 株式会社インサイトテクノロジー 中村範夫

601 views

Published on

2016-09-29(木)にMySQLとPostgreSQLと日本語全文検索3で発表した内容となります!

Published in: Technology
  • Be the first to comment

20160929_InnoDBの全文検索を使ってみた by 株式会社インサイトテクノロジー 中村範夫

  1. 1. InnoDBの全文検索を使ってみた
  2. 2. 自己紹介 2 • 中村範夫(なかむらのりお) • 2013年(株)インサイトテクノロジー 入社 • プロダクトコンサルティング事業部所属 • 大阪で10年以上システム開発に従事した後に現社へ • 扱ったデータベース経験年数では、Oracle、SQL Server、MySQLの順
  3. 3. アジェンダ 3 • InnoDBの全文検索を使い始めた経緯 • InnoDBの全文検索を試してみる • INNODB_FT_INDEX_TABLEを使って分析してみる • まとめ
  4. 4. InnoDBの全文検索を使い始めた経緯 4 • 社内製品開発 • SNMPから上がってくる膨大な警告メッセージを全文検索したい • 全文検索だけじゃなくて分析したい • MySQL 5.7 登場! – InnoDB全文検索の日本語対応 経緯
  5. 5. MySQL InnoDBを使っての全文検索 5 • 2種類の わかち分かち書き をサポート • N-gram(デフォルト) • MeCab(形態素解析)※プラグインのインストールが必要 • 分かち書きされた結果もデータディクショナリで簡単参照 ( INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE) MySQL InnoDB全文検索の特徴 CREATE TABLE ALT_TAB( FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, HOST_NAME VARCHAR(20), MSG VARCHAR(400), INS_TIME datetime, FULLTEXT INDEX ftsidx (MSG) WITH PARSER mecab )ENGINE=InnoDB CHARACTER SET utf8;
  6. 6. 対象データを分かち書きした結果( N-Gram と Mecab の比較) 6 N-Gramは設定した(デフォルト2)文字数でのみ分割するのに対し、 Mecabは単語毎に分割し、辞書サイズも節約でき性能もよさそう 例)192.10.9.26: IF-MIB/ifHCInOctets_psec(1)=400 ※SNMPエラーメッセージ
  7. 7. まずは実際の検索性能をMecabで実施 7 mysql> select SQL_NO_CACHE count(*) from ALT_100 -> where match (MSG) against('使用時間' IN BOOLEAN MODE); +------------+ | count(MSG) | +------------+ | 9465 | +------------+ 1 row in set (0.73 sec) mysql> select SQL_NO_CACHE count(*) from ALT_1000 -> where match (MSG) against('使用時間' IN BOOLEAN MODE); +------------+ | count(MSG) | +------------+ | 94650 | +------------+ 1 row in set (30.70 sec) 検索処理性能は、およそ100万件で1秒程度、1000万件で30秒程度 ※母数に対し1%程度のデータがヒットする検索条件で実施 データ インデックス 100万件 500 57 1000万件 4200 473 総件数 InnoDB Mecab (MB)
  8. 8. 分かち書きした結果のMySQLでの見え方 8 mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE ORDER BY doc_id, position LIMIT 10; +-------------------+--------------+-------------+-----------+--------+----------+ | WORD | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION | +-------------------+--------------+-------------+-----------+--------+----------+ | 192 | 1 | 22785 | 21846 | 1 | 0 | | 10 | 1 | 19652 | 18931 | 1 | 4 | | 100 | 1 | 383964 | 11774 | 1 | 7 | | 200 | 1 | 383935 | 10885 | 1 | 10 | | ip | 1 | 29784 | 21845 | 1 | 14 | | mib | 1 | 22785 | 21846 | 1 | 18 | | ipifstatsinoctets | 1 | 383899 | 18412 | 1 | 22 | | psec | 1 | 22785 | 21846 | 1 | 40 | | )= | 1 | 21971 | 21846 | 1 | 48 | | 15 | 1 | 374036 | 21459 | 1 | 50 | +-------------------+--------------+-------------+-----------+--------+----------+ 10 rows in set (18.39 sec) 分かち書き結果(転置インデックスの中身)の確認 分析にも使えそうな予感!
  9. 9. 分かち書きの結果データディクショナリと実テーブルとの関係 9 DOC_IDが外部キーとして使える! ※一意な識別子として、FTS_DOC_ID列を明示的に作成することを推奨とマニュアルにもあります。
  10. 10. 分析用SQL:時間を指定して上位ワードを取得 10 mysql> -- 時間を指定して特定の日のMSGに含まれるワードの一覧上位30件を取得 mysql> SELECT a.WORD, -> COUNT(*) -> FROM INNODB_FT_INDEX_TABLE AS a -- 分かち結果テーブル -> INNER JOIN ALT_TAB AS b -- ALTテーブル -> ON a.DOC_ID = b.FTS_DOC_ID -> WHERE b.INS_TIME BETWEEN '2016-05-23 01:00:00' AND '2016-05-23 01:59:59' -> GROUP BY b.HOST_NAME, a.word -> ORDER BY COUNT(*) DESC -> LIMIT 30; +--------------+----------+ | WORD | COUNT(*) | +--------------+----------+ | fan | 92 | | threshold | 72 | | over | 72 | | psec | 72 | | 192 | 72 | | 10 | 71 | | 168 | 71 | | over | 71 | | psec | 71 | | threshold | 67 | | 90 | 67 | | over | 67 | | psec | 67 | IPアドレスが分割されて残念な結果になっているの で、辞書に登録してみよう!
  11. 11. Mecab 辞書の登録方法 11 [root@Nlocalhost ~]# cd /usr/lib64/mysql/mecab/dic/ipadic_utf-8 [root@localhost ipadic_utf-8]# /usr/libexec/mecab/mecab-dict-index -d ./ -u user.dic -f utf8 -t utf8 user-dic.csv reading user-dic.csv ... 12 emitting double-array: 100% |###########################################| done! [root@localhost ipadic_utf-8]# ls -ltr 合計 51840 -rw-r--r--. 1 root root 6241 5月 11 20:36 2016 rewrite.def -rw-r--r--. 1 root root 49199027 5月 11 20:36 2016 sys.dic -rw-r--r--. 1 root root 5690 5月 11 20:36 2016 unk.dic -rw-r--r--. 1 root root 1477 5月 11 20:36 2016 pos-id.def -rw-r--r--. 1 root root 55910 5月 11 20:36 2016 left-id.def -rw-r--r--. 1 root root 693 5月 11 20:36 2016 dicrc -rw-r--r--. 1 root root 262496 5月 11 20:36 2016 char.bin -rw-r--r--. 1 root root 55910 5月 11 20:36 2016 right-id.def -rw-r--r--. 1 root root 3463716 5月 11 20:36 2016 matrix.bin -rw-r--r--. 1 root root 1073 5月 11 20:36 2016 user-dic.csv -rw-r--r--. 1 root root 4125 5月 20 10:32 2016 user.dic mecab-dict-index コマンでcsvファイルを元に ユーザー辞書を作成 できあがった、user.dicをmecabrcファイルに追記 mecab-dict-indexコマンドで辞書を作成する 表層形 左文脈ID 右文脈ID コスト 品詞 品詞細分類1 品詞細分類2 品詞細分類3 活用形 活用型 原形 読み 発音 172.16.32.1 * * 10000 名詞 一般 * * * * 172.16.32.1 172.16.32.1 172.16.32.1 icmpInEchoReps * * 10000 名詞 一般 * * * * icmpInEchoReps icmpInEchoReps icmpInEchoReps icmpInEchos * * 10000 名詞 一般 * * * * icmpInEchos icmpInEchos icmpInEchos 追加する辞書フォーマット サンプル
  12. 12. Mecab 辞書登録後の分かち書き結果 12 辞書登録前はIPアドレスが分割される 辞書登録後はIPアドレスが分割されない
  13. 13. 分析用SQL:時間を指定して上位ワードを取得 13 mysql> -- 時間を指定して特定の日のMSGに含まれるワードの一覧上位30件を取得 mysql> SELECT a.WORD, -> COUNT(*) -> FROM INNODB_FT_INDEX_TABLE AS a -- 分かち結果テーブル -> INNER JOIN ALT_TAB AS b -- ALTテーブル -> ON a.DOC_ID = b.FTS_DOC_ID -> WHERE b.INS_TIME BETWEEN '2016-05-23 01:00:00' AND '2016-05-23 01:59:59' -> GROUP BY b.HOST_NAME, a.word -> ORDER BY COUNT(*) DESC -> LIMIT 30; +-------------------+----------+ | WORD | COUNT(*) | +-------------------+----------+ | fan | 572 | | threshold | 351 | | hardware_resource | 351 | | 192.168.10.83 | 351 | | over | 351 | | tach | 286 | | 6500 | 78 | | 5900 | 65 | | 6700 | 52 | IPアドレスが分割されなくなったが、意味のない数 値のみ(しきい値の実数等)が邪魔
  14. 14. 除外リスト(STOPWORD)について 14 分かち書きさせたくない除外リスト(STOPWORD)に登録する方法 /etc/my.cnf ファイルに以下の設定を追記 # for stop words innodb_ft_server_stopword_table=‘test/STOP_WORDS‘ 自動判定したしきい値等の実数を除外したい場合(1~1000の整数値等)、MySQLの全文検索機能に は、STOPWORDテーブルが用意されている。予め予測できる数値等はここに登録しておくことで、 分かちの除外対象とすることができる。次に紹介するSQLの検索側で除外する方法に比べ、INSERT時 に除外できるので、分かち結果テーブルの件数を抑えられるメリットがある。以下その方法。 -- STOPWORDテーブルに単語登録 INSERT INTO test.STOP_WORDS VALUES (1),(2),(3),…(1000);
  15. 15. 正規表現によりSQLで数値のみや記号を除外する 15 -- MySQLでは正規表現が使えるので記号のみ、数値のみのワードは除外する SELECT a.WORD as WORD , count(*) as cnt FROM TFM_ALT_20160525_FTIDX AS a WHERE a.WORD NOT REGEXP ‘^[!-/:-@[-`{-~0-9]+$’ -- 数字のみ、記号のみは除外 GROUP BY a.WORD ORDER BY count(*) DESC LIMIT 100 自動判定したしきい値等の実数は、動的に変更するため、全てを予め辞 書に登録しておくことは不可能なので、SQL側で除外した
  16. 16. 分析用SQL:前日にはなかったエラーワード一覧 16 mysql> SELECT x.WORD -> , x.cnt as day25 -> FROM (-- 当日の頻出上位100件のワード一覧 -> SELECT a.WORD as WORD, count(*) as cnt -> FROM FTIDX_20160525 AS a -- INNODB_FT_INDEX_TABLEを日ごとに物理化したテーブル -> WHERE a.WORD NOT REGEXP ‘^[!-/:-@[-`{-~0-9]+$’ -> GROUP BY a.WORD -> ORDER BY count(*) DESC LIMIT 100) AS x -> LEFT JOIN ( -> -- 前日の頻出上位100件のワード一覧 -> SELECT a.WORD as WORD, count(*) as cnt -> FROM FTIDX_20160524 AS a -- INNODB_FT_INDEX_TABLEを日ごとに物理化したテーブル -> WHERE a.WORD NOT REGEXP ‘^[!-/:-@[-`{-~0-9]+$’ -> GROUP BY a.WORD -> ORDER BY count(*) DESC LIMIT 100) y -> ON x.WORD = y.WORD -> WHERE y.cnt IS NULL -- 前日(day24)には存在しなかった -> ORDER BY x.cnt DESC; +------------------------------+-------+ | WORD | day25 | +------------------------------+-------+ | 172.16.34.89 | 1027 | | 172.16.34.65 | 899 | | 172.16.34.87 | 886 | | ipsystemstatshcinmcastpkts | 700 | | ipsystemstatsinmcastpkts | 700 | | ipsystemstatshcinoctets | 680 | | ipsystemstatsinoctets | 680 | | ipsystemstatshcinmcastoctets | 628 | | ipsystemstatsinmcastoctets | 628 | ・ ・
  17. 17. MySQL InnoDBを使ってみての感想 17 • MySQL InnoDB 全文検索 • Ver5.7以上を使っているなら、日本語全文検索は直ぐに使える • 対象データに見合うトークナイザー(N-gram or MeCab)の選択が重要 • Mecabの場合 • 性能はデータに大きく依存するがそこそこ使える • 転置インデックスのデータディクショナリは有用 • 辞書と STOPWORD の活用により分かち書きの調整が可能 • 使いどころは? • 規模:全体の件数が数100万件程度まで、サイズは数GB~数十GB程度 • 向いていると思うシステム: • 例): グループウェア(社内メール)、チケット管理システム(redmine)等

×