【问题标题】:In PostgreSQL what does hashed subplan mean?在 PostgreSQL 中,散列子计划是什么意思?
【发布时间】:2021-08-02 07:21:43
【问题描述】:

我想知道优化器如何重写查询以及如何读取PostgreSQL中的执行计划 这是示例代码。

DROP TABLE ords;
CREATE TABLE ords (
ORD_ID INT NOT NULL,
ORD_PROD_ID VARCHAR(2) NOT NULL,
ETC_CONTENT VARCHAR(100));
ALTER TABLE ords ADD CONSTRAINT ords_PK PRIMARY KEY(ORD_ID);
CREATE INDEX ords_X01 ON ords(ORD_PROD_ID);
INSERT INTO ords
SELECT i
      ,chr(64+case when i <= 10 then i else 26 end)
      ,rpad('x',100,'x')
  FROM generate_series(1,10000) a(i);

SELECT COUNT(*) FROM ords WHERE ORD_PROD_ID IN ('A','B','C');

DROP TABLE delivery;
CREATE TABLE delivery (
ORD_ID INT NOT NULL,
VEHICLE_ID VARCHAR(2) NOT NULL,
ETC_REMARKS VARCHAR(100));
ALTER TABLE delivery ADD CONSTRAINT delivery_PK primary key (ORD_ID, VEHICLE_ID);
CREATE INDEX delivery_X01 ON delivery(VEHICLE_ID);
INSERT INTO delivery
SELECT i
     , chr(88 + case when i <= 10 then mod(i,2) else 2 end)
     , rpad('x',100,'x')
  FROM generate_series(1,10000) a(i);

analyze ords;
analyze delivery;

这是我感兴趣的 SQL。

SELECT *
  FROM ords a
 WHERE ( EXISTS (SELECT 1
                   FROM delivery b
                  WHERE a.ORD_ID = b.ORD_ID
                    AND b.VEHICLE_ID IN ('X','Y')
                 )
         OR a.ORD_PROD_ID IN ('A','B','C')
         );
Here is the execution plan
| Seq Scan on portal.ords a (actual time=0.038..2.027 rows=10 loops=1)                                           |
|   Output: a.ord_id, a.ord_prod_id, a.etc_content                                                               |
|   Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) OR ((a.ord_prod_id)::text = ANY ('{A,B,C}'::text[]))) |
|   Rows Removed by Filter: 9990                                                                                 |
|   Buffers: shared hit=181                                                                                      |
|   SubPlan 1                                                                                                    |
|     ->  Index Only Scan using delivery_pk on portal.delivery b (never executed)                                |
|           Index Cond: (b.ord_id = a.ord_id)                                                                    |
|           Filter: ((b.vehicle_id)::text = ANY ('{X,Y}'::text[]))                                               |
|           Heap Fetches: 0                                                                                      |
|   SubPlan 2                                                                                                    |
|     ->  Index Scan using delivery_x01 on portal.delivery b_1 (actual time=0.023..0.025 rows=10 loops=1)        |
|           Output: b_1.ord_id                                                                                   |
|           Index Cond: ((b_1.vehicle_id)::text = ANY ('{X,Y}'::text[]))                                         |
|           Buffers: shared hit=8                                                                                |
| Planning:                                                                                                      |
|   Buffers: shared hit=78                                                                                       |
| Planning Time: 0.302 ms                                                                                        |
| Execution Time: 2.121 ms  

我不知道优化器是如何转换 SQL 的。

  • 优化器重写的最终 SQL 是什么?
  • 我上面的SQL中只有一个EXISTS子查询,为什么会有两个子计划?
  • “哈希子计划 2”是什么意思?

如果有人与我分享一点知识,我将不胜感激。

【问题讨论】:

  • 优化器不输出SQL代码。
  • 我认为优化器会转换我们提供的 SQL 并计算重写查询的成本。我想知道最终转换后的 SQL 会是什么。
  • 它将其转换为查询计划,而不是 SQL 代码。
  • 贝尔吉,你是对的。我无法区分优化器和查询重写器。

标签: postgresql optimization sql-execution-plan


【解决方案1】:

您有一种误解,认为优化器会重写 SQL 语句。事实并非如此。重写查询是 查询重写器 的工作,例如用视图的定义替换视图。优化器提出了一系列执行步骤来计算结果。它产生一个计划,而不是一个 SQL 语句。

优化器计划了两种选择:对找到的每一行执行子计划 1,或者执行一次子计划 2(注意它独立于 a),根据结果构建一个哈希表并为找到的每一行探测该哈希在a

在执行时,PostgreSQL 决定使用后一种策略,这就是子计划 1 永远不会执行的原因。

【讨论】:

  • 你知道这个决定的依据是什么吗?我以为是哈希表是否开始超出work_mem,但实验性的似乎不是。
  • jjanes,子计划 1 与哈希表无关,子计划 1 从未执行。优化器执行了 SubPlan 2,并且优化器使用了一个没有超出 work_mem 的哈希表。我猜如果优化器预测它会溢出 work_mem,那么它就不会执行 SubPlan 2。如果它预测它会溢出 SubPlan2 中的哈希表,它就会执行 SubPlan1。
  • @jjanes 不,我不明白这个决定是如何做出的。我试图解释执行计划。
  • Laurenz,这是我对如何做出决定的估计。在有问题的 SQL 中,我们只能看到现有子查询没有崩溃的查询计划的估计成本。当您在现有子查询中添加 OFFSET 0 时,估计成本与有问题的 SQL 相同。我猜在幕后优化器还计算了使用 SubPlan2 的现有子查询崩溃的执行计划的成本 当子查询崩溃发生时,估计的成本要低得多。所以优化器选择了不执行 SubPlan1 的 SubPlan2。
  • @LaurenzAlbe 我找到了这个决定的依据。延迟运行时间从未有意义地实施,最近被删除 (41efb8340877e8ffd0023bb)。该决定是根据规划者可用的信息做出的,现在由规划者做出。
猜你喜欢
  • 2021-08-05
  • 2022-07-22
  • 1970-01-01
  • 2010-10-19
  • 2012-10-28
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 2019-02-15
相关资源
最近更新 更多