Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое индексирование?
Индексирование — это один из ключевых способов оптимизации производительности баз данных. Давайте разберёмся, что это такое, как это работает и когда это нужно использовать.
Определение
Индекс (Index) — это структура данных в БД, которая позволяет быстро находить записи без необходимости просмотра всей таблицы. Можно сравнить с оглавлением книги: вместо того чтобы прочитать всю книгу в поисках главы, вы смотрите в оглавление и сразу переходите на нужную страницу.
Как работает индекс
Без индекса (Table Scan):
Табель: User
id | name | email
1 | Alice | alice@example.com
2 | Bob | bob@example.com
3 | Charlie | charlie@example.com
4 | David | david@example.com
Query: SELECT * FROM User WHERE email = 'bob@example.com';
Процесс: Проверяем каждую строку (4 проверки)
С индексом на email:
Индекс: email -> rowid
bob@example.com -> 2
alice@example.com -> 1
charlie@example.com -> 3
Query: SELECT * FROM User WHERE email = 'bob@example.com';
Процесс: Ищем в индексе (логарифмическое время), находим rowid=2, берём строку
Структура индекса — B-Tree
Большинство БД используют B-Tree (сбалансированное дерево поиска):
[B|M]
/ \
[A] [C-L]
/ | \ / | \
...
Преимущества B-Tree:
- Сбалансировано (высота O(log n))
- Хорошо использует дисковый кэш
- Оптимизировано для чтения больших объёмов данных
Типы индексов в Java/SQL
1. Primary Key Index (первичный ключ)
@Entity
public class User {
@Id // Автоматически создаёт уникальный индекс
private Long id;
private String name;
private String email;
}
-- В SQL
CREATE TABLE User (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
-- Запрос использует индекс автоматически
SELECT * FROM User WHERE id = 1; -- Очень быстро O(log n)
2. Unique Index (уникальный индекс)
@Entity
public class User {
@Id
private Long id;
@Column(unique = true) // Создаёт уникальный индекс
private String email;
}
-- SQL
CREATE UNIQUE INDEX idx_user_email ON User(email);
-- Поиск по email быстрый
SELECT * FROM User WHERE email = 'bob@example.com'; -- O(log n)
3. Composite Index (составной индекс)
@Entity
@Table(indexes = @Index(name = "idx_user_name_email",
columnList = "firstName,lastName"))
public class User {
@Id
private Long id;
private String firstName;
private String lastName;
private String email;
}
-- SQL
CREATE INDEX idx_user_name ON User(firstName, lastName);
-- Быстро для обоих запросов
SELECT * FROM User WHERE firstName = 'Bob' AND lastName = 'Smith'; -- Индекс используется
SELECT * FROM User WHERE firstName = 'Bob'; -- Индекс используется (левая часть)
-- НЕ использует индекс
SELECT * FROM User WHERE lastName = 'Smith'; -- Индекс НЕ подходит
4. Full-Text Index (полнотекстовый индекс)
@Entity
public class Article {
@Id
private Long id;
@Column(columnDefinition = "FULLTEXT INDEX")
private String content;
}
-- SQL
CREATE FULLTEXT INDEX idx_article_content ON Article(content);
-- Быстрый поиск по тексту
SELECT * FROM Article WHERE MATCH(content) AGAINST('Java tutorial' IN BOOLEAN MODE);
5. Partial Index (частичный индекс)
-- SQL (PostgreSQL)
CREATE INDEX idx_active_users ON User(email) WHERE is_active = true;
-- Полезно для условных запросов
SELECT * FROM User WHERE is_active = true AND email = 'bob@example.com'; -- Использует индекс
SELECT * FROM User WHERE is_active = false AND email = 'bob@example.com'; -- Индекс не подходит
Производительность
Временная сложность:
| Операция | Без индекса | С индексом |
|---|---|---|
| SELECT по условию | O(n) | O(log n) |
| INSERT | O(1) | O(log n) |
| UPDATE | O(n) | O(log n) |
| DELETE | O(n) | O(log n) |
Пример на реальных цифрах:
Табель с 1 000 000 записей
Без индекса: ~500 000 проверок (в среднем)
С индексом: ~20 проверок (log2(1000000) ≈ 20)
Когда создавать индексы
СОЗДАВАЙ индекс для:
// 1. Поля в WHERE
SELECT * FROM User WHERE email = ?; // Индекс на email
// 2. Поля в JOIN
SELECT * FROM User u JOIN Order o ON u.id = o.user_id; // Индекс на user_id
// 3. Поля в ORDER BY
SELECT * FROM User ORDER BY created_at DESC; // Индекс на created_at
// 4. Часто ищешь
SELECT * FROM User WHERE status = 'active'; // Если часто фильтруешь по статусу
// 5. Уникальные значения
@Column(unique = true) // Автоматически создаёт индекс
private String email;
НЕ создавай индекс для:
// 1. Маленькие таблицы (<1000 записей)
// Индекс может быть медленнее, чем table scan
// 2. Таблицы с частыми INSERT/UPDATE
// Индекс нужно перестраивать при каждом изменении
// 3. Низкая кардинальность (мало уникальных значений)
boolean is_active; // Только true/false — индекс неэффективен
DayOfWeek day; // Только 7 значений
// 4. Функции в WHERE
SELECT * FROM User WHERE UPPER(name) = 'BOB'; // Индекс на name не поможет
// 5. Начинается с wildcards
SELECT * FROM User WHERE email LIKE '%@gmail.com'; // Индекс не поможет
SELECT * FROM User WHERE email LIKE 'bob%'; // Индекс поможет
Практический пример в Spring Data JPA
@Entity
@Table(name = "orders", indexes = {
@Index(name = "idx_user_id", columnList = "user_id"),
@Index(name = "idx_created_date", columnList = "created_at"),
@Index(name = "idx_user_status", columnList = "user_id,status")
})
public class Order {
@Id
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Enumerated(EnumType.STRING)
private OrderStatus status;
private BigDecimal amount;
}
// Repository с индексированными запросами
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// Использует индекс на user_id
List<Order> findByUser(User user);
// Использует индекс на created_at
List<Order> findByCreatedAtBetween(LocalDateTime from, LocalDateTime to);
// Использует составной индекс на (user_id, status)
List<Order> findByUserAndStatus(User user, OrderStatus status);
}
Анализ запросов
-- PostgreSQL: EXPLAIN показывает план выполнения
EXPLAIN ANALYZE
SELECT * FROM User WHERE email = 'bob@example.com';
-- Результат без индекса:
-- Seq Scan on "User" (cost=0.00..35.50 rows=1 width=100)
-- Результат с индексом:
-- Index Scan using idx_user_email on "User" (cost=0.29..8.30 rows=1 width=100)
-- Много быстрее!
Лучшие практики
- Анализируй медленные запросы перед добавлением индексов
- Индексируй LEFT части составных индексов в первую очередь
- Избегай слишком много индексов — они замедляют INSERT/UPDATE
- Мониторь размер индексов — они занимают место на диске
- Пересматривай индексы при изменении паттернов доступа
- Используй EXPLAIN для проверки использования индексов
Индексирование — это баланс между скоростью чтения и скоростью записи. Правильное индексирование может улучшить производительность в 100+ раз!