Что такое составной (комбинированный) индекс и как правильно его проектировать?

Ответ

Составной индекс — это индекс, созданный на несколько столбцов таблицы. Он оптимизирует запросы, которые используют эти столбцы в условиях WHERE, JOIN или ORDER BY.

Ключевой принцип: правило левого префикса (leftmost prefix). Индекс (col1, col2, col3) может быть эффективно использован для запросов с:

  • col1
  • col1, col2
  • col1, col2, col3

Но НЕ будет эффективен для запросов только с col2 или col3, так как эти столбцы не являются левым префиксом индекса.

Пример создания в SQL:

-- Индекс для ускорения поиска по фамилии и фильтра по возрасту
CREATE INDEX idx_users_lastname_age ON users(last_name, age);

Эффективные запросы для этого индекса:

SELECT * FROM users WHERE last_name = 'Ivanov'; -- Использует префикс индекса
SELECT * FROM users WHERE last_name = 'Ivanov' AND age = 30; -- Использует оба столбца
SELECT * FROM users WHERE last_name = 'Ivanov' ORDER BY age; -- Использует для сортировки

Неэффективный запрос:

SELECT * FROM users WHERE age = 30; -- Полное сканирование, т.к. age не левый префикс

Практические рекомендации:

  1. Порядок столбцов критичен. Первым ставьте столбец с наибольшей селективностью (уникальностью) или тот, который чаще всего используется в условиях равенства (=).
  2. В JPA/Hibernate индекс можно задать аннотацией:
    @Entity
    @Table(indexes = @Index(name = "idx_lastname_age", columnList = "lastName, age"))
    public class User {
        private String lastName;
        private Integer age;
    }
  3. Составные индексы также могут обслуживать запросы только на сортировку (ORDER BY) или группировку (GROUP BY), если порядок столбцов в этих операциях совпадает с порядком в индексе.

Ответ 18+ 🔞

А, составной индекс, говоришь? Ну это ж, блядь, классика жанра! Ситуация, когда ты пытаешься одним махом, как говорится, накрыть сразу несколько своих косяков в запросах. Представь, что у тебя в таблице users лежат фамилии и возраст. И ты постоянно ищешь то по фамилии, то по фамилии и возрасту вместе.

Так вот, составной индекс — это как взять и склеить эти два столбца в одну умную папку, которую база данных будет листать в разы быстрее. Создаётся он элементарно:

CREATE INDEX idx_users_lastname_age ON users(last_name, age);

Вот теперь самое интересное, ёпта! Работает эта штука по правилу левого префикса. Это как с дверью, у которой три замка подряд. Если у тебя ключ только от первого — откроешь. От первого и второго — откроешь. От всех трёх — вообще красота. А вот если ключ только от второго или третьего — нихуя не откроешь, будешь ломиться вхолостую.

То есть наш индекс (last_name, age) поможет, если в запросе есть:

  • Только last_name (первый замок).
  • И last_name, и age вместе (оба замка).

Работает:

-- Ищет по первому столбцу индекса — летит!
SELECT * FROM users WHERE last_name = 'Ivanov';
-- Ищет по обоим — вообще пуля!
SELECT * FROM users WHERE last_name = 'Ivanov' AND age = 30;

А вот это — полный пиздец и сканирование всей таблицы:

-- Возраст — это второй столбец в индексе, без фамилии он нихуя не стоит!
SELECT * FROM users WHERE age = 30;

База посмотрит на этот запрос, на индекс и скажет: «Мужик, ты чё, блядь? Я по этой твоей папке могу быстро найти всех Ивановых, а потом среди них — тридцатилетних. А как я тебе быстро найду всех тридцатилетних, не зная фамилии? Придётся всю таблицу, сука, перерыть!». И перероет. А ты потом будешь охуевать, почему запрос тормозит.

Короче, главный лайфхак: когда создаёшь такой индекс, первым делом суй туда столбец с самой высокой селективностью — тот, в котором больше всего уникальных значений. Или тот, по которому чаще всего ищешь строгое равенство (=).

В Hibernate, если ты вдруг с ним работаешь, это можно сделать прямо в ентити, не вылезая в чистый SQL. Аннотацией прилепить:

@Entity
@Table(indexes = @Index(name = "idx_lastname_age", columnList = "lastName, age"))
public class User {
    private String lastName;
    private Integer age;
}

И ещё прикол: если у тебя в запросе есть ORDER BY last_name, age, то такой индекс может отдать данные уже в отсортированном виде, даже не напрягаясь. Красота же!

Так что думай головой, когда их создаёшь, а не тыкай столбцы как попало. А то накропаешь индексов овердохуища, а толку — ноль ебать.