小数据集的错误查询计划使其非常慢

Bad query plan for small dataset makes it very slow

问题

我的查询(简单的几个连接)运行当我有少量数据(~50k 行)时非常慢,但当我有大量数据时运行s 很快数据(~180k 行)。时差很大,从几秒到将近半小时。

尝试次数

我重新检查了连接,它们都是正确的。此外,在 运行 查询之前,我有 运行 一个 VACUUM ANALYZE 到 table,但它没有解决任何问题。我还检查了是否有锁以任何方式阻止了查询,或者连接在任何情况下都很慢,但它们不是故障的情况。

因此,我去检查EXPLAIN的输出。阅读结果后,我发现在缓慢的情况下,它会进行不必要的额外排序,并且会卡在一个嵌套的 for 循环中,而在我有更多数据的情况下,这个循环是不存在的。我不确定如何告诉 postgres 执行与更大数据集方案相同的计划。

根据评论,我也尝试不使用 CTE,但它也无济于事:仍然进行嵌套循环和排序。

详情:

  1. Postgres 版本PostgreSQL 12.3
  2. 完整查询文本:
WITH t0 AS (SELECT * FROM original_table WHERE id=0),
     t1 AS (SELECT * FROM original_table WHERE id=1),
     t2 AS (SELECT * FROM original_table WHERE id=2),
     t3 AS (SELECT * FROM original_table WHERE id=3),
     t4 AS (SELECT * FROM original_table WHERE id=4)
 
SELECT
    t0.dtime,
    t1.dtime,
    t3.dtime,
    t3.dtime::date,
    t4.dtime,
    t1.first_id,
    t1.field,
    t1.second_id,
    t1.third_id,
    t2.fourth_id,
    t4.fourth_id
FROM t1
LEFT JOIN t0 ON t1.first_id=t0.first_id
     JOIN t2 ON t1.first_id=t2.first_id AND t1.second_id = t2.second_id AND t1.third_id = t2.third_id
     JOIN t3 ON t1.first_id=t3.first_id AND t1.second_id = t3.second_id AND t1.third_id = t3.third_id
     JOIN t4 ON t1.first_id=t4.first_id AND t1.second_id = t4.second_id AND t1.fourth_id= t4.third_id
ORDER BY t3.dtime
;
  1. Table 定义:
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)
  1. 基数:慢的情况~50k,快的情况~180k
  2. 查询计划:两种情况下 EXPLAIN (BUFFERS, ANALYZE) 的输出 - 慢速情况 https://explain.depesz.com/s/5JDw, fast case: https://explain.depesz.com/s/JMIL
  3. 附加信息:相关内存配置为:
      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

我经常在 SQL-Server 遇到这种情况。
通常导致速度变慢的原因是,它对每行连接执行一次 CTE。

您可以通过选择 temp-tables 而不是使用 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