Mroongan((‘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: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 MySQLn ((‘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は一時ファイル(何十GB単位)を作る’))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のtextsearchとほぼ同じ

# 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'))もっと速くならないの?