現在、postgres クエリを改善しようとしています。以下は、postgres-12.6 で私の問題の本質を再現するための最小限のセットアップです。
CREATE TABLE data_node (id BIGINT PRIMARY KEY);
CREATE TABLE data_entry (id BIGINT PRIMARY KEY, node_fk BIGINT NOT NULL, FOREIGN KEY (node_fk) REFERENCES data_node (id));
CREATE INDEX node_ix ON data_entry USING BTREE (node_fk);
INSERT INTO data_node (id) SELECT generate_series(1,10);
INSERT INTO data_entry (id, node_fk) SELECT s, 2 FROM generate_series(1,10000000) s;
data_node
大きなテーブルによって参照される小さなテーブルのすべてのエントリを効率的にフィルタリングしようとしていdata_entry
ます。ノードのサブセットのみが参照されることが予想され、一部のインストール インスタンスでは、1 つのノードのみが参照される場合があります。次のクエリは、仕事をするのに自然に思えます。
SELECT * FROM data_node
WHERE EXISTS (
SELECT 1 FROM data_entry WHERE node_fk = data_node.id
);
両方のテーブルに対してVACUUM ANALYZE
を実行すると、次のクエリ プランが生成されます。
Merge Join (cost=179055.16..179055.99 rows=1 width=8) (actual time=1895.155..1895.158 rows=1 loops=1)
Merge Cond: (data_node.id = data_entry.node_fk)
-> Index Only Scan using data_node_pkey on data_node (cost=0.14..8.29 rows=10 width=8) (actual time=0.004..0.008 rows=3 loops=1)
Heap Fetches: 0
-> Sort (cost=179055.02..179055.03 rows=1 width=8) (actual time=1895.143..1895.144 rows=1 loops=1)
Sort Key: data_entry.node_fk
Sort Method: quicksort Memory: 25kB
-> HashAggregate (cost=179055.00..179055.01 rows=1 width=8) (actual time=1895.135..1895.136 rows=1 loops=1)
Group Key: data_entry.node_fk
-> Seq Scan on data_entry (cost=0.00..154055.00 rows=10000000 width=8) (actual time=0.009..831.883 rows=10000000 loops=1)
示されている最小限のセットアップでは、クエリに数秒 (実際の、最小限度の低いアプリケーション シナリオでは数分) かかりますが、より大きなテーブルでインデックスを使用すると、クエリは 1 ミリ秒未満で実行されるはずです。を設定SET enable_seqscan = false;
することで、postgres に「適切な」クエリ プランを使用させることができました。
Nested Loop Semi Join (cost=0.57..12.86 rows=1 width=8) (actual time=0.029..0.044 rows=1 loops=1)
-> Index Only Scan using data_node_pkey on data_node (cost=0.14..8.29 rows=10 width=8) (actual time=0.003..0.004 rows=10 loops=1)
Heap Fetches: 0
-> Index Only Scan using node_ix on data_entry (cost=0.43..185308.04 rows=10000000 width=8) (actual time=0.004..0.004 rows=0 loops=10)
Index Cond: (node_fk = data_node.id)
Heap Fetches: 0
Planning Time: 0.269 ms
Execution Time: 0.068 ms
enable_seqscan
これにより、テスト設定で必要なものが得られますが、もちろん、グローバルに変更することは、運用環境にとって実行可能なソリューションではありません.
postgres が「適切な」クエリ プランまたは同等のパフォーマンスのプランを使用して、参照された小さなテーブル エントリのフィルタリングを実行することを確認する「クリーンな」方法はありますか?