← Назад к вопросам

Какие типы сканирования использует планировщик запросов?

1.0 Junior🔥 151 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Типы сканирования в планировщике запросов

Планировщик (Query Planner) выбирает оптимальный способ получить данные. Это критично для производительности. Разберу основные типы сканирования.

1. Full Scan (Полное сканирование)

Читает все строки таблицы последовательно. Самый медленный способ.

-- Full Scan: читает все строки
SELECT * FROM users WHERE status = 'active';

-- В PostgreSQL:
EXPLAIN SELECT * FROM users WHERE status = 'active';
-- Seq Scan on users (cost=0.00..35.50)

Когда используется:

  • Нет индекса по status
  • WHERE условие неселективно (очень много совпадений)
  • Таблица маленькая

2. Index Scan

Использует индекс для быстрого поиска.

-- Создаём индекс
CREATE INDEX idx_users_status ON users(status);

-- Index Scan: использует индекс
SELECT * FROM users WHERE status = 'active';

-- EXPLAIN:
-- Index Scan using idx_users_status on users
-- Index Cond: (status = 'active')

Преимущества:

  • O(log N) вместо O(N)
  • Работает при наличии индекса
  • Очень быстро для selective условий

3. Bitmap Index Scan

Сочетает сканирование индекса с bitmap-структурой. Оптимально для диапазонов.

-- Bitmap Index Scan: используется для диапазонов и OR условий
EXPLAIN SELECT * FROM users WHERE age > 18 AND status = 'active';

-- Bitmap Index Scan using idx_users_age
-- Filter: (age > 18)
-- Bitmap Index Scan using idx_users_status
-- Filter: (status = 'active')

Используется когда:

  • Несколько индексов нужно объединить
  • Диапазоны значений
  • Множественные OR условия

4. Index Only Scan

Все нужные данные уже в индексе, не нужно обращаться к таблице. Самый быстрый вариант.

-- Индекс содержит всё необходимое
CREATE INDEX idx_users_id_email ON users(id, email);

-- Index Only Scan: не обращается к таблице!
SELECT id, email FROM users WHERE id = 123;

-- EXPLAIN:
-- Index Only Scan using idx_users_id_email
-- Index Cond: (id = 123)

Преимущества:

  • Не нужно читать основную таблицу
  • Самый быстрый вариант
  • Работает благодаря covering indexes

5. Hash Join

Для объединения таблиц строит hash-таблицу.

-- Hash Join: одна таблица в памяти, вторая сканируется
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.amount > 100;

-- EXPLAIN:
-- Hash Join (cost=100.00..200.00)
--   Hash Cond: (o.user_id = u.id)
--   -> Seq Scan on orders o
--   -> Hash (cost=50.00..50.00)
--        -> Seq Scan on users u

Определение: одна таблица читается и строится hash-таблица, вторая сканируется с поиском в hash.

6. Nested Loop Join

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

-- Nested Loop: внешняя таблица в цикле
SELECT *
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.id = 5;

-- EXPLAIN:
-- Nested Loop (cost=0.42..2.50)
--   -> Index Scan using idx_users_id
--   -> Index Scan using idx_orders_user_id

Используется:

  • Для маленьких результатов
  • Когда есть индекс на join column
  • Для корреляции между таблицами

7. Merge Join (Sort-Merge Join)

Обе таблицы сортируются, затем merge-ются. Эффективно для больших объёмов.

-- Merge Join: сортировка потом merge
SELECT *
FROM users u
JOIN orders o ON u.id = o.user_id;

-- EXPLAIN:
-- Merge Join
--   -> Sort on u.id
--   -> Sort on o.user_id

Преимущества:

  • Кэш-эффективно
  • Для больших наборов данных
  • Работает с дисковыми операциями

Выбор типа сканирования

ТипСтоимостьКогда использовать
Full ScanO(N)Маленькие таблицы
Index ScanO(log N)Selective условия, есть индекс
Index Only ScanO(log N)Covering index
Bitmap ScanO(N)Множественные условия
Hash JoinO(N+M)Большие таблицы, нет индекса
Nested LoopO(N*M)Маленькие наборы
Merge JoinO(N+M)Отсортированные данные

Практический пример оптимизации

import psycopg2

# Плохо: Full Scan
query = 'SELECT * FROM orders WHERE user_id = 123 AND status = "completed";'
# EXPLAIN shows: Seq Scan on orders (cost=0.00..1000.00)

# Хорошо: добавляем индекс
'CREATE INDEX idx_orders_user_status ON orders(user_id, status);'
# EXPLAIN shows: Index Scan (cost=0.42..2.50)

# Отлично: Index Only Scan
'CREATE INDEX idx_orders_user_id_amount ON orders(user_id, amount);'
'SELECT user_id, amount FROM orders WHERE user_id = 123;'
# EXPLAIN shows: Index Only Scan (cost=0.42..1.50)

Как смотреть план запроса

-- PostgreSQL
EXPLAIN SELECT ...
EXPLAIN ANALYZE SELECT ...  -- Со статистикой

-- MySQL
EXPLAIN SELECT ...
EXPLAIN FORMAT=JSON SELECT ...

-- Ищи в результатах:
-- Seq Scan — полное сканирование
-- Index Scan — сканирование индекса
-- Index Only Scan — данные только в индексе
-- Hash Join/Nested Loop/Merge Join — способ join

Вывод: правильный выбор сканирования может ускорить запрос в 1000 раз. Используй EXPLAIN, смотри Seq Scan и создавай индексы.