Зачем использовать составной индекс вместо нескольких индексов по разным колонкам?
Что проверяют
Понимаете ли вы, как база использует индексы в запросе с несколькими условиями, и знаете ли правило порядка колонок. Следом почти всегда спрашивают, поможет ли индекс по (a, b) запросу только по b.
Как отвечать
Возьмите запрос WHERE last_name = ? AND first_name = ?. С двумя отдельными индексами у базы плохой выбор: взять более селективный и досеивать остальное по строкам таблицы или прочитать оба и пересечь результаты (в PostgreSQL это BitmapAnd, в MySQL index merge). Оба варианта дороже одного спуска по B-дереву индекса (last_name, first_name), который сразу приводит к нужным строкам.
Что ещё даёт составной индекс:
- сортировку без сортировки:
WHERE status = ? ORDER BY created_atобслуживается индексом(status, created_at), строки уже идут по порядку, иLIMITчитает только первые; - покрытие: если все нужные колонки есть в индексе, PostgreSQL делает Index Only Scan и не ходит в таблицу. Лишние колонки добавляют через
INCLUDE; - уникальность пары:
UNIQUE (user_id, job_id)защищает от дублей на уровне базы.
Индекс работает по левому префиксу: (a, b) помогает запросам по a и по паре, но не по одному b. Колонки с равенством ставят первыми, диапазонную последней: после > или BETWEEN следующие колонки для поиска уже не работают. Skip scan в MySQL 8 и PostgreSQL 18 смягчает правило префикса, но проектировать в расчёте на него не стоит.
В Java-проекте индекс объявляют в миграции Flyway или Liquibase либо через @Index(columnList = "last_name, first_name") в @Table, а проверяют через EXPLAIN ANALYZE на SQL, который сгенерировал Hibernate.
Где спотыкаются
Ставят колонки в произвольном порядке. Индекс (created_at, status) для запроса с status = ? и диапазоном по дате работает заметно хуже, чем (status, created_at).
Считают, что база идеально склеит любые одиночные индексы. Склейка есть, но это отдельная работа, и на больших выборках она проигрывает составному индексу.
Заводят составной индекс под каждую комбинацию фильтров. Каждый индекс замедляет INSERT и UPDATE, поэтому их строят под реальные частые запросы.