Postgresql 针对小数据集的错误查询计划使其速度非常慢 问题:

Postgresql 针对小数据集的错误查询计划使其速度非常慢 问题:,postgresql,postgresql-performance,postgresql-12,Postgresql,Postgresql Performance,Postgresql 12,当我有少量数据(约5万行)时,我的查询(简单的几个连接)运行得非常慢,但当我有大量数据(约18万行)时,我的查询运行得非常快。时差是巨大的,因为它从几秒钟到几乎半小时不等 尝试 我重新检查了连接,它们都是正确的。此外,在运行查询之前,我对表运行了真空分析,但没有解决任何问题。我还检查了是否有锁以任何方式阻止了查询,或者连接速度在任何情况下都很慢,但不是故障 因此,我去检查EXPLAIN的输出。在阅读了结果之后,我发现在慢的情况下,它会进行不必要的额外排序,并且会陷入一个嵌套的for循环中,而在我

当我有少量数据(约5万行)时,我的查询(简单的几个连接)运行得非常慢,但当我有大量数据(约18万行)时,我的查询运行得非常快。时差是巨大的,因为它从几秒钟到几乎半小时不等

尝试 我重新检查了连接,它们都是正确的。此外,在运行查询之前,我对表运行了
真空分析
,但没有解决任何问题。我还检查了是否有锁以任何方式阻止了查询,或者连接速度在任何情况下都很慢,但不是故障

因此,我去检查EXPLAIN的输出。在阅读了结果之后,我发现在慢的情况下,它会进行不必要的额外排序,并且会陷入一个嵌套的for循环中,而在我有更多数据的情况下,该循环是不存在的。我不知道如何告诉postgres做与更大数据集场景相同的计划

根据一条评论,我也试着不使用CTE,但它也没有帮助:仍然进行嵌套循环和排序

细节:
  • Postgres版本
    PostgreSQL 12.3
  • 完整查询文本
  • 表格定义
  • 基数:慢格~50k,快格~180k
  • 查询计划:两种情况下的
    解释(缓冲区,分析)
    输出-慢速情况,快速情况:
  • 附加信息:相关内存配置为:

  • 在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