Почему PostgreSQL может отказаться делать поиск по индексу на поле?

Ответ

Планировщик запросов PostgreSQL может принять решение не использовать индекс, если полное сканирование таблицы (sequential scan) оценивается как более эффективное. Основные причины:

  1. Низкая селективность индекса: Если условие отбирает большую часть таблицы (например, WHERE active = true, где true у 90% строк), сканирование всей таблицы может быть быстрее из-за накладных расходов на случайный доступ к индексу и затем к таблице.
  2. Несоответствие типов данных или использование функций:

    -- Индекс по текстовому полю email не будет использован из-за неявного приведения
    SELECT * FROM users WHERE email = 123;
    
    -- Индекс по timestamp полю created_at не будет использован из-за функции
    SELECT * FROM orders WHERE DATE(created_at) = '2023-01-01';
  3. Недостаточный объем данных: Для очень маленьких таблиц (часто менее ~1000 строк) накладные расходы на использование индекса превышают выгоду.
  4. Устаревшая статистика: PostgreSQL использует статистику для оценки селективности. Если статистика устарела (ANALYZE не запускался давно), планировщик может ошибиться в выборе плана.
  5. Отсутствие подходящего индекса: Частичный индекс (CREATE INDEX ... WHERE ...) не будет использован, если условие запроса не покрывается его предикатом.

Как проверить? Используйте EXPLAIN или EXPLAIN ANALYZE перед запросом, чтобы увидеть выбранный план выполнения.

Ответ 18+ 🔞

А, ну это же классика, ёпта! Смотри, бывает так: пишешь ты красивый запрос, индекс настрогал, а эта мартышлюшка PostgreSQL берёт и игнорирует его, как будто его нет. Сам от себя охуеешь, когда такое увидишь в EXPLAIN ANALYZE.

Вот основные причины, почему так происходит, по полочкам:

  1. Селективность на нуле. Это когда твоё условие отбирает овердохуища записей. Ну, например, WHERE active = true, а true у 90% строк в таблице. Планировщик смотрит на это и думает: «Ну нахуя мне прыгать по индексу, а потом ещё по таблице, если можно просто прочитать всё подряд одним махом?». И он прав, кстати. Последовательное сканирование (sequential scan) в таком случае часто быстрее.

  2. Ты сам всё проебал. То есть, несоответствие типов или обёртывание поля в функцию. Смотри, вот живой пример:

    -- Тут у тебя индекс по text полю email, а ты сравниваешь с числом. PostgreSQL попытается привести типы, но индекс уже накрылся медным тазом.
    SELECT * FROM users WHERE email = 123;
    
    -- А тут ты взял timestamp поле created_at и обернул в DATE(). Всё, пиши пропало. Индекс по created_at лежит мёртвым грузом.
    SELECT * FROM orders WHERE DATE(created_at) = '2023-01-01';

    Планировщик видит эту хитрожопую операцию и говорит: «Чувак, я не могу использовать индекс для этого выражения, да похуй, буду сканировать всё».

  3. Таблица — просто манда с ушами. Если в таблице пара сотен строк, то возня с индексом (случайные чтения) будет дороже, чем просто прочитать эти три листика данных целиком. Для маленьких таблиц последовательное сканирование — это норма.

  4. Статистика устарела, как будто на дворе 2002-й год. PostgreSQL не шарит, что творится в твоей таблице, если ты давно не запускал ANALYZE. Он может думать, что у тебя там 100 уникальных значений, а их уже миллион. И на основе этой кривой статистики примет идиотское решение. Запусти ANALYZE table_name; — иногда это решает все проблемы.

  5. Индекс есть, но он не подходит. Создал ты, допустим, частичный индекс CREATE INDEX ... WHERE status = 'active'. А в запросе у тебя WHERE status = 'pending'. Ну и какой тут, нахуй, индекс? Правильно, никакой. Доверия к такому индексу — ноль ебать.

Как проверить, что за пиздец творится? Да элементарно, чувак. Прилепи перед своим запросом EXPLAIN или, что лучше, EXPLAIN ANALYZE. Он тебе вывалит весь план, покажет, что сканируется, какие индексы используются (или не используются), и сколько времени на это ушло. Вот тогда и поймёшь, в чём корень зла.