Как я могу “передумать” при чтении плана запросов PostgreSQL?

Я провел более чем час сегодня, озадачивающий сам по плану запросов, который я не мог понять. Запрос был UPDATE и это просто не работало бы вообще. Полностью заведенный в тупик: pg_locks показал, что это ничего не ожидало также. Теперь, я не считаю меня лучшим или худшим читателем плана запросов, но я нахожу этого исключительно трудным. Я задаюсь вопросом, как каждый читает их? Существует ли методология, за которой тузы Pg следуют для точного определения ошибки?

Я планирую задавание другого вопроса относительно того, как работать вокруг этой проблемы, но прямо сейчас я говорю конкретно о том, как считать эти типы планов.

                                         QUERY PLAN                                         
--------------------------------------------------------------------------------------------
 Nested Loop Anti Join  (cost=47680.88..169413.12 rows=1 width=77)
   Join Filter: ((co.fkey_style = v.chrome_styleid) AND (co.name = o.name))
   ->  Nested Loop  (cost=5301.58..31738.10 rows=1 width=81)
         ->  Hash Join  (cost=5301.58..29722.32 rows=229 width=40)
               Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))
               ->  Seq Scan on options io  (cost=0.00..20223.32 rows=23004 width=36)
                     Filter: (name IS NULL)
               ->  Hash  (cost=4547.33..4547.33 rows=36150 width=24)
                     ->  Seq Scan on vehicles iv  (cost=0.00..4547.33 rows=36150 width=24)
                           Filter: (date_sold IS NULL)
         ->  Index Scan using options_pkey on options co  (cost=0.00..8.79 rows=1 width=49)
               Index Cond: ((co.fkey_style = iv.chrome_styleid) AND (co.code = io.code))
   ->  Hash Join  (cost=42379.30..137424.09 rows=16729 width=26)
         Hash Cond: ((v.lot_id = o.lot_id) AND ((v.vin)::text = (o.vin)::text))
         ->  Seq Scan on vehicles v  (cost=0.00..4547.33 rows=65233 width=24)
         ->  Hash  (cost=20223.32..20223.32 rows=931332 width=44)
               ->  Seq Scan on options o  (cost=0.00..20223.32 rows=931332 width=44)
(17 rows)

Проблема с этим планом запросов - я полагаю, что понимаю - вероятно, лучше всего сказан RhodiumToad (он определенно лучше в этом, таким образом, я буду держать пари на его объяснении, являющемся лучше) irc://irc.freenode.net/#postgresql:

о, тот план потенциально имеет катастрофические последствия, проблема с тем планом состоит в том, что он выполняет чрезвычайно дорогое хэширование для каждой строки, проблемой является оценка rows=1 от другого соединения, и планировщик думает, что нормально помещать чрезвычайно дорогой запрос во внутренний путь nestloop, где внешний путь, как оценивается, возвращает только одну строку. с тех пор, очевидно, оценкой планировщика дорогая часть будет только выполнена однажды, но это имеет очевидную тенденцию действительно испортить на практике проблему, то, что планировщик верит ее собственным оценкам идеально, планировщик должен знать, что различие между "предполагаемым возвращает 1 строку" и "не возможный возвратить больше чем 1 строку", но нисколько не ясно, как включить это в существующий код

Он продолжает:

это может влиять на любое соединение, но обычно присоединяется против подзапросов, наиболее вероятны

Теперь то, когда я прочитал этот план первая вещь, я заметил, было Nested Loop Anti Join, это имело стоимость 169,413 (Я буду придерживаться верхних границ). Это Антисоединение ломается к результату a Nested Loop за счет 31,738, и результат a Hash Join по стоимости 137,424. Теперь, 137,424, намного больше, чем 31,738 таким образом, я знал, что проблемой было Хэширование.

