middle
Как читать план выполнения запроса?
План выполнения — это дерево узлов (nodes). Выполнение идёт от внутренних узлов к внешним (снизу вверх). Чтение плана — ключевой навык для оптимизации SQL.
Основные типы узлов сканирования
| Узел | Описание |
|---|---|
Seq Scan |
Последовательное чтение всей таблицы |
Index Scan |
Чтение через индекс, затем обращение к таблице за данными |
Index Only Scan |
Все нужные данные уже есть в индексе |
Bitmap Index Scan + Bitmap Heap Scan |
Двухфазное: сначала собирается битовая карта по индексу, затем читаются страницы |
Типы соединений (JOIN)
| Узел | Описание |
|---|---|
Nested Loop |
Для каждой строки внешнего набора ищем во внутреннем. Хорош при малом внешнем наборе |
Hash Join |
Строится хеш-таблица по одной стороне. Эффективен для больших наборов |
Merge Join |
Оба набора сортируются, затем сливаются. Эффективен, если данные уже отсортированы |
Пример чтения сложного плана
EXPLAIN ANALYZE
SELECT t.id, t.amount, c.name
FROM transactions t
JOIN clients c ON t.client_id = c.id
WHERE t.created_at > '2024-01-01'
AND t.amount > 50000;
Hash Join (cost=30.00..150.00 rows=100 width=48) (actual time=0.5..2.1 rows=87 loops=1)
Hash Cond: (t.client_id = c.id)
-> Bitmap Heap Scan on transactions t (cost=10.00..120.00 rows=100 width=24) (actual time=0.3..1.5 rows=87 loops=1)
Recheck Cond: (created_at > '2024-01-01')
Filter: (amount > 50000)
Rows Removed by Filter: 213
-> Bitmap Index Scan on idx_tx_date (cost=0.00..9.97 rows=300 width=0) (actual time=0.2..0.2 rows=300 loops=1)
Index Cond: (created_at > '2024-01-01')
-> Hash (cost=15.00..15.00 rows=500 width=32) (actual time=0.1..0.1 rows=500 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 40kB
-> Seq Scan on clients c (cost=0.00..15.00 rows=500 width=32) (actual time=0.01..0.05 rows=500 loops=1)
Planning Time: 0.2 ms
Execution Time: 2.3 ms
На что обращать внимание
- Большое расхождение между estimated и actual rows — устаревшая статистика, нужен
ANALYZE - Seq Scan на большой таблице с фильтром — возможно, не хватает индекса
- Rows Removed by Filter — строки прочитаны, но отброшены; признак неоптимального индекса
- Buffers: shared read — чтение с диска (медленно), shared hit — чтение из кеша
На собеседовании: не нужно помнить все типы узлов. Покажите, что умеете читать план: снизу вверх, сравнивать estimated vs actual rows, искать Seq Scan на больших таблицах и «Rows Removed by Filter». Это три красных флага, которые решают 80% проблем производительности.