В каком случае при наличии индекса будет фулскан
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда БД выбирает Full Scan несмотря на наличие индекса
Оптимизатор БД выбирает стратегию на основе стоимости. Иногда full table scan дешевле, чем использование индекса. Рассмотрю основные случаи.
1. Выбирается большой процент строк таблицы
Если индекс вернёт 70% строк таблицы:
SELECT * FROM users WHERE age > 10; -- Если 70% пользователей старше 10
Почему full scan лучше:
- Индекс: отсортирован по age, ищем по индексу, потом random access в таблицу для каждой строки (I/O операции)
- Full scan: последовательное чтение всей таблицы (намного быстрее благодаря sequential I/O, кэшированию)
Типично: если > 30-40% строк, full scan более эффективен.
2. Индекс не может быть использован (условия не совпадают)
CREATE INDEX idx_users_name ON users(name);
-- Индекс НЕ используется:
SELECT * FROM users WHERE LOWER(name) = 'john'; -- Функция!
SELECT * FROM users WHERE age + 5 > 50; -- Выражение!
SELECT * FROM users WHERE status IS NULL; -- NULL может не индексироваться
Функции и выражения:
БД не может использовать индекс по name, если применены трансформации.
-- Решение: функциональный индекс
CREATE INDEX idx_name_lower ON users(LOWER(name));
SELECT * FROM users WHERE LOWER(name) = 'john'; -- Теперь используется!
3. Статистика устаревшая (из-за VACUUM)
Если таблица сильно изменилась, но ANALYZE не запустился:
-- Таблица содержит 10M строк, но БД думает что 100k
SELECT * FROM orders WHERE status = 'cancelled'; -- Если думает мало строк, выберет индекс
Решение:
ANALYZE table_name; -- Пересчитать статистику
EXPLAIN SELECT ... -- Проверить новый план
4. Индекс частичный или неподходящий
-- Частичный индекс
CREATE INDEX idx_active_users ON users(user_id) WHERE is_active = true;
SELECT * FROM users WHERE is_active = false; -- Full scan! (не в индексе)
5. Индекс на очень маленькой таблице
-- Таблица на 10 строк
SELECT * FROM statuses WHERE id = 5;
-- Full scan выбирается потому что таблица целиком в памяти
-- Чтение индекса + доступ к таблице медленнее
6. BETWEEN с широким диапазоном
CREATE INDEX idx_price ON products(price);
SELECT * FROM products WHERE price BETWEEN 0 AND 1000000; -- Full scan
SELECT * FROM products WHERE price BETWEEN 100 AND 110; -- Index Scan
7. OR условия без правильной индексизации
SELECT * FROM users
WHERE age > 50 OR country = 'USA'; -- Full scan
-- Индекс на age + индекс на country не помогут
-- Решение: composite index или два отдельных индекса + union
8. JOIN с неправильным порядком таблиц
SELECT *
FROM large_table l
JOIN small_table s ON l.id = s.id;
-- Если БД выберет большую таблицу на сканирование первой
-- может быть full scan по large_table
9. LIMIT без ORDER BY на малые числа
SELECT * FROM logs ORDER BY created_at DESC LIMIT 10;
-- Если индекс на created_at descending
-- LIMIT 10 может быть дешевле полной сортировки
-- Иногда full scan быстрее, если в памяти
Диагностика с EXPLAIN
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT * FROM users WHERE status = 'active';
Ищу в выводе:
- Seq Scan — full table scan
- Index Scan — использует индекс
- Index Only Scan — идеально (covering index)
- Rows — сколько строк прогнозирует
- Actual rows — сколько на самом деле
Если Rows и Actual rows сильно отличаются — статистика устаревшая.
Практические советы
1. Не полагайся на индекс для > 30% выборки:
-- Если знаешь что > 30% будет выбрано
SELECT * FROM users WHERE age > 18; -- Full scan нормален
2. Функциональные индексы:
CREATE INDEX idx_email_lower ON users(LOWER(email));
3. Регулярный ANALYZE:
VACUUM ANALYZE; -- Автоматически в PostgreSQL, но можно явно
4. Composite индексы для OR:
-- Вместо OR
CREATE INDEX idx_user_status_active ON users(is_active, user_id);
5. Проверь cost/rows в EXPLAIN:
Если cost full scan < cost index scan → full scan будет выбран.
Итог
Оптимизатор правильно выбирает между индексом и full scan, когда статистика актуальна. 90% проблем с неправильным планом решаются через ANALYZE и понимание, что индекс не всегда быстрее.