pg_bigmとpg_trgmの併存
Fujii先生からのツッコミ
昨日、pg_bigmの検証のエントリを書いた後、作者(か或いは作者に近い立場と思われる)のFujii Masaoさんから、以下のコメントをもらった。
@nuko_yokohama BTW you can easily register pg_bigm and pg_trgm to the same database at the same time by modifying sql file slightly.
ふむ。そういうことならSQLファイルを修正して、pg_bigmとpg_trgmを同一データベース上にインストールできるようにしてみよう。
通常の運用では不要だとは思うけど。
なぜ併存できないのか
とりあえず何で併存できないのか、実際に2つのEXTENSIONを突っ込んで挙動を見るのが一番手っ取り早い。
ということでやってみた。
[ぬこ@横浜] psql gmtest psql (9.2.4) Type "help" for help. gmtest=# CREATE EXTENSION pg_trgm ; CREATE EXTENSION gmtest=# CREATE EXTENSION pg_bigm ; ERROR: operator class "gin_trgm_ops" for access method "gin" already exists STATEMENT: CREATE EXTENSION pg_bigm ; gmtest=#
ふーむ。GIN用の演算子クラス名が重複しているのが原因なのか。
ソースフォルダ上にある pg_bigm--1.0.sql の中を見てみるとGIN演算子クラスとして gin_bigm_ops と gin_trgm_ops の両方が定義されている。gin_trgm_ops のほうはコメントにあるように
support also gin_trgm_ops for backward-compatibility
下位互換性のための演算子クラスなので、これを使うつもりがなければ、定義から削除してしまえば良いのではないか。
複数EXTENSION併存の実証実験
ということで gin_trgm_ops のOPERATOR CLASS を削除し、その後に make install で再インストールする。
[ぬこ@横浜] make USE_PGXS=1 install /bin/mkdir -p '/home/harada/pgsql-9.2.4/lib' /bin/mkdir -p '/home/harada/pgsql-9.2.4/share/extension' /bin/mkdir -p '/home/harada/pgsql-9.2.4/share/extension' /bin/sh /home/harada/pgsql-9.2.4/lib/pgxs/src/makefiles/../../config/install-sh -c -m 755 pg_bigm.so '/home/harada/pgsql-9.2.4/lib/pg_bigm.so' /bin/sh /home/harada/pgsql-9.2.4/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./pg_bigm.control '/home/harada/pgsql-9.2.4/share/extension/' /bin/sh /home/harada/pgsql-9.2.4/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./pg_bigm--1.0.sql '/home/harada/pgsql-9.2.4/share/extension/' [ぬこ@横浜] psql gmtest psql (9.2.4) Type "help" for help. gmtest=# CREATE EXTENSION pg_bigm ; CREATE EXTENSION gmtest=# \dx List of installed extensions Name | Version | Schema | Description ---------+---------+------------+------------------------------------------------------------------- pg_bigm | 1.0 | public | text index searching based on bigrams pg_trgm | 1.0 | public | text similarity measurement and index searching based on trigrams plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (3 rows)
同一データベースにpg_trgmとpg_bigmが併存できた。
本当に両方使えるのかどうか、両方の演算子クラスを使ったインデクスを両方登録し、両方使えるのかを検証してみる。このために、pg_plan_hintをインストールしておく。pg_hint_planについてはFujii氏のブログに詳しい解説があるのでそっちをみてくだちい。
とりあえず、また「走れメロス」をサンプルにして、2種類のインデクスを生成する。
gmtest=# CREATE INDEX test_trgm ON test USING gin (data gin_trgm_ops); CREATE INDEX gmtest=# \d test Table "public.test" Column | Type | Modifiers --------+---------+--------------------------------------------------- id | integer | not null default nextval('test_id_seq'::regclass) data | text | Indexes: "test_bigm" gin (data gin_bigm_ops) "test_trgm" gin (data gin_trgm_ops) gmtest=#
両方のインデクスができた。
検索してみる。とはいえ普通にクエリを発行するとTom Laneというかプランナの御心のままになる。
gmtest=# EXPLAIN SELECT id, data FROM test WHERE data LIKE '%セリヌンティウス%' LIMIT 3; QUERY PLAN ----------------------------------------------------------------------------- Limit (cost=52.08..53.62 rows=3 width=349) -> Bitmap Heap Scan on test (cost=52.08..57.20 rows=10 width=349) Recheck Cond: (data ~~ '%セリヌンティウス%'::text) -> Bitmap Index Scan on idx_tr (cost=0.00..52.08 rows=10 width=0) Index Cond: (data ~~ '%セリヌンティウス%'::text) (5 rows)
今回はpg_trgmの演算子クラスを使ったGINインデクスが選択されるようだ。
で、pg_plan_hitの機能を使ってインデクスを制御してみようとしたが・・・
gmtest=# \d test Table "public.test" Column | Type | Modifiers --------+---------+--------------------------------------------------- id | integer | not null default nextval('test_id_seq'::regclass) data | text | Indexes: "idx_bi" gin (data gin_bigm_ops) "idx_tr" gin (data gin_trgm_ops) gmtest=# /*+ IndexScan(test idx_bi) */ EXPLAIN SELECT id, data FROM test WHERE data LIKE '%セリヌンティウス%' LIMIT 3; QUERY PLAN --------------------------------------------------------------------------------- Limit (cost=10000000000.00..10000000001.84 rows=3 width=349) -> Seq Scan on test (cost=10000000000.00..10000000006.12 rows=10 width=349) Filter: (data ~~ '%セリヌンティウス%'::text) (3 rows) gmtest=# /*+ IndexScan(test idx_tr) */ EXPLAIN SELECT id, data FROM test WHERE data LIKE '%セリヌンティウス%' LIMIT 3; QUERY PLAN --------------------------------------------------------------------------------- Limit (cost=10000000000.00..10000000001.84 rows=3 width=349) -> Seq Scan on test (cost=10000000000.00..10000000006.12 rows=10 width=349) Filter: (data ~~ '%セリヌンティウス%'::text) (3 rows)
え?なんでHINT句指定が無視されてるんだ・・・?
インデクスが両方生成できてもこれじゃ意味ないじゃないか・・・。
起動時にpg_hint_planの読み込みはされているはずなのに。どういうことなの。
追記
ビール飲みながら検証とかしちゃイカンなあ・・・w
早速、Fujii Masaoさんからツッコミが。
HINTの指定が間違っていただけだった。
IndexScanではなくBitmapScanを指定することできちんと制御できた・・・。阿呆か俺はw
gmtest=# /*+ BitmapScan(test idx_bi) */ EXPLAIN SELECT id, data FROM test WHERE data LIKE '%セリヌンティウス%' LIMIT 3; QUERY PLAN ----------------------------------------------------------------------------- Limit (cost=60.08..61.62 rows=3 width=349) -> Bitmap Heap Scan on test (cost=60.08..65.20 rows=10 width=349) Recheck Cond: (data ~~ '%セリヌンティウス%'::text) -> Bitmap Index Scan on idx_bi (cost=0.00..60.08 rows=10 width=0) Index Cond: (data ~~ '%セリヌンティウス%'::text) (5 rows) gmtest=# /*+ BitmapScan(test idx_tr) */ EXPLAIN SELECT id, data FROM test WHERE data LIKE '%セリヌンティウス%' LIMIT 3; QUERY PLAN ----------------------------------------------------------------------------- Limit (cost=52.08..53.62 rows=3 width=349) -> Bitmap Heap Scan on test (cost=52.08..57.20 rows=10 width=349) Recheck Cond: (data ~~ '%セリヌンティウス%'::text) -> Bitmap Index Scan on idx_tr (cost=0.00..52.08 rows=10 width=0) Index Cond: (data ~~ '%セリヌンティウス%'::text) (5 rows) gmtest=#
当初の目的は達成できた。めでたしめでたし。これで眠れる。