Index Cond и Filter в плане
Как отличить навигацию по индексу от последующей фильтрации и переписать условия, чтобы составной индекс использовался эффективнее.
PostgreSQLEXPLAINИндексыПроизводительность— Макс, я сломала мозг, — подошла Лена. — Смотри: у меня есть составной индекс по (status, created_at). Я делаю запрос с условиями по обоим этим полям. Но в плане запроса поле status попадает в Index Cond, а created_at — в Filter. Почему он не использует индекс целиком? Это же неэффективно!
Макс отвлекся от своего монитора.
— Поздравляю. Ты только что нашла «серую зону» оптимизации, где запрос вроде бы использует индекс, но не на все сто. Это как купить швейцарский нож и использовать его только для открывания бутылок.
— То есть Filter — это плохо?
— Ну не то чтобы это плохо, но это важный сигнал. Давай на аналогии. Представь, что индекс — это картотека в библиотеке.
-
Index Cond (условие индекса): Ты подходишь к ящику с буквой «Т», потому что ищешь Толстого. Это мгновенный поиск. Ты сразу отсекаешь огромную часть книг.
-
Filter (фильтр): А теперь ты достаешь всю огромную пачку карточек на «Толстой» и начинаешь вручную перебирать их в поисках той, где год издания равен заданному. Это уже дополнительная работа. Это все еще быстрее, чем обыскивать всю библиотеку, но медленнее, чем если бы у тебя был отдельный ящик для «Толстой, 1900».
Лена кивнула:
— Да это я понимаю. Index Cond — это навигация, Filter — это перебор. Но почему created_at ушло в Filter, если оно входит в мой индекс?
Макс развернул её ноутбук.
— Покажи запрос.
SELECT *FROM ordersWHERE status = 'processed' AND date_trunc('month', created_at) = '2025-08-01';— Вот! — воскликнул он. — Ты ищешь не по created_at, а по функции от него. Индекс хранит точные значения created_at, но ничего не знает о результате date_trunc. Поэтому Postgres делает так:
-
Index Cond:
status = 'processed': Быстро находит по индексу все строки с нужным статусом. -
Filter:
date_trunc('month', created_at) = ...: Для каждой найденной строки он вычисляет функцию и проверяет, подходит ли она под условие.
Когда ещё условие попадает в Filter?
-
Преобразование самого столбца: условие вроде
WHERE phone::bigint = 79991234567ищет уже по выражению, а не по исходному текстовому ключу. Без подходящего индекса по выражению прямой поиск по обычному индексу становится недоступен. Простая ошибка типов чаще вообще завершится сообщением об отсутствии оператора, а не тихим Seq Scan. -
Поле в
INCLUDEиндекса: Если ты создала индекс... ON t(a) INCLUDE (b), то полеbне является частью ключа поиска. УсловиеWHERE b = 5уйдет в Filter. -
Нет условия по ведущему столбцу: индекс
(city, street)обычно хуже подходит дляWHERE street = 'Ленина', чем индекс сstreetв начале. Конкретный план зависит от версии PostgreSQL, статистики и доступных способов сканирования, поэтому проверяйте его черезEXPLAIN, а не только по правилу «левого префикса». -
Частая ловушка — когда условия включаются через OR или NOT. Индекс использует только то, что соответствует структуре (обычно первое поле в ключе), а всё остальное фильтруется вручную.
— Так что же, я зря создавала составной индекс? — расстроилась Лена.
— Нет! Ты все еще можешь его использовать эффективно. Например, перепиши условие по created_at без использования функции:
SELECT *FROM ordersWHERE status = 'processed' AND created_at >= timestamp '2025-08-01' AND created_at < timestamp '2025-09-01';— Если условие по функции действительно является частью устойчивого контракта запросов, а created_at имеет тип timestamp without time zone, можно рассмотреть индекс по выражению:
CREATE INDEX ON orders (status, date_trunc('month', created_at));С таким индексом оба твоих условия попадут в Index Cond, и Filter исчезнет.
Для timestamptz выражение должно однозначно фиксировать часовой пояс и быть допустимым для индексного выражения. На практике полуоткрытый диапазон обычно проще, понятнее и не захватывает полночь следующего месяца.
Что запомнить
-
Index Cond: Прямое использование индекса для навигации. Это высшая лига.
-
Filter: Дополнительная проверка строк, уже полученных после сканирования по Index Cond.
-
Появление Filter — это не катастрофа, а подсказка: возможно, твой индекс или запрос можно улучшить.