3

我目前正在尝试改进 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。预计仅引用节点的子集,并且对于某些安装实例,可能仅引用单个节点。以下查询似乎很自然地完成了这项工作:

SELECT * FROM data_node
WHERE EXISTS (
 SELECT 1 FROM data_entry WHERE node_fk = data_node.id
);

VACUUM ANALYZE两个表上 a 之后,这会产生以下查询计划:

 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)

在所示的最小设置中,查询需要几秒钟(在实际的最小应用场景中,需要几分钟),尽管使用较大表上的索引,它应该在不到一毫秒的时间内完成。通过设置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 使用“好”查询计划或性能相当的计划来过滤引用的小表条目?

4

0 回答 0