PostgreSQL Query Plan: что значат Nested Loop, Hash Join и прочие страшные слова

PostgreSQL — мощная СУБД, но без понимания планов выполнения запросов даже опытные разработчики сталкиваются с неожиданными тормозами. В этой статье разберём основные типы JOIN-операций (Nested Loop, Hash Join, Merge Join) и методы доступа к данным (Index Scan, Seq Scan, Bitmap Scan), которые влияют на производительность Rails-приложений. Узнаем, как читать вывод EXPLAIN ANALYZE и оптимизировать запросы с учётом статистики, индексов и настроек work_mem.


Когда ты смотришь на EXPLAIN ANALYZE, первое чувство — легкое замешательство.
Вот краткий словарь, который поможет читать планы PostgreSQL с первого взгляда.

🔁 Nested Loop

Цикл в цикле: для каждой строки из внешней таблицы выполняется поиск во внутренней.

Nested Loop
  -> Seq Scan on orders
  -> Index Scan on users ...

🚨 Медленно, если строк много. Может выполнить 1000+ отдельных запросов.

🛠 Спасает LIMIT, индексы, анализ размера выборки.


🧠 Hash Join

Вместо перебора — Postgres строит хеш-таблицу по одной из таблиц и быстро ищет совпадения.

Hash Join
  -> Seq Scan on users
  -> Hash
    -> Seq Scan on orders

📌 Отлично работает при большом количестве строк, если есть память.


📚 Merge Join

Обе таблицы отсортированы, и Postgres идёт по ним одновременно как merge в merge-sort.

Merge Join
  -> Index Scan on users
  -> Index Scan on orders

⚡ Быстро, если таблицы уже отсортированы по join-ключу.


🔍 Index Scan

Идеально: Postgres использует индекс, чтобы найти только нужные строки.

Index Scan using users_pkey on users

📌 Быстро, если фильтр попадает по индексу.


👣 Seq Scan

Постгрес читает всю таблицу целиком.

Seq Scan on users

🚨 Бывает быстро (если мало строк) или ужасно медленно (если таблица большая).


🧮 Bitmap Index Scan

Промежуточный вариант между Index Scan и Seq Scan. Сначала собираются ссылки, потом идут за данными.

Bitmap Heap Scan on users
  -> Bitmap Index Scan on index_users_on_email

📌 Хорошо работает, когда условие даёт много совпадений.


🧰 Что влияет на выбор плана

  • Размер выборки
  • Наличие и состав индексов
  • Статистика (ANALYZE!)
  • Настройки памяти (work_mem)
  • Связанные условия JOIN, WHERE

🧭 Запомни

Термин Что это Когда быстро
Nested Loop Цикл в цикле Мало строк, быстрый индекс
Hash Join Хеш-таблица Много строк, хорошее распределение
Merge Join Сортированные источники Есть индекс по join-полю
Seq Scan Чтение всей таблицы Маленькая таблица или нет индекса
Index Scan Точное попадание по индексу Хорошие индексы
Bitmap Index Scan Много совпадений, сжатый поиск Альтернатива Seq Scan для индекса

🗓 Дата публикации: 17.01.2025, но это не точно...

PostgreSQL JOIN-операции оптимизация запросов EXPLAIN ANALYZE Rails индексы