PostgreSQL 9.6中不需要的Nest Loop与Hash Join


13

我在PostgreSQL 9.6查询计划上遇到麻烦。我的查询如下所示:

SET role plain_user;

SELECT properties.*
FROM properties
JOIN entries_properties
  ON properties.id = entries_properties.property_id
JOIN structures
  ON structures.id = entries_properties.entry_id 
WHERE structures."STRUKTURBERICHT" != ''
  AND properties."COMPOSITION" LIKE 'Mo%'
  AND (
    properties."NAME" LIKE '%VASP-ase-preopt%'
    OR properties."CALCULATOR_ID" IN (7,22,25)
  )
AND properties."TYPE_ID" IN (6)

我为上述表启用了行级安全性。

VACUUM ANALYZE在运行查询之前就做了,但是并没有帮助。

我知道,set enable_nestloop = False对于规划师以及其他任何类似的选择,这都不是一个好习惯。但是,如何在不禁用嵌套循环的情况下“说服”计划程序使用哈希联接呢?

重写查询是一种选择。

如果我在绕过RLS的角色下运行相同的查询,则执行速度非常快。行级安全策略如下所示:

CREATE POLICY properties_select
ON properties
FOR SELECT
USING (
  (
    properties.ouid = get_current_user_id()
    AND properties.ur
  )
  OR (
    properties.ogid in (select get_current_groups_id())
    AND properties.gr
  )
  OR properties.ar
);

任何想法或建议,将不胜感激。


只是有点困惑:为什么AND properties."TYPE_ID" IN (6);= 6;
Vérace

2
@Vérace虽然=的使用更广泛,但它们都以相同的方式进行计划。我的假设是他正在使用多个值,或者ORM有点懒。
埃文·卡罗尔

Answers:


15

这里发生的是嵌套循环在一侧偏离。当一侧很小(例如返回一行)时,嵌套循环的效果非常好。在您的查询中,计划者在此处摸索,估计哈希联接将仅返回一行。相反,该哈希联接(property_id = id)返回1,338行。这将迫使1,338个循环在已具有3,444行的嵌套循环的另一侧运行。当您只期望一个时,这真是个麻烦(甚至不算什么“循环”)。反正

当我们向下移动时进一步检查表明,哈希联接确实对由此产生的估计感到厌烦,

Filter: (((properties."COMPOSITION")::text ~~ 'Mo%'::text) AND (((properties."NAME")::text ~~ '%VASP-ase-preopt%'::text) OR (properties."CALCULATOR_ID" = ANY ('{7,22,25}'::integer[]))))

PostgreSQL期望返回一行。但事实并非如此。而且,这确实是您的问题。因此,这里的一些选择不涉及取出大铁锤并禁用nested_loop

  • 您可以添加一两个索引,properties以帮助其完全跳过seq扫描,或者更好地估计收益。

    CREATE INDEX ON properties USING ( "TYPE_ID", "CALCULATOR_ID" );
    -- the gist_trgm_ops may or may not be needed depending on selectivity of above.
    CREATE INDEX ON properties USING GIST (
      "COMPOSITION" gist_trgm_ops,
      "NAME"        gist_trgm_ops
    );
    ANALYZE properties;
  • 或者,您可以将属性材料移动到CTE或子选择中,OFFSET 0以创建围栏。

    WITH t AS (
      SELECT *
      FROM properties.
      WHERE "COMPOSITION" LIKE 'Mo%'
      AND (
        "NAME" LIKE '%VASP-ase-preopt%'
        OR "CALCULATOR_ID" IN (7,22,25)
      )
      AND "TYPE_ID" IN (6)
    )
    SELECT * FROM structures
    JOIN t ON (
      structures.id = entries_properties.entry_id
    )
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.