Небажана Nest Loop проти Hash Приєднуйтесь до PostgreSQL 9.6


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)

Увімкнено захист рівних рівнів для використовуваних вище таблиць.

  • з set enable_nestloop = True, планувальник запитів виконує з'єднання Nested Loop із загальним часом роботи близько 37 секунд: https://explain.depesz.com/s/59BR

  • з set enable_nestloop = False, використовується метод Hash Join і час запиту становить приблизно 0,3 сек: https://explain.depesz.com/s/PG8E

Я робив 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 в той час як = ширше використовується, вони обидва плануються однаково. Моє припущення, що він грає з більш ніж одним значенням, або ОРМ є ледачим.
Еван Керролл

Відповіді:


15

Те, що відбувається тут, це те, що вкладена петля знаходиться далеко на одній стороні. Вкладені петлі працюють дуже добре, коли одна сторона дуже мала, наприклад, повернення одного рядка. У вашому запиті планувальник тужить тут і підрахує, що Hash Join поверне лише один рядок. Натомість, що Hash Join (property_id = id) повертає 1338 рядків. Це змушує 1338 циклів працювати з іншого боку вкладеного циклу, який вже має 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допомогти йому повністю пропустити сканування послідовностей або краще оцінити віддачу.

    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
    )
    
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.