Частичные индексы в PostgreSQL
В таблице заказов миллионы строк, но интересуют нас обычно только «зависшие» — те, что в статусе pending. Обычный индекс по (status, created_at) справляется, но он большой и обновляется при каждой вставке. Частичный индекс хранит только нужные строки:
CREATE INDEX CONCURRENTLY orders_pending_created_idx
ON orders (created_at)
WHERE status = 'pending';
Запрос, который им воспользуется:
SELECT id, created_at
FROM orders
WHERE status = 'pending'
AND created_at < now() - interval '1 hour'
ORDER BY created_at
LIMIT 100;
Что важно помнить
- Планировщик берёт частичный индекс, только если может доказать, что условие запроса влечёт условие индекса.
status = 'pending'в тексте запроса — да;status = $1в общем (generic) плане — нет, значение параметра там неизвестно. - С prepared statements PostgreSQL первые несколько выполнений строит отдельные планы под конкретные значения, а потом может перейти на общий — и индекс внезапно «пропадёт». Помогает литерал в запросе или
SET plan_cache_mode = force_custom_planдля этого места. CREATE INDEX CONCURRENTLYне блокирует запись в таблицу, но его нельзя выполнять внутри транзакции, а после ошибки остаётся невалидный индекс — его нужно удалить и создать заново.
Проверить, что индекс действительно используется: EXPLAIN (ANALYZE, BUFFERS) на реальном запросе и счётчик idx_scan в pg_stat_user_indexes.