PostgresSQL Вложенные циклы - когда планировщик решает использовать вложенный L oop при выполнении ВНУТРЕННЕГО СОЕДИНЕНИЯ? - PullRequest
1 голос
/ 28 января 2020

Я выполняю запрос с INNER JOIN, в котором планировщик решает использовать вложенный L oop. Я выяснил, что это связано с условиями WHERE, так как я пытался написать запрос с различными условиями WHERE, поэтому он возвращает тот же результат, но не использует вложенный L oop.

Мой вопрос почему планировщик решил принимать разные решения, когда запросы кажутся идентичными, поскольку оба они возвращают один и тот же результат? Запрос выполняется за 77 секунд с Nested L oop и 13 se c без, а запрос, выполняемый за 13 se c, довольно уродлив и не элегантен, что заставляет меня думать, что есть лучший способ написать его.

Вот два запроса. Обратите внимание, что разница между ними заключается в том, как предложение WHERE фильтрует по дате, когда первый использует BETWEEN, а второй - серию операторов OR. Мне известно, что странно, что current_date обернут в их собственные подзапросы, но это потому, что эти запросы используют сторонние обертки данных. Это позволяет передавать current_date как неизменяемый объект, что значительно ускоряет производительность.

SELECT ROUND(AVG(m.forecast - w.wind),6) from pjm.wind_forecast_recent w
    INNER JOIN pjm.load_forecast_recent m ON w.pricedate = m.pricedate AND w.hour = m.hour
  WHERE w.hour = 5 AND m.area = 'RTO_COMBINED' AND 
            (w.pricedate BETWEEN (SELECT current_date-6) AND (SELECT current_date));

-----------

SELECT ROUND(AVG(m.forecast - w.wind),6) from pjm.wind_forecast_recent w
    INNER JOIN pjm.load_forecast_recent m ON w.pricedate = m.pricedate AND w.hour = m.hour
  WHERE w.hour = 5 AND m.area = 'RTO_COMBINED' AND (
    w.pricedate = (SELECT current_date-6) OR
    w.pricedate = (SELECT current_date-5) OR
    w.pricedate = (SELECT current_date-4) OR
    w.pricedate = (SELECT current_date-3) OR
    w.pricedate = (SELECT current_date-2) OR
    w.pricedate = (SELECT current_date-1) OR
    w.pricedate = (SELECT current_date))

А вот соответствующий EXPLAIN ANALYZE:

Aggregate  (cost=842341.01..842341.02 rows=1 width=32) (actual time=77120.088..77120.089 rows=1 loops=1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.007..0.008 rows=1 loops=1)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  ->  Nested Loop  (cost=840333.25..842340.97 rows=1 width=18) (actual time=14719.661..77119.994 rows=7 loops=1)
        ->  Foreign Scan on wind_forecast_recent w  (cost=242218.45..242218.49 rows=1 width=18) (actual time=3184.714..3184.720 rows=7 loops=1)
        ->  Foreign Scan on load_forecast_recent m  (cost=598114.80..600122.47 rows=1 width=16) (actual time=10531.723..10531.724 rows=1 loops=7)
Planning Time: 744.979 ms
Execution Time: 77227.512 ms
Aggregate  (cost=841657.94..841657.95 rows=1 width=32) (actual time=13683.022..13683.023 rows=1 loops=1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.006..0.006 rows=1 loops=1)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 3 (returns $2)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 4 (returns $3)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 5 (returns $4)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 6 (returns $5)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 7 (returns $6)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  ->  Foreign Scan  (cost=833725.15..841657.83 rows=1 width=18) (actual time=13682.974..13682.977 rows=7 loops=1)
        Relations: (pjm.wind_forecast_recent w) INNER JOIN (pjm.load_forecast_recent m)
Planning Time: 332.870 ms
JIT:
  Functions: 16
  Options: Inlining true, Optimization true, Expressions true, Deforming true
  Timing: Generation 4.163 ms, Inlining 15.088 ms, Optimization 44.489 ms, Emission 28.064 ms, Total 91.804 ms
Execution Time: 13724.094 ms

Я работаю PostgreSQL 12.1 на сервере Ubuntu 18.04.

Дайте мне знать, если у вас есть дополнительные вопросы. Спасибо!

1 Ответ

2 голосов
/ 28 января 2020

Планировщик не решает использовать определенную стратегию объединения, основанную на глубоких рассуждениях, он просто строит все возможные стратегии объединения, оценивает стоимость и выбирает самую дешевую.

Тем не менее, вложенные l oop Объединения, как правило, лучший выбор, если внешняя таблица мала, так что внутренняя l oop не должна выполняться часто. Кроме того, индекс условия соединения внутренней таблицы может значительно снизить стоимость вложенного соединения l oop и сделать его привлекательной стратегией.

В вашем случае неправильный выбор обусловлен ошибкой -stimate:

Foreign Scan on wind_forecast_recent w  (cost=... rows=1 ...) (actual ... rows=7 ...)

Это вызывает выполнение внутреннего l oop 7 раз, а не один раз, так что время выполнения составляет 70 секунд, а не 10.

Вы должны собрать статистика таблиц по wind_forecast_recent:

ANALYZE wind_forecast_recent;

Помните, что автоматический анализ не обрабатывает сторонние таблицы; Вы должны позаботиться об этом сами.

Если это не сработает, вы можете попробовать установить параметр use_remote_estimate для внешней таблицы и убедиться, что статистика таблицы точна в удаленной базе данных. .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...