= Mroonga\n(('note:と'))\nPGroonga : subtitle Groongaを使って\nMySQLとPostgreSQLで日本語全文検索 : author 須藤功平 : institution クリアコード : content-source MySQLとPostgreSQLと日本語全文検索 : date 2016-02-09 : allotted-time 25m : theme groonga = Mroonga・PGroonga * Mroonga(むるんが) * (('wait'))MySQLに\n 高速日本語全文検索機能を追加する\n プロダクト * PGroonga(ぴーじーるんが) * (('wait'))PostgreSQLに\n 高速日本語全文検索機能を追加する\n プロダクト = すごい!使いたい! * (('wait'))インストールして! * え。。。組み込みじゃないの。。。\n (('note:(MariaDBにはMroongaは組み込まれています!)')) * (('wait'))パッケージあるから簡単だよ! * クラウドサービスで使えない。。。 * (('wait'))(('note:(クラウドサービスに入っていれば…!)')) * (('note:HerokuのPostgreSQLにPGroonga入れて!とお願いだ!')) = 使いたい!? # blockquote HerokuのPostgreSQLで\n PGroongaを使えるなら\n Herokuを使いたい!\n#herokujp (('tag:center')) ↑と思うならtweet!\n (Herokuの人が観測します。) = 高速? (('tag:center')) ベンチマーク! * 対象:Wikipedia日本語版 * レコード数:約185万件 * データサイズ:約7GB * メモリー4GB・SSD250GB(('note:(ConoHa)')) (('note:FYI https://github.com/groonga/wikipedia-search/issues/4')) (('tag:center')) (('note:(他人のベンチマークは参考程度)'))\n (('note:(検討時はちゃんと実際の環境でベンチマークをとろう!)')) = 速さ:検索1 (('tag:center')) キーワード:テレビアニメ\n (('note:(ヒット数:約2万3千件)')) # RT delimiter = [|] InnoDB ngram | 3m2s InnoDB MeCab | 6m20s Mroonga:((*1*)) | 0.11s pg_bigm | 4s PGroonga:((*2*)) | 0.29s = 速さ:検索2 (('tag:center')) キーワード:データベース\n (('note:(ヒット数:約1万7千件)')) # RT delimiter = [|] InnoDB ngram | 36s InnoDB MeCab:((*1*)) | 0.03s Mroonga:((*2*)) | 0.09s pg_bigm | 2s PGroonga:((*3*)) | 0.17s = 速さ:検索3 (('tag:center')) キーワード:PostgreSQL OR MySQL\n (('note:(ヒット数:約400件)')) # RT delimiter = [|] InnoDB ngram | N/A(エラー) InnoDB MeCab:((*1*)) | 0.005s Mroonga:((*2*)) | 0.028s pg_bigm | 0.185s PGroonga:((*3*)) | 0.063s = 速さ:検索4 (('tag:center')) キーワード:日本\n (('note:(ヒット数:約63万件)')) # RT delimiter = [|] InnoDB ngram | 1.3s InnoDB MeCab | 1.3s Mroonga:((*1*)) | 0.21s pg_bigm:((*2*)) | 0.84s PGroonga | 1s = 速さ:検索まとめ * Mroonga・PGroonga * (('wait'))安定して速い * InnoDB FTS mecab・pg_bigm * (('wait'))ハマれば速い * InnoDB FTS ngram * (('wait'))安定して遅い = 使いたい!? # blockquote HerokuのPostgreSQLで\n PGroongaを使えるなら\n Herokuを使いたい!\n#herokujp (('tag:center')) ↑と思うならtweet!\n (Herokuの人が観測します。) = 速さ:データロード (('tag:center')) 約185万件・約7GB・SSD # RT delimiter = [|] InnoDB ngram | 6m51s InnoDB mecab | 6m22s Mroonga:((*3*)) | 5m45s pg_bigm:((*1*)) | 5m14s PGroonga:((*2*)) | 5m22s (('note:MySQLはbinlog有効、PostgreSQLはWAL有効'))\n (('note:InnoDBはどっちも同じ処理'))\n (('note:pg_bigmとPGroongaもどっちも同じ処理')) = 速さ:インデックス作成 (('tag:center')) 約185万件・約7GB・SSD # RT delimiter = [|] InnoDB ngram | 3h06m58s InnoDB mecab | 2h41m55s Mroonga:((*1*)) | 22m24s pg_bigm | 3h43m23s PGroonga:((*2*)) | 54m34s (('note:MySQLはbinlog有効、PostgreSQLはWAL有効'))\n (('note:バルクインデックス作成'))\n (('note:=データ投入後インデックス作成')) = 速さ:ロードまとめ * (('wait'))データロードは大差ない * (('wait'))インデックス作成は大差 * Mroonga・PGroongaは分単位 * InnoDB・pg_bigmは時間単位 = 使いたい!? # blockquote HerokuのPostgreSQLで\n PGroongaを使えるなら\n Herokuを使いたい!\n#herokujp (('tag:center')) ↑と思うならtweet!\n (Herokuの人が観測します。) = サイズ:データ # RT delimiter = [|] InnoDB ngram | 10GB InnoDB mecab | 10GB Mroonga | 8.2GB pg_bigm:((*2*)) | 5.1GB PGroonga:((*1*)) | 4.3GB (('note:InnoDBはどっちも同じ'))\n (('note:pg_bigmとPGroongaはどっちも同じはずだけど…')) = サイズ:インデックス # RT delimiter = [|] InnoDB ngram | 12GB InnoDB mecab:((*1*)) | 6GB Mroonga:((*1*)) | 6GB pg_bigm:((*3*)) | 7GB PGroonga | 10GB (('note:InnoDBは一時ファイル(何10GB単位)を作る'))\n (('note:PGroongaは元データ(8GB)のコピーもLZ4圧縮して持っている')) = サイズ:まとめ * データサイズ * PostgreSQLは元データより小さめ * InnoDBは元データより大きめ * インデックスサイズ * InnoDB mecabは小さめ\n (('note:(ヒント:形態素解析ベースの方が小さくなる)')) * Mroonga・pg_bigmはN-gramなのにInnoDB mecabと同じくらい = 高速? ベンチマークで\n 確認 = Mroonga・PGroonga * Mroonga(むるんが) * (('wait'))MySQLに\n 高速日本語全文検索機能を追加する\n プロダクト * PGroonga(ぴーじーるんが) * (('wait'))PostgreSQLに\n 高速日本語全文検索機能を追加する\n プロダクト = 実現方法 * Mroonga(むるんが) * (('wait'))MySQLに\n ((*Groonga(ぐるんが)*))を組み込み * PGroonga(ぴーじーるんが) * (('wait'))PostgreSQLに\n ((*Groonga(ぐるんが)*))を組み込み = Groonga * (('wait'))国産の高速全文検索エンジン * 日本語バッチリ * (('wait'))ライブラリーとして使える * 組み込みやすい * (('wait'))マルチスレッド対応\n (('note:(MySQL組み込み時にうれしい)')) * (('wait'))マルチプロセス対応\n (('note:(PostgreSQL組み込み時にうれしい)')) = 組み込み方針 * Groongaをできるだけ活かす * 使い勝手はMySQL・PostgreSQLに寄せる (('wait')) (('tag:center')) ↓\n SQLで使えるGroonga = ポジション # image # src = images/position.svg # relative_height = 100 = SQLで使えるGroonga * Groongaのフル機能は諦める * 速度など譲れない部分はがんばる * その分、使いやすさを重視 * (('wait'))使いやすさ1=\n MySQL・PostgreSQLとなじんでいる * (('wait'))使いやすさ2=\n MySQL・PostgreSQLの不便を解消 = なじみ度:Mroonga (('tag:center')) インデックス作成:MySQLと同じ # coderay sql CREATE TABLE ... ( ..., FULLTEXT INDEX (column) ) ENGINE=Mroonga; = なじみ度:Mroonga (('tag:center')) 全文検索:MySQLと同じ # coderay sql SELECT * FROM ... WHERE MATCH(column) AGAINST('キーワード' IN BOOLEAN MODE); = 不便解消:Mroonga (('tag:center')) デフォルトOR→AND # coderay sql -- ↓AまたはBが含まれていればマッチ AGAINST('A B' IN BOOLEAN MODE); AGAINST('+A +B' IN BOOLEAN MODE); -- ↑↓AとBが含まれていればマッチ -- ↓Mroongaの拡張 AGAINST('*D+ A B' IN BOOLEAN MODE); = 不便解消:Mroonga (('tag:center')) 重み指定 # coderay sql -- titleかcontentにAがあればマッチ -- (便利。PostgreSQLではできない。) MATCH(title, content) AGAINST('A' IN BOOLEAN MODE) -- でもtitleの方を重要視したい! -- ↓Mroongaの拡張 AGAINST('*W1:10,2:1 A' IN BOOLEAN MODE) = 不便解消:Mroonga (('tag:center')) 全文検索+ORDER LIMIT高速化 # coderay sql SELECT * FROM tweets WHERE MATCH(content) AGAINST('...' IN BOOLEAN MODE) ORDER BY timestamp DESC LIMIT 10; = ORDER LIMIT高速化 * なぜ速いか * (('wait'))Groongaでソートし、LIMIT件だけMySQLに返しているから * (('wait'))MySQLよりGroongaでやった方が速い\n (('note:(ヒント:カラムストア)')) = さらにORDER LIMIT高速化 # coderay sql SELECT * FROM tweets WHERE MATCH(content) AGAINST('...' IN BOOLEAN MODE) AND timestamp >= '2016-02-01' -- ↑今月の分だけ対象にしたい ORDER BY timestamp DESC LIMIT 10; = さらにORDER LIMIT高速化 * なぜ速いか * (('wait'))Groongaで((*絞り込んで*))ソートし、\n LIMIT件だけMySQLに返しているから * (('wait'))MySQLよりGroongaでやった方が速い\n (('note:(ヒント:カラムストア)')) * 場合によっては10倍以上高速化 * (('note:http://tech.gmo-media.jp/post/69542751128/mroonga-311-new-optimization')) = PGroonga * Groongaのフル機能は諦める * 速度など譲れない部分はがんばる * その分、使いやすさを重視 * 使いやすさ1=\n MySQL・((*PostgreSQL*))となじんでいる * 使いやすさ2=\n MySQL・((*PostgreSQL*))の不便を解消 = なじみ度:PGroonga (('tag:center')) インデックス作成:\n PostgreSQLと同じ # coderay sql CREATE INDEX name ON texts USING pgroonga (content); = なじみ度:PGroonga (('tag:center')) 全文検索:\n PostgreSQLのtextsearhとほぼ同じ # coderay sql SELECT * FROM ... WHERE column @@ 'キーワード'; = textsearchとの違い (('tag:center')) 構文 # coderay sql -- textsearch -- プログラムのよう '(A & B) | C' -- PGroonga(不便解消) -- Web検索エンジンのよう '(A B) OR C' = 不便解消:PGroonga (('tag:center')) Windows用バイナリーあり * (('wait'))商用ログ管理製品\n 「VVAULT AUDIT」が採用\n (('note:http://vvault.jp/product/vvault-audit/')) * アクセスログに対して\n ユーザー名・パスを全文検索 * (('wait'))決め手:高速・省スペース = 不便解消:PGroonga (('tag:center')) JSONデータを全文検索 # coderay sql CREATE TABLE logs (record jsonb); CREATE INDEX i ON logs USING pgroonga (record); -- ログのどこかに「error」があればマッチ SELECT * FROM logs WHERE record @@ 'string @ "error"'; = JSON全文検索例 (('tag:center')) 以下は全部マッチ # coderay json {"message": "Error!"} {"tags": ["web", "error"]} {"syslog": {"message": "error!"}} = 使いたい!? # blockquote HerokuのPostgreSQLで\n PGroongaを使えるなら\n Herokuを使いたい!\n#herokujp (('tag:center')) ↑と思うならtweet!\n (Herokuの人が観測します。) = まとめ1 * Groonga(ぐるんが) * (('wait'))国産の高速全文検索エンジン * Mroonga(むるんが) * (('wait'))MySQLからGroongaを使える! * PGroonga(ぴーじーるんが) * (('wait'))PostgreSQLからGroongaを使える! = まとめ2 (('tag:center')) 実装方針 * (('wait'))Groongaをできるだけ活かす\n (('note:(例:速度)')) * (('wait'))MySQL/PostgreSQLっぽく使える * (('wait'))MySQL/PostgreSQLをより便利に = 次回予告 * (('wait'))トランザクションは? * (('wait'))クラッシュしたら? * (('wait'))レプリケーションは? * (('wait'))もっと速くならないの?