← Назад к вопросам
Как определить индекс, если требуется максимальная производительность?
2.8 Senior🔥 121 комментариев
#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Определение индекса для максимальной производительности в БД
Индексы — это критичный элемент оптимизации базы данных. Правильное их определение напрямую влияет на производительность. Рассмотрю стратегию создания индексов.
1. Основные типы индексов
-- B-Tree индекс (по умолчанию, для большинства случаев)
CREATE INDEX idx_users_email ON users(email);
-- Composite индекс (несколько столбцов)
CREATE INDEX idx_users_city_age ON users(city, age);
-- Unique индекс (гарантирует уникальность)
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);
-- Full-text индекс (для поиска текста)
CREATE FULLTEXT INDEX idx_posts_content ON posts(content);
-- Hash индекс (только для точного совпадения)
CREATE INDEX idx_users_id_hash ON users(id) USING HASH;
-- GiST индекс (для геоданных)
CREATE INDEX idx_locations_geo ON locations USING GIST(location);
2. Правило выбора столбцов для индекса
-- ПРАВИЛЬНО: Часто используемые в WHERE условиях
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_created_at ON orders(created_at);
-- ПРАВИЛЬНО: Столбцы с высокой селективностью (много уникальных значений)
CREATE INDEX idx_users_email ON users(email); -- email уникален
CREATE INDEX idx_users_username ON users(username); -- username уникален
-- НЕПРАВИЛЬНО: Столбцы с низкой селективностью
CREATE INDEX idx_users_active ON users(is_active); -- только true/false
-- НЕПРАВИЛЬНО: Редко используемые столбцы
CREATE INDEX idx_users_notes ON users(notes); -- редко в WHERE
3. Composite индексы (индексы на несколько столбцов)
Порядок столбцов критичен:
-- Порядок: WHERE условия слева направо
CREATE INDEX idx_orders_user_status_date ON orders(user_id, status, created_at);
-- Эта таблица показывает, какие запросы будут использовать индекс:
-- ✓ user_id
-- ✓ user_id, status
-- ✓ user_id, status, created_at
-- ✗ status (пропущен user_id в начале)
-- ✗ created_at (пропущены user_id и status)
Примеры:
// Этот запрос будет использовать индекс полностью
// SELECT * FROM orders WHERE user_id = 123 AND status = 'COMPLETED' AND created_at > '2024-01-01';
// Этот запрос будет использовать только user_id и status
// SELECT * FROM orders WHERE user_id = 123 AND status = 'COMPLETED';
// Этот запрос НЕ будет использовать индекс (пропущен user_id)
// SELECT * FROM orders WHERE status = 'COMPLETED' AND created_at > '2024-01-01';
4. Анализ запросов с EXPLAIN
-- MySQL
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'COMPLETED';
-- Смотрим на:
-- type: range, ref, const (хорошо), ALL (плохо)
-- key: какой индекс используется
-- rows: сколько строк сканируется
-- Extra: возможные проблемы
Пример вывода:
id select_type table type key rows Extra
1 SIMPLE orders ref idx_orders_user_status_date 142 Using index condition
5. Стратегия создания индексов
// Сначала анализируем основные запросы
public interface OrderRepository extends JpaRepository<Order, Long> {
// Часто используемые запросы
List<Order> findByUserId(Long userId); // Нужен индекс на user_id
List<Order> findByStatus(String status); // Низкая селективность, может не нужен
List<Order> findByUserIdAndStatus(Long userId, String status); // Нужен composite индекс
List<Order> findByCreatedAtBetween(LocalDate start, LocalDate end); // Нужен индекс
}
Миграция:
-- Создаём индексы для основных операций поиска
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
CREATE INDEX idx_orders_created_at ON orders(created_at);
CREATE INDEX idx_orders_status_created ON orders(status, created_at);
6. Индексы для JOIN операций
-- Для JOIN нужны индексы на столбцы, которые объединяют таблицы
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_products_id ON products(id);
-- Запрос будет использовать эти индексы:
SELECT o.*, oi.*, p.* FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.user_id = 123;
7. Индексы для SORT и GROUP BY
-- ORDER BY может использовать индекс для сортировки
CREATE INDEX idx_orders_created_at_user_id ON orders(created_at, user_id);
-- Этот запрос не потребует дополнительной сортировки
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC;
-- GROUP BY также может использовать индекс
SELECT status, COUNT(*) FROM orders
WHERE created_at > '2024-01-01'
GROUP BY status;
8. Что НЕ нужно индексировать
-- Малые таблицы (< 1000 строк)
CREATE INDEX idx_statuses_name ON statuses(name); -- Бесполезно
-- Столбцы, редко используемые в WHERE
CREATE INDEX idx_users_notes ON users(notes); -- Редко ищут
CREATE INDEX idx_users_bio ON users(bio); -- Редко ищут
-- Столбцы с низкой селективностью
CREATE INDEX idx_orders_is_paid ON orders(is_paid); -- Только true/false
CREATE INDEX idx_users_gender ON users(gender); -- Мало значений
-- Текстовые поля без полнотекстового поиска
CREATE INDEX idx_posts_content ON posts(content); -- Используй FULLTEXT
9. Monitoring и оптимизация
-- Найти неиспользуемые индексы (MySQL)
SELECT * FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_NAME='orders' AND COUNT_READ=0;
-- Найти медленные запросы (MySQL)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
-- Анализ индексов (PostgreSQL)
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE tablename='orders';
-- Статистика использования индексов (PostgreSQL)
SELECT schemaname, tablename, indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE tablename='orders'
ORDER BY idx_scan DESC;
10. Полная стратегия индексирования
@Entity
@Table(name = "orders", indexes = {
// Основные WHERE условия
@Index(name = "idx_user_id", columnList = "user_id"),
@Index(name = "idx_status", columnList = "status"),
@Index(name = "idx_created_at", columnList = "created_at"),
// Composite индексы для частых комбинаций
@Index(name = "idx_user_status", columnList = "user_id,status"),
@Index(name = "idx_status_date", columnList = "status,created_at"),
// Уникальные индексы
@Index(name = "idx_order_number_unique", columnList = "order_number", unique = true)
})
public class Order {
@Id
private Long id;
@Column(nullable = false)
private Long userId;
@Column(nullable = false)
private String status;
@Column(nullable = false)
private LocalDateTime createdAt;
@Column(unique = true)
private String orderNumber;
}
Checklist для создания индексов
- Анализируй медленные запросы — используй EXPLAIN
- Индексируй столбцы в WHERE — начни отсюда
- Используй composite индексы — для частых комбинаций
- Помни про порядок — левый префикс важен
- Смотри на селективность — уникальные значения = хороший индекс
- Балансируй reads vs writes — больше индексов = медленнее UPDATE/INSERT
- Мониторь использование — удаляй ненужные
- Регулярно ANALYZE — для обновления статистики
- Тестируй на боевых данных — с реальными объёмами
- Документируй решения — почему каждый индекс нужен
Правильные индексы могут улучшить производительность в 100+ раз!