← Назад к вопросам

На что влияет порядок полей добавляемых в индекс?

3.0 Senior🔥 91 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Влияние порядка полей в индексе на производительность БД

Быстрый ответ

Порядок полей в индексе критически влияет на:

  1. Возможность использования индекса (Index Selectivity)
  2. Скорость выполнения запросов (Query Performance)
  3. Размер индекса (Index Size)
  4. Эффективность диапазонных запросов (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. Правила выбора порядка

  1. Equality fields first — field с = в WHERE
  2. Sort fields next — fields в ORDER BY
  3. 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) для диапазона
    }
}

Заключение

Порядок полей в индексе влияет на:

  1. Использование индекса — неправильный порядок может привести к полному сканированию таблицы
  2. Скорость запросов — может быть разница в 1000x раз
  3. Сортировка результатов — индекс может заменить ORDER BY
  4. Размер индекса — несколько правильных индексов дешевле одного большого

Правило ESR:

  • Equality fields first
  • Sort fields next
  • Range fields last

Правильно выбранный порядок полей в индексе — одно из самых важных решений для оптимизации производительности БД!