Запрос по 300 млн строк отваливается по таймауту — как через EXPLAIN ANALYZE найти виновника?
Запрос дашборда по таблице events в 300 млн строк отваливается по таймауту. Его план снят через EXPLAIN (ANALYZE, BUFFERS):
Hash Join (cost=.. rows=1200 ..) (actual time=.. rows=48250213 ..)
-> Seq Scan on events (cost=.. rows=1200) (actual rows=300000000 ..)
Filter: (country = 'US')
Rows Removed by Filter: 251749787
-> Hash (actual rows=1000 ..)
-> Seq Scan on dim_country (actual rows=1000 ..)
Определите причину и назовите исправление.
Читают снизу вверх узел, чьи фактические время и строки доминируют. Здесь Seq Scan по 300 млн events оценён в 1200 строк, а вернул 300 млн — огромный разрыв оценки и факта из-за устаревшей статистики и отсутствия индекса на country. Фикс: ANALYZE events и индекс на фильтруемом столбце.
- ✗Поднимать statement_timeout вместо починки плана
- ✗Винить алгоритм hash-join, а не оценку и отсутствие индекса
- ✗Читать Rows Removed by Filter как признак сломанного фильтра
- →Как строка BUFFERS подтверждает, что узкое место — Seq Scan?
- →Почему плохая оценка строк вводит в заблуждение выбор алгоритма join?
План читают снизу вверх и ищут узел, где фактические время и число строк максимальны. Внутренний Seq Scan on events оценён планировщиком в rows=1200, но фактически вернул rows=300000000, а Rows Removed by Filter показывает: фильтр country = 'US' отбросил 251 млн строк уже ПОСЛЕ чтения всей таблицы. Разрыв оценки и факта в 250 000 раз — признак устаревшей статистики; из-за заниженной оценки планировщик выбрал план, который затем разросся до 48 млн строк.
Две правки:
ANALYZE events; -- обновить статистику
CREATE INDEX ON events (country); -- убрать Seq Scan по фильтру
После ANALYZE оценка приблизится к факту, а индекс по country заменит Seq Scan на Index/Bitmap Scan, читая только строки США вместо всех 300 млн. Не трогайте алгоритм join и не поднимайте таймаут — это лечит симптом, а не причину.