web-dev-qa-db-ja.com

PostgreSQL 9.6の望ましくないネストループとハッシュ結合

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
);

任意のアイデアや提案をいただければ幸いです。

13

ここで起こっているのは、ネストされたループが片側でかなり離れていることです。ネストされたループは機能しますreally 1つの行を返すなど、片側が非常に小さい場合に適しています。クエリでは、プランナーはここを探し回って、ハッシュ結合が1行だけを返すと推定します。代わりに、そのハッシュ結合(property_id = id)は1,338行を返します。これにより、すでに3,444行ある入れ子ループの反対側で1,338ループが強制的に実行されます。これは、1つだけを期待している場合(これは、「ループ」ではない)です。とにかく。

下に進むにつれてさらに調べていくと、ハッシュ結合は、これから生じる見積もりによって本当に失敗していることがわかります。

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

PostgreSQLはそれが1行を返すことを期待しています。しかし、そうではありません。そして、それは本当にあなたの問題です。だからここにいくつかのオプションがあり、そりハンマーを取り出してnested_loopを無効にする必要はありません。

  • インデックスを1つまたは2つ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
    )
    
15
Evan Carroll