← Назад к вопросам
Является ли составной индекс неупорядоченным?
2.0 Middle🔥 131 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Является ли составной индекс неупорядоченным?
Ответ: Нет, составной индекс УПОРЯДОЧЕН, но порядок сортировки определяется ПОРЯДКОМ КОЛОНОК в определении индекса. Это критически важный момент для понимания производительности запросов в базах данных.
Что такое составной индекс
-- Составной (composite/compound) индекс
-- включает несколько колонок
CREATE INDEX idx_user_profile
ON users(country, city, age);
-- Это НЕ то же самое, что три отдельных индекса!
CREATE INDEX idx_country ON users(country);
CREATE INDEX idx_city ON users(city);
CREATE INDEX idx_age ON users(age);
Порядок в составном индексе
public class CompositeIndexOrdering {
// Индекс (country, city, age) упорядочен так:
//
// Сначала по country (первая колонка)
// Потом по city (вторая колонка) внутри country
// Потом по age (третья колонка) внутри city
//
// На диске выглядит так:
//
// country | city | age | row_id
// --------|-----------|-----|-------
// USA | New York | 25 | 101
// USA | New York | 30 | 102
// USA | Boston | 22 | 103
// USA | Boston | 35 | 104
// Canada | Toronto | 28 | 105
// Canada | Montreal | 31 | 106
// UK | London | 29 | 107
//
// Это УПОРЯДОЧЕНО по (country, city, age)
}
SQL примеры использования составного индекса
1. Query, который использует индекс полностью
-- Индекс: (country, city, age)
-- Эффективно: использует индекс для всех трех колонок
SELECT * FROM users
WHERE country = 'USA'
AND city = 'New York'
AND age > 25;
-- Выполнение: найти в индексе USA → New York,
-- затем range scan по age
2. Query, который использует индекс частично
-- Индекс: (country, city, age)
-- Эффективно: использует индекс для первых двух колонок
SELECT * FROM users
WHERE country = 'USA'
AND city = 'New York';
-- Индекс помогает до (city)
-- age не используется (он не важен для WHERE)
-- Хорошо: использует индекс для country
SELECT * FROM users
WHERE country = 'USA';
-- Индекс помогает найти все USA строки
3. Query, который НЕ использует индекс оптимально
-- Индекс: (country, city, age)
-- Неэффективно: пропускает middle column (city)
SELECT * FROM users
WHERE country = 'USA'
AND age > 25;
-- Индекс используется только для country
-- age условие не может использовать индекс
-- (потому что city в условии отсутствует)
-- Очень неэффективно: начинает со второй колонки
SELECT * FROM users
WHERE city = 'New York';
-- Индекс НЕ используется вообще!
-- (leading column country отсутствует)
Java пример с ORM
public class CompositeIndexJPA {
@Entity
@Table(name = "users",
indexes = {
@Index(name = "idx_country_city_age",
columnList = "country, city, age")
})
public class User {
@Id
private Long id;
@Column(name = "country")
private String country;
@Column(name = "city")
private String city;
@Column(name = "age")
private Integer age;
}
// ХОРОШО: использует индекс полностью
public List<User> findUsersInArea1(
String country, String city, int minAge) {
return entityManager.createQuery(
"SELECT u FROM User u "
+ "WHERE u.country = :country "
+ " AND u.city = :city "
+ " AND u.age > :minAge",
User.class)
.setParameter("country", country)
.setParameter("city", city)
.setParameter("minAge", minAge)
.getResultList();
}
// ХОРОШО: использует индекс для (country, city)
public List<User> findUsersInCity(
String country, String city) {
return entityManager.createQuery(
"SELECT u FROM User u "
+ "WHERE u.country = :country "
+ " AND u.city = :city",
User.class)
.setParameter("country", country)
.setParameter("city", city)
.getResultList();
}
// ПЛОХО: не использует индекс
public List<User> findUsersByAge(
int minAge) {
return entityManager.createQuery(
"SELECT u FROM User u "
+ "WHERE u.age > :minAge",
User.class)
.setParameter("minAge", minAge)
.getResultList();
}
}
Правило Leading Column
public class LeadingColumnRule {
// Индекс: (country, city, age)
//
// Правило Leading Column (Leading Prefix):
// Индекс может использоваться для запроса
// только если WHERE условие содержит
// leading columns без пропусков
//
// ✅ Хорошо:
// - country
// - country, city
// - country, city, age
//
// ❌ Плохо (пропускает middle column):
// - city
// - age
// - city, age
// - country, age (пропускает city!)
//
// Подумай: индекс отсортирован как
// (country ASC) → (city ASC) → (age ASC)
// Если в WHERE пропускаешь country,
// ты не можешь использовать упорядочение
// по city и age
}
Практический пример с EXPLAIN PLAN
-- Индекс: (country, city, age)
-- ✅ EXPLAIN показывает использование индекса
EXPLAIN
SELECT * FROM users
WHERE country = 'USA'
AND city = 'New York'
AND age > 25;
-- Output:
-- Index: idx_country_city_age
-- Type: range
-- Key length: полная длина индекса
-- Rows: относительно мало
-- ❌ EXPLAIN показывает full table scan
EXPLAIN
SELECT * FROM users
WHERE age > 25;
-- Output:
-- Type: ALL (full table scan)
-- Rows: 1000000 (все строки!)
Как выбрать порядок колонок в индексе
1. Equality columns first (Колонки с = первыми)
public class IndexColumnOrdering {
// Плохо: вложил age раньше city
// CREATE INDEX idx_bad ON users(age, city, country);
//
// Хорошо: equality conditions first
// CREATE INDEX idx_good ON users(country, city, age);
//
// Логика: equality columns позволяют
// быстро фильтровать, range columns — скан
// Пример:
// WHERE country = 'USA' (equality, ограничивает диапазон)
// AND city = 'Boston' (equality, еще ограничивает)
// AND age > 30 (range, скан внутри диапазона)
}
2. Порядок based на query patterns
-- Частые запросы:
-- Q1: WHERE country = ? AND city = ?
-- Q2: WHERE country = ?
-- Q3: WHERE country = ? AND city = ? AND age = ?
-- Лучший индекс:
CREATE INDEX idx_queries ON users(
country, -- используется во всех
city, -- используется в Q1 и Q3
age -- используется в Q3
);
-- Этот индекс поддерживает:
-- - Q1: точно (country, city)
-- - Q2: точно (country)
-- - Q3: точно (country, city, age)
3. Фильтрующие vs сортирующие колонки
-- Если нужна сортировка в результатах
CREATE INDEX idx_country_age_city ON users(
country, -- для фильтра WHERE
age, -- для фильтра WHERE + сортировка
city -- для ORDER BY
);
SELECT * FROM users
WHERE country = 'USA'
AND age > 25
ORDER BY city; -- может использовать индекс для сортировки
Различие между составным индексом и отдельными индексами
public class CompositeVsSeparate {
// СОСТАВНОЙ ИНДЕКС
// CREATE INDEX idx ON users(country, city);
//
// Плюсы:
// - Эффективен для queries (country, city)
// - Может быть Index-Only scan
// - Лучше для Range queries
// - Меньше памяти, чем 2 индекса
//
// Минусы:
// - Не эффективен для query только по city
// - Порядок колонок критичен
// ДВА ОТДЕЛЬНЫХ ИНДЕКСА
// CREATE INDEX idx1 ON users(country);
// CREATE INDEX idx2 ON users(city);
//
// Плюсы:
// - Эффективен для любого условия
// - Гибче при изменении queries
//
// Минусы:
// - Больше памяти
// - Медленнее при обновлениях
// - MySQL может использовать только один индекс
// для фильтрации (Index Merge очень медленно)
}
Специальные случаи
1. DESC сортировка в индексе
-- Можешь контролировать направление сортировки
CREATE INDEX idx_mixed ON users(
country ASC, -- возрастающий
age DESC -- убывающий
);
-- Это помогает для
SELECT * FROM users
WHERE country = 'USA'
ORDER BY age DESC; -- может использовать индекс без sorting!
2. Partial Index (фильтрованный индекс)
CREATE INDEX idx_active_users ON users(
country, city
)
WHERE active = true; -- индекс только для активных
-- Это экономит памяти и быстрее
3. B-Tree структура индекса
public class BTreeStructure {
// Составной индекс (country, city, age)
// хранится как B-Tree, где:
//
// 1. Root: все значения country
// 2. Level 2: для каждого country — все city
// 3. Leafs: для каждого (country, city) — все age + row_id
//
// Это создает упорядоченную иерархическую структуру
// которая позволяет быстро найти нужные строки
}
Практические рекомендации
public class BestPractices {
// 1. Анализируй свои queries
// Посмотри, какие WHERE условия чаще всего
// 2. Leading column должна быть selective
// (иметь много уникальных значений)
// 3. Второе место: колонка из WHERE условий
// 4. Последнее место: колонка для сортировки
// SELECT * FROM users
// WHERE country = ? AND city = ?
// ORDER BY age;
// → CREATE INDEX ON (country, city, age)
// 5. Тестируй с EXPLAIN / ANALYZE
// Убедись, что индекс используется
// 6. Избегай слишком много индексов
// каждый индекс замедляет INSERT/UPDATE/DELETE
// 7. Пересматривай индексы регулярно
// query patterns меняются со временем
}
Вывод
Составной индекс УПОРЯДОЧЕН — первая колонка упорядочена по всему диапазону, вторая колонка упорядочена внутри каждого значения первой колонки, и так далее. Порядок колонок в определении индекса критичен для эффективности запросов:
- Используй Leading Column Rule
- Помещай equality conditions перед range conditions
- Тестируй с EXPLAIN PLAN
- Выбирай порядок на основе actual query patterns
Это один из самых важных аспектов оптимизации баз данных.