Postgresql 针对小数据集的错误查询计划使其速度非常慢 问题:
当我有少量数据(约5万行)时,我的查询(简单的几个连接)运行得非常慢,但当我有大量数据(约18万行)时,我的查询运行得非常快。时差是巨大的,因为它从几秒钟到几乎半小时不等 尝试 我重新检查了连接,它们都是正确的。此外,在运行查询之前,我对表运行了Postgresql 针对小数据集的错误查询计划使其速度非常慢 问题:,postgresql,postgresql-performance,postgresql-12,Postgresql,Postgresql Performance,Postgresql 12,当我有少量数据(约5万行)时,我的查询(简单的几个连接)运行得非常慢,但当我有大量数据(约18万行)时,我的查询运行得非常快。时差是巨大的,因为它从几秒钟到几乎半小时不等 尝试 我重新检查了连接,它们都是正确的。此外,在运行查询之前,我对表运行了真空分析,但没有解决任何问题。我还检查了是否有锁以任何方式阻止了查询,或者连接速度在任何情况下都很慢,但不是故障 因此,我去检查EXPLAIN的输出。在阅读了结果之后,我发现在慢的情况下,它会进行不必要的额外排序,并且会陷入一个嵌套的for循环中,而在我
真空分析
,但没有解决任何问题。我还检查了是否有锁以任何方式阻止了查询,或者连接速度在任何情况下都很慢,但不是故障
因此,我去检查EXPLAIN的输出。在阅读了结果之后,我发现在慢的情况下,它会进行不必要的额外排序,并且会陷入一个嵌套的for循环中,而在我有更多数据的情况下,该循环是不存在的。我不知道如何告诉postgres做与更大数据集场景相同的计划
根据一条评论,我也试着不使用CTE,但它也没有帮助:仍然进行嵌套循环和排序
细节:
PostgreSQL 12.3
解释(缓冲区,分析)
输出-慢速情况,快速情况:在SQL Server上,这种情况经常发生在我身上
通常导致速度慢的原因是,它每连接一行执行一次CTE
您可以通过选择进入临时表而不是使用CTE来防止这种情况发生。
我假设PostgreSQL也是如此,但我没有测试它:
DROP TABLE IF EXISTS tempT0;
DROP TABLE IF EXISTS tempT1;
CREATE TEMP TABLE tempT0 AS SELECT * FROM original_table WHERE id=0;
CREATE TEMP TABLE tempT1 AS SELECT * FROM original_table WHERE id=1;
[... etc]
FROM tempT1 AS t1
LEFT JOIN tempT0 AS t0 ON t1.first_id=t0.first_id
如果您在这种情况下不使用CTEs怎么办?他们不是真的需要这个最初我有视图t0,…,t4,但我真的不知道如何做没有CTE或创建视图的查询。哦,我怎么能如此盲目哈哈哈!但是,这没有帮助。它仍然有相同的嵌套循环,而且需要永远的时间…非常感谢Stefan。我怀疑有了更多的数据,临时表是在后台创建的,但我没有考虑在使用不太多数据的情况下显式创建它们。它完全解决了我的问题!:)
Column | Type
----------+----------------------------
id | smallint
dtime | timestamp without time zone
first_id | character varying(10)
second_id | character varying(10)
third_id | character varying(10)
fourth_id | character varying(10)
field | character varying(10)
name | current_setting | source
---------------+-----------------+---------------------
max_stack_dept | 2MB | environment variable
max_wal_size | 1GB | configuration file
min_wal_size | 80MB | configuration file
shared_buffers | 128MB | configuration file
DROP TABLE IF EXISTS tempT0;
DROP TABLE IF EXISTS tempT1;
CREATE TEMP TABLE tempT0 AS SELECT * FROM original_table WHERE id=0;
CREATE TEMP TABLE tempT1 AS SELECT * FROM original_table WHERE id=1;
[... etc]
FROM tempT1 AS t1
LEFT JOIN tempT0 AS t0 ON t1.first_id=t0.first_id