CreateField Blog

オープンソースを使って個人でWebサービスを開発・運営していたブログ

PostgreSQLの日本語対応全文検索モジュールpg_bigmとPGroongaを検証してみた

はじめに

最近、Web系のエンジニアに転職して、Railsをよく触っています。 Rails界隈では、HerokuかActiveRecordの関係かよくわかりませんがPostgreSQLが利用されていることが多いような気がします。

これまで個人的に全文検索のWebサービスを開発するためにGroongaとよく戯れていたのですが、最近はなかなか戯れることができていません。

最近になってRailsとPostgreSQLを触りはじめたという状況ですが、先日、PostgreSQLでGroongaが使えるPGroonga 0.20がリリースされたようです。

PostgreSQLで簡単に日本語対応で高速な全文検索が使えるようになるなんて素晴らしいじゃないですか。

最近はRailsの使い方ばっかり調べていて、若干知識欲が満たされない感があったので、PostgreSQLの知識向上がてら、PGroongaと、PGroongaと同じく日本語対応の全文検索モジュールであるpg_bigmの性能を検証してみました。

検証環境

  • ハードウェア
CPU メモリ ディスク
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz 1CPU 4Core 32GB HDD 2TB


* ソフトウェア

ソフトウェア バージョン
PostgreSQL 9.4.0
PGroonga 0.3.0(2015/2/1時点最新マスター)
Groonga 4.0.8
pg_bigm 1.1
CentOS 6.5

テーブル定義

  • PGroonga
CREATE TABLE text (
  id integer,
  title text,
  text text
);
CREATE INDEX pgroonga_index ON text USING pgroonga (text);
  • pg_bigm
CREATE TABLE text (
  id integer,
  title text,
  text text
);
CREATE INDEX pg_bigm_index ON text USING gin (text gin_bigm_ops);

更新手順

  • Wikipedia(ja)のデータ1万件(XMLの状態で169MiB)を1件ずつスクリプトでシーケンシャルにinsert(バルクインサートはしない)
  • データ挿入中にWALログのcheckpointが走らないようにpostgresql.confに以下の設定を行う

  • postgresql.conf

checkpoint_segments = 64
checkpoint_timeout = 1h
  • 更新SQL
INSERT INTO text VALUES (1, "title", "text");
  • 最後に1回だけcheckpointコマンド実行
checkpoint;

検索手順

  • Wikipedia(ja)の日本語のみのカテゴリのうち、上記のデータで1件以上ヒットするものをランダムに500件抽出したものを1件ずつSELECTで全文検索を実行
  • 純粋なindexscanの速度を比較するため、pg_bigmとPGroongaではSELECTを発行する前に以下のようにしてシーケンシャルスキャンを無効
SET enable_seqscan TO off;
  • 以下のコマンドでキャッシュをクリアし、プロセスを再起動してから検索実行
echo 3 > /proc/sys/vm/drop_caches
  • 検索SQL(pg_bigm,indexなし)
SELECT COUNT(*) as cnt FROM text WHERE text LIKE '%カテゴリー%';
  • 検索SQL(PGroonga)
SELECT COUNT(*) as cnt FROM text WHERE text %% 'カテゴリー';

更新時間

indexなし pg_bigm pgroonga
1万件トータル 102.618sec 238.485 sec 191.193 sec
平均 0.0102 sec 0.0238 sec 0.0191 sec

更新時間はわずかにpg_bigmよりもPGroongaの方がやや速かったです。

f:id:naoa_y:20150203094238p:plain

検索時間

indexなし pg_bigm pgroonga
500件トータル 664.393 sec 38.764 sec 11.604 sec
平均 1.328 sec 0.0775 sec 0.0232 sec

検索時間はpg_bigmよりもPGroongaの方が3倍以上速かったです。indexなしに比べれば、pg_bigmもかなり速いことがわかります。

f:id:naoa_y:20150203094201p:plain

  • EXPLAIN例
pgroonga=# EXPLAIN ANALYZE SELECT COUNT(*) as cnt FROM text WHERE text %% 'テレビアニメ';
                                                              QUERY PLAN

---------------------------------------------------------------------------------------------------------------
------------------------
 Aggregate  (cost=487.81..487.82 rows=1 width=0) (actual time=532.997..532.997 rows=1 loops=1)
   ->  Bitmap Heap Scan on text  (cost=43.21..475.21 rows=5040 width=0) (actual time=454.347..532.810 rows=359
loops=1)
         Recheck Cond: (text %% 'テレビアニメ'::text)
         Heap Blocks: exact=173
         ->  Bitmap Index Scan on pgroonga_index  (cost=0.00..41.95 rows=5040 width=0) (actual time=454.102..45
4.102 rows=359 loops=1)
               Index Cond: (text %% 'テレビアニメ'::text)
 Planning time: 415.373 ms
 Execution time: 538.047 ms
(8 rows)
pg_bigm=# EXPLAIN ANALYZE SELECT COUNT(*) as cnt FROM text WHERE text LIKE '%テレビアニメ%';
                                                             QUERY PLAN

---------------------------------------------------------------------------------------------------------------
---------------------
 Aggregate  (cost=108.02..108.03 rows=1 width=0) (actual time=1036.064..1036.065 rows=1 loops=1)
   ->  Bitmap Heap Scan on text  (cost=104.01..108.02 rows=1 width=0) (actual time=165.421..1035.735 rows=359 l
oops=1)
         Recheck Cond: (text ~~ '%テレビアニメ%'::text)
         Rows Removed by Index Recheck: 321
         Heap Blocks: exact=214
         ->  Bitmap Index Scan on pg_bigm_index  (cost=0.00..104.01 rows=1 width=0) (actual time=121.101..121.1
01 rows=680 loops=1)
               Index Cond: (text ~~ '%テレビアニメ%'::text)
 Planning time: 115.805 ms
 Execution time: 1048.345 ms
(9 rows)

この検索クエリの例ではPGroongaの方がpg_bigmよりも2倍ぐらい速いですね。

  • 開発者のコメント

サイズ

indexなし pg_bigm pgroonga
92MiB 650MiB 672MiB*1

サイズは、PGroongaの方がpg_bigmよりも少しだけ大きくなっています。 現状のPGroongaでは、全文インデックス以外にデータがPostgreSQLだけでなくGroongaのストレージにも格納されており、サイズがやや大きくなっています。 現在の実装では、Groonga側のデータは利用されていません。これについては今後、圧縮等によりいくらか改善されるかもしれません。

f:id:naoa_y:20150203094301p:plain

おわりに

上記のように、PGroongaは高速な日本語全文検索機能を簡単に追加することができて非常に便利です。 もし、PGroongaのExtensionがHerokuのPostgreSQLアドオンに配備されるなんてことになれば、Herokuで簡単に 日本語対応の高速な全文検索ができるようになって素敵ですね!

期待しています!

参考

https://github.com/pgroonga/pgroonga

http://pgbigm.sourceforge.jp/pg_bigm-1-1.html

*1:このサイズはスパースが考慮されておらず、領域を確保した全サイズです。実際に使っているのは518MiBぐらい。参考