PostgreSQL 哈希连接与嵌套循环和哈希索引

问题描述 投票:0回答:1

我正在测试 PostgreSQL 16.2 中的连接如何与哈希索引配合使用。这是一个测试表。只有 2 列,其中包含文本格式的数字。

create table join_test (pk varchar(20), fk varchar(20));
insert into join_test(pk, fk) select s::varchar(20), (10000001 - s)::varchar(20) from generate_series(1, 10000000) as s;

然后我进行简单的加入。

explain analyze select * from join_test t1 join join_test t2 on t1.pk = t2.fk;
Hash Join  (cost=327879.85..878413.95 rows=9999860 width=28) (actual time=5181.056..18337.596 rows=10000000 loops=1)
  Hash Cond: ((t1.pk)::text = (t2.fk)::text)
  ->  Seq Scan on join_test t1  (cost=0.00..154053.60 rows=9999860 width=14) (actual time=0.070..1643.618 rows=10000000 loops=1)
  ->  Hash  (cost=154053.60..154053.60 rows=9999860 width=14) (actual time=5147.801..5147.803 rows=10000000 loops=1)
        Buckets: 262144  Batches: 128  Memory Usage: 5691kB
        ->  Seq Scan on join_test t2  (cost=0.00..154053.60 rows=9999860 width=14) (actual time=0.024..2163.714 rows=10000000 loops=1)
Planning Time: 0.172 ms
Execution Time: 18718.586 ms

这里没有什么奇怪的,没有索引,就有一个带有动态哈希表构造的哈希连接。

然后我在“pk”列上添加一个哈希索引。并再次加入同样的操作。

Nested Loop  (cost=0.00..776349.75 rows=9999860 width=28) (actual time=0.107..85991.520 rows=10000000 loops=1)
  ->  Seq Scan on join_test t2  (cost=0.00..154053.60 rows=9999860 width=14) (actual time=0.062..1399.400 rows=10000000 loops=1)
  ->  Index Scan using join_test_pk_idx on join_test t1  (cost=0.00..0.05 rows=1 width=14) (actual time=0.008..0.008 rows=1 loops=10000000)
        Index Cond: ((pk)::text = (t2.fk)::text)
        Rows Removed by Index Recheck: 0
Planning Time: 0.195 ms
Execution Time: 86490.687 ms

据我了解,在这种特殊情况下,嵌套循环并不是真正的“嵌套循环”,在算法上与哈希连接相同,只是它使用索引中已经构造的哈希表。因此,理论上,带有索引的查询应该比没有索引的查询运行得更快。但实际上它的效果要糟糕得多。我想知道为什么?使用哈希索引进行连接是否有意义,或者我应该始终使用 btree?

postgresql query-optimization sql-execution-plan postgresql-performance
1个回答
0
投票

那么你做了什么来实现这一目标呢?在我看来,嵌套循环比哈希连接慢得多。您是否做了一些愚蠢的事情,例如将 random_page_cost 设置为零?

据我了解,在这种特殊情况下,Nested Loop 并不是真正的“嵌套循环”,在算法上与 Hash Join 相同

你可以玩无休止的文字游戏,其中一切都“真正”是别的东西,但这很快就变得毫无意义,因为没有任何意义。

哈希连接使用顺序扫描来读取数据并构建一个私有哈希表,该表旨在在需要时有效地溢出到磁盘。所以无论是IO还是锁都是高效的。嵌套循环使用基于共享磁盘的结构,其中每次访问都可能触发低效的随机 IO(这可能会也可能不会由缓存实现),并且肯定会涉及一些锁定和缓冲区管理开销。如果您确实想了解性能细节,您可以使用

perf
来深入了解。

这里使用哈希索引而不是 btree 索引没有什么意义。对于这种情况,欺骗规划者认为嵌套循环实际上是一个好主意就更没有意义了。

© www.soinside.com 2019 - 2024. All rights reserved.