Postgres 9.4データベースでは、各行の識別子のリストを含むフィールドidentifiers: varchar(512)[]
のテーブルがあります。新しい行を追加するときは、そのidentifiers
リストが既存の行と重複しないようにしたいので、その列のGINインデックスを使用して、このような競合する行を最初に検索します。
Column | Type | Modifiers
----------------+--------------------------+-----------------------------------------------------------
id | integer | not null default nextval('my_table_id_seq'::regclass)
identifiers | character varying(512)[] |
... other columns ...
Indexes:
"my_table_pkey" PRIMARY KEY, btree (id)
"gin_idx_identifiers" gin (identifiers) WITH (fastupdate='on')
... other indexes ...
問題は、デフォルトで、次のクエリがGINインデックスを使用しないことです。
EXPLAIN ANALYZE
SELECT * FROM "my_table"
WHERE "my_table"."identifiers" && ARRAY['a_sample_identifier']::varchar(512)[] LIMIT 1;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..20.97 rows=1 width=1368) (actual time=31120.985..31120.986 rows=1 loops=1)
-> Seq Scan on my_table (cost=0.00..4318413.15 rows=205920 width=1368) (actual time=31120.983..31120.983 rows=1 loops=1)
Filter: (identifiers && '{a_sample_identifier}'::character varying(512)[])
Rows Removed by Filter: 14572058
Planning time: 0.135 ms
Execution time: 31121.023 ms
enable_seqscan = off
、次にインデックスが使用されます(そして、クエリは驚くほど速くなります):
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=1755.88..1757.62 rows=1 width=1368) (actual time=0.065..0.065 rows=1 loops=1)
-> Bitmap Heap Scan on my_table (cost=1755.88..359284.85 rows=205920 width=1368) (actual time=0.064..0.064 rows=1 loops=1)
Recheck Cond: (identifiers && '{a_sample_identifier}'::character varying(512)[])
Heap Blocks: exact=1
-> Bitmap Index Scan on gin_idx_identifiers (cost=0.00..1704.40 rows=205920 width=0) (actual time=0.056..0.056 rows=1 loops=1)
Index Cond: (identifiers && '{a_sample_identifier}'::character varying(512)[])
Planning time: 0.126 ms
Execution time: 0.102 ms
(8 rows)
ただし、その設定を読みましたenable_seqscan = off
グローバルに危険です。 postgresにインデックスを強制的に使用させるにはどうすればよいですか?
EXCLUDE制約を使用して、システムに自動的にチェックを行わせることができます。配置すると、制約に違反する行を挿入しようとすると、自動的にエラーがスローされます。これはおそらくあなたの最良のオプションです。最初に手動でチェックしてから行を挿入すると、競合する2つの行を正常に挿入できるようになる可能性があります。
何らかの理由で実際にチェックを行う必要がある場合は、クエリを書き直して、LIMIT 1ではなくCOUNT(*)を使用し、カウントがゼロかどうかを確認します。
問題は、seqスキャンが一致する1つの行を非常に迅速に検出し、停止する可能性があることです。 COUNT(*)を使用してクエリを書き込むと、プランナはseqスキャンの真のコストを実現するように強制されます。または、クエリからLIMIT 1を削除するだけで、クライアント側で1行のみをフェッチし、それ以上フェッチしないようにすることもできます。