J'ai un problème avec la planification des requêtes de PostgreSQL 9.6. Ma requête ressemble à ceci:
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)
J'ai la sécurité au niveau des lignes activée pour les tables utilisées ci-dessus.
avec set enable_nestloop = True
, le planificateur de requêtes exécute la jonction de boucles imbriquées avec un temps d'exécution total d'environ 37 secondes: https://explain.depesz.com/s/59BR
avec set enable_nestloop = False
, la méthode Hash Join est utilisée et le temps de requête est d'environ 0,3 sec: https://explain.depesz.com/s/PG8E
J'ai fait VACUUM ANALYZE
avant d'exécuter les requêtes, mais cela n'a pas aidé.
Je sais que ce n'est pas une bonne pratique de set enable_nestloop = False
, et toute autre option similaire pour le planificateur. Mais comment "convaincre" le planificateur d'utiliser des jointures de hachage sans désactiver les boucles imbriquées?
La réécriture de la requête est une option.
Si j'exécute la même requête sous un rôle qui contourne RLS, alors elle est exécutée très rapidement. La politique de sécurité au niveau des lignes ressemble à ceci:
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
);
Toutes les idées ou suggestions seraient grandement appréciées.
Ce qui se passe ici, c'est que la boucle imbriquée est loin d'un côté. Les boucles imbriquées fonctionnent vraiment bien quand un côté est très petit, comme retourner une ligne. Dans votre requête, le planificateur cherche ici et estime qu'une jointure par hachage ne renverra qu'une seule ligne. Au lieu de cela, cette jointure par hachage (id_propriété = id) renvoie 1 338 lignes. Cela force 1 338 boucles à s'exécuter de l'autre côté de la boucle imbriquée qui compte déjà 3 444 lignes. C'est un sacré lot quand vous n'en attendez qu'un (ce qui n'est même pas vraiment une "boucle"). Quoi qu'il en soit ..
Un examen plus approfondi à mesure que nous avançons montre que la jointure par hachage est vraiment altérée par les estimations qui en découlent,
Filter: (((properties."COMPOSITION")::text ~~ 'Mo%'::text) AND (((properties."NAME")::text ~~ '%VASP-ase-preopt%'::text) OR (properties."CALCULATOR_ID" = ANY ('{7,22,25}'::integer[]))))
PostgreSQL s'attend à ce que cela renvoie une ligne. Mais ce n'est pas le cas. Et c'est vraiment votre problème. Donc, certaines options ici, qui n'impliquent pas de retirer un marteau et de désactiver nested_loop
Vous pouvez ajouter un ou deux index à properties
pour l'aider à ignorer complètement l'analyse seq, ou mieux estimer le retour.
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;
Vous pouvez également déplacer les propriétés vers un CTE ou sous-sélectionner avec OFFSET 0
qui crée une clôture.
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
)