← Назад к вопросам
На что влияет порядок полей добавляемых в индекс?
3.0 Senior🔥 91 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Влияние порядка полей в индексе на производительность БД
Быстрый ответ
Порядок полей в индексе критически влияет на:
- Возможность использования индекса (Index Selectivity)
- Скорость выполнения запросов (Query Performance)
- Размер индекса (Index Size)
- Эффективность диапазонных запросов (Range Queries)
1. Правило ESR (Equality, Sort, Range)
Оптимальный порядок полей в индексе:
INDEX (equality_fields, sort_fields, range_fields)
Пример:
-- Создаём индекс
CREATE INDEX idx_users ON users (status, created_at, age);
-- ^1 ^2 ^3
-- Equality Sort Range
2. Equality Fields — главные на начало
Суть: Поля с точным совпадением должны быть первыми.
-- Таблица
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255),
status VARCHAR(50),
age INT,
salary DECIMAL(10, 2)
);
-- Плохо: status в конце индекса
CREATE INDEX idx_bad ON users (age, salary, status);
-- Хорошо: status первый (equality filter)
CREATE INDEX idx_good ON users (status, age, salary);
-- Запрос
SELECT * FROM users
WHERE status = 'active' -- Equality
AND age > 25 -- Range
AND salary > 50000; -- Range
-- Индекс idx_good:
-- ✅ Использует (status) из индекса — быстро найти активных
-- ❌ Индекс idx_bad:
-- ❌ Не может начать с status! Полный scan по (age, salary)
3. Sort Fields — для ORDER BY
Суть: Поля сортировки должны идти после equality полей.
-- Таблица заказов
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
status VARCHAR(50),
created_at TIMESTAMP,
amount DECIMAL(10, 2)
);
-- Плохой индекс
CREATE INDEX idx_bad ON orders (amount, created_at, customer_id, status);
-- Хороший индекс (ESR)
CREATE INDEX idx_good ON orders (customer_id, status, created_at, amount);
-- ^1 ^2 ^3 ^4
-- Equality Equality Sort Range
-- Запрос
SELECT * FROM orders
WHERE customer_id = 123 -- Equality 1
AND status = 'pending' -- Equality 2
ORDER BY created_at ASC; -- Sort field
-- idx_good:
-- ✅ Индекс отсортирован по (customer_id, status, created_at)
-- ✅ Находит все заказы customer_id=123 AND status='pending'
-- ✅ Уже отсортированы по created_at — без дополнительной сортировки!
-- Результат: очень быстро
-- idx_bad:
-- ❌ Начинает с amount — не может быстро найти по customer_id
-- ❌ Требует сортировки результатов
4. Range Fields — на конец
Суть: Диапазонные фильтры (<, >, BETWEEN) идут последними.
-- Таблица продуктов
CREATE TABLE products (
id INT PRIMARY KEY,
category_id INT,
brand_id INT,
price DECIMAL(10, 2),
stock INT
);
-- Плохой индекс
CREATE INDEX idx_bad ON products (price, stock, category_id, brand_id);
-- ^Range fields в начале!
-- Хороший индекс (ESR)
CREATE INDEX idx_good ON products (category_id, brand_id, price, stock);
-- ^1 ^2 ^3Range ^4Range
-- Equality Equality Range Range
-- Запрос
SELECT * FROM products
WHERE category_id = 5 -- Equality
AND brand_id = 10 -- Equality
AND price BETWEEN 100 AND 500 -- Range
AND stock > 10; -- Range
-- idx_good выполнение:
-- 1. Находит records где (category_id=5, brand_id=10) — O(log n)
-- 2. Фильтрует price BETWEEN 100 AND 500 — сканирует только нужный диапазон
-- 3. Фильтрует stock > 10
-- Результат: очень эффективно
-- idx_bad выполнение:
-- 1. Не может начать с category_id — требует полный scan
-- 2. Полный scan по price BETWEEN
-- Результат: медленно
5. Практические примеры
Пример 1: E-commerce фильтры
CREATE TABLE products (
id INT PRIMARY KEY,
category INT, -- Equality
brand INT, -- Equality
created_at DATE, -- Sort
price DECIMAL, -- Range
rating FLOAT -- Range
);
-- Типичный запрос:
SELECT * FROM products
WHERE category = 5
AND brand = 10
ORDER BY created_at DESC
LIMIT 20;
-- ✅ Правильный индекс (ESR)
CREATE INDEX idx_products ON products
(category, brand, created_at, price, rating);
-- ^Equality ^Equa ^Sort
-- ❌ Неправильный индекс
CREATE INDEX idx_products_bad ON products
(created_at, category, brand, price);
-- created_at первый — не помогает category фильтру!
Пример 2: Логирование событий
CREATE TABLE events (
id INT PRIMARY KEY,
user_id INT, -- Equality
event_type VARCHAR, -- Equality
timestamp DATETIME, -- Sort
duration INT -- Range
);
-- Типичный запрос: последние события пользователя
SELECT * FROM events
WHERE user_id = 123
AND event_type = 'click'
ORDER BY timestamp DESC;
-- ✅ Правильный индекс
CREATE INDEX idx_events ON events
(user_id, event_type, timestamp DESC);
-- ^E1 ^E2 ^Sort
-- Выполнение:
-- 1. Индекс найдёт все (user_id=123, event_type='click')
-- 2. Уже отсортированы по timestamp DESC
-- 3. Нет дополнительной сортировки!
Пример 3: Множественные диапазоны
CREATE TABLE analytics (
date DATE, -- Equality (часто используется)
country VARCHAR, -- Equality
region VARCHAR, -- Range (может быть диапазон или точно)
revenue DECIMAL, -- Range
visits INT -- Range
);
-- Запрос 1: Точные значения
SELECT SUM(revenue) FROM analytics
WHERE date = '2025-03-22'
AND country = 'USA'
AND region > 'NY'; -- Range
-- Запрос 2: Диапазон по дате
SELECT * FROM analytics
WHERE date BETWEEN '2025-01-01' AND '2025-03-22'
AND country = 'USA'
ORDER BY revenue DESC;
-- ✅ Комбинированный индекс
CREATE INDEX idx_analytics ON analytics
(country, date, region, revenue);
-- ^E ^E/R ^Range ^Range
-- Этот индекс хорошо работает для обоих запросов
6. Индексная селективность
Селективность = % уникальных значений / всего записей
CREATE TABLE users (
id INT,
status VARCHAR, -- Селективность 0.0001 (5 значений)
email VARCHAR UNIQUE, -- Селективность 0.99 (почти все уникальны)
age INT -- Селективность 0.05 (много повторов)
);
-- ✅ Правильный порядок (от наихудшей к лучшей селективности)
-- Это помогает скорее отсеять записи
CREATE INDEX idx_users ON users (status, age, email);
-- ^Low ^Medium ^High
-- ❌ Неправильный порядок
CREATE INDEX idx_users_bad ON users (email, age, status);
-- ^High ^Medium ^Low
-- email найдёт одну запись, но неэффективнее для группировки
7. Влияние на разные типы запросов
Точное совпадение (WHERE ... AND ...)
INDEX (a, b, c) подходит для:
- WHERE a = 1 AND b = 2 AND c = 3 ✅
- WHERE a = 1 AND b = 2 ✅
- WHERE a = 1 ✅
- WHERE b = 2 ❌ Не может начать со второго поля!
- WHERE c = 3 ❌ Не может прыгнуть на третье
Диапазон (BETWEEN, <, >)
INDEX (a, b, c) и WHERE a = 1 AND b > 5:
- Находит все где a=1
- Затем сканирует где b > 5 (именно поэтому b на втором месте)
INDEX (a, b, c) и WHERE a = 1 AND b > 5 AND c = 3:
- ✅ Использует (a, b) для диапазона
- ❌ НЕ может использовать c = 3 из индекса
(потому что b диапазон, потом идёт c)
Сортировка (ORDER BY)
-- Без ORDER BY индекс
SELECT * FROM users
WHERE status = 'active'
ORDER BY created_at;
-- Требует сортировка! ❌
-- С индексом (status, created_at)
INDEX (status, created_at)
-- Находит active users, уже отсортированы ✅
8. Правила выбора порядка
- Equality fields first — field с
=в WHERE - Sort fields next — fields в ORDER BY
- Range fields last — fields с
<,>,BETWEEN
Суть: Порядок в индексе должен совпадать с порядком условий в запросе:
WHERE a = 1 → Equality (первый в индексе)
AND b = 2 → Equality (второй в индексе)
AND c > 10 → Range (третий в индексе)
ORDER BY d → Sort (последний в индексе)
INDEX (a, b, c, d)
9. Java + БД: практический пример
public class UserRepository {
// Запрос: пользователи с status='active', отсортированные по дате
public List<User> findActiveUsers(LocalDate fromDate) {
// SELECT * FROM users
// WHERE status = 'active'
// AND created_date >= ?
// ORDER BY created_date DESC;
// Нужен индекс:
// CREATE INDEX idx_users ON users (status, created_date DESC);
// ✅ (status) для быстрого поиска
// ✅ (created_date DESC) для сортировки без доп. операции
}
// Запрос: пользователи по возрастному диапазону
public List<User> findUsersByAge(int minAge, int maxAge, String city) {
// SELECT * FROM users
// WHERE city = 'Moscow'
// AND age BETWEEN ? AND ?;
// Нужен индекс:
// CREATE INDEX idx_users_age ON users (city, age);
// ✅ (city) для точного совпадения
// ✅ (age) для диапазона
}
}
Заключение
Порядок полей в индексе влияет на:
- Использование индекса — неправильный порядок может привести к полному сканированию таблицы
- Скорость запросов — может быть разница в 1000x раз
- Сортировка результатов — индекс может заменить ORDER BY
- Размер индекса — несколько правильных индексов дешевле одного большого
Правило ESR:
- Equality fields first
- Sort fields next
- Range fields last
Правильно выбранный порядок полей в индексе — одно из самых важных решений для оптимизации производительности БД!