Затем я продолжаю двигаться к EXPLAIN ANALYZE сегмент Hash Join за пределами запроса. Это выполнилось в 7 secs. Я удостоверился, что были индексы на (lot_id, vin), и (co.code, и v.code) - было. Я отключил seq_scan и hashjoin индивидуально и уведомление увеличение скорости меньше чем 2 секунд. Не около достаточно для составления, почему это не прогрессировало после часа.

Но, в конце концов, это я полностью неправ! Да, это была более медленная часть запроса, но потому что rows="1" бит (я предполагаю, что он шел Nested Loop Anti Join). Здесь это - ошибка (отсутствие способности) в планировщике, неверно оценивающем сумму строк? Как я, как предполагается, читаю в это, чтобы прийти к тому же заключению RhodiumToad ?

Это просто rows="1" это, как предполагается, инициировало меня понимающий это?

Я действительно работал VACUUM FULL ANALYZE на всех таблицах, включенных, и это - Postgresql 8.4.

19
задан halfer 15 December 2018 в 23:40
поделиться

2 ответа

Чтобы разрешить подобные проблемы, требуется некоторый опыт в том, где что-то может пойти не так. Но чтобы найти проблемы в планах запросов, попробуйте проверить созданный план изнутри, проверьте, соответствуют ли оценки количества строк и оценки затраченному времени. Кстати. две оценки затрат не являются нижними и верхними границами, первая - это оценочная стоимость производства первой строки вывода, второе число - это оценочная общая стоимость, см. поясняющую документацию для получения подробной информации, есть также некоторые Доступна плановая документация . Это также помогает узнать, как работают различные методы доступа. В качестве отправной точки Википедия содержит информацию о вложенном цикле , хэше и объединениях слиянием .

В вашем примере вы должны начать с:

           ->  Seq Scan on options io  (cost=0.00..20223.32 rows=23004 width=36)
                 Filter: (name IS NULL)

Выполните EXPLAIN ANALYZE SELECT * FROM options WHERE name IS NULL; и посмотрите, соответствуют ли возвращенные строки оценке. 2-кратное отклонение обычно не проблема, вы пытаетесь определить разницу на порядок величины.

Затем см. EXPLAIN ANALYZE SELECT * FROM cars, WHERE date_sold IS NULL; возвращает ожидаемое количество строк.

Затем перейдите на один уровень вверх к хеш-соединению:

     ->  Hash Join  (cost=5301.58..29722.32 rows=229 width=40)
           Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))

Посмотрите, EXPLAIN ANALYZE SELECT * FROM транспортных средств, КАК iv ВНУТРЕННИЕ параметры СОЕДИНЕНИЯ io ON (io.lot_id = iv.lot_id) AND ((io.vin) :: text = (iv.vin) :: text) ГДЕ iv.date_sold IS NULL И io.name IS NULL; дает 229 строк.

Еще один уровень выше добавляет INNER JOIN options co ON (co.fkey_style = iv.chrome_styleid) AND (co.code = io.code) и, как ожидается, вернет только одну строку. Вероятно, здесь проблема заключается в том, что, если фактическое количество строк изменяется от 1 до 100, общая оценка стоимости обхода внутреннего цикла содержащего вложенного цикла снижается в 100 раз.

Основная ошибка заключается в том, что планировщик, вероятно, предполагает, что два предиката для объединения в co не зависят друг от друга и умножают их избирательность. Хотя на самом деле они могут быть сильно коррелированы, и избирательность ближе к MIN (s1, s2), а не к s1 * s2.

23
ответ дан 30 November 2019 в 04:29
поделиться

Вы АНАЛИЗИЛИ таблицы? А что pg_stats говорит об этих таблицах? План запроса основан на статистике, она должна быть в порядке. А какой версией пользуетесь? 8.4?

Затраты можно рассчитать, используя статистику, количество связанных страниц, количество строк и настройки в postgresql.conf для констант затрат планировщика.

work_mem также задействован, он может быть слишком низким и вынудить планировщик выполнить seqscan, чтобы убить производительность ...

2
ответ дан 30 November 2019 в 04:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: