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

Что такое индексирование?

1.2 Junior🔥 241 комментариев
#Базы данных и SQL

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

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

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

Что такое индексирование?

Индексирование — это один из ключевых способов оптимизации производительности баз данных. Давайте разберёмся, что это такое, как это работает и когда это нужно использовать.

Определение

Индекс (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)
INSERTO(1)O(log n)
UPDATEO(n)O(log n)
DELETEO(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)
-- Много быстрее!

Лучшие практики

  1. Анализируй медленные запросы перед добавлением индексов
  2. Индексируй LEFT части составных индексов в первую очередь
  3. Избегай слишком много индексов — они замедляют INSERT/UPDATE
  4. Мониторь размер индексов — они занимают место на диске
  5. Пересматривай индексы при изменении паттернов доступа
  6. Используй EXPLAIN для проверки использования индексов

Индексирование — это баланс между скоростью чтения и скоростью записи. Правильное индексирование может улучшить производительность в 100+ раз!

Что такое индексирование? | PrepBro