Я потратил более часа сегодня, ломая голову над планом запроса, который я не мог понять. Запрос был 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
:
о, этот план потенциально катастрофичен
проблема с этим планом в том, что он запускает очень дорогой хэш-джойн для каждой строки
проблема заключается в оценке строк = 1 от другого соединения и
планировщик считает, что можно поместить чрезвычайно дорогой запрос во внутренний путь nestloop, где внешний путь должен возвращать только одну строку.
поскольку, по оценкам планировщика, дорогая часть будет запущена только один раз
но у этого есть очевидная тенденция действительно испортить на практике
проблема в том, что планировщик считает свои собственные оценки
в идеале планировщик должен знать разницу между «по оценкам, чтобы вернуть 1 строку» и «невозможно вернуть более 1 строки»
но не совсем понятно, как включить это в существующий код
Он продолжает:
это может повлиять на любое объединение, но обычно объединения с подзапросами являются наиболее вероятными
Теперь, когда я прочитал этот план, первое, что я заметил, было Nested Loop Anti Join
, это стоило 169,413
(я буду придерживаться верхних границ). Это Anti-Join ломается до результата Nested Loop
по стоимости 31,738
и результата Hash Join
по стоимости 137,424
. Теперь 137,424
, намного больше, чем 31,738
, поэтому я знал, что проблема заключалась в хэш-соединении.
Затем я перехожу к EXPLAIN ANALYZE
сегменту Hash Join вне запроса. Выполнено за 7 секунд. Я удостоверился, что были индексы (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.