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

Какие поля нужно индексировать

1.7 Middle🔥 191 комментариев
#Базы данных и SQL

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

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

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

Ответ

Индексирование — критический фактор производительности БД. Выбор полей для индексирования требует анализа паттернов запросов и баланса между скоростью чтения и затратами на запись.

Приоритет индексирования

1. Обязательно индексировать

Primary Key (PK)

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;  // Индекс автоматически
}

Всегда индексируется по умолчанию.

Foreign Keys (FK)

@Entity
public class Order {
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;  // Нужен индекс для JOINов
}

Поля в WHERE условиях

-- Частый запрос:
SELECT * FROM users WHERE email = ?;
SELECT * FROM orders WHERE status = ?;
SELECT * FROM posts WHERE created_at > ?;

Индексируйте эти поля:

@Entity
public class User {
    @Column(unique = true)
    @Index(name = "idx_email")
    private String email;
}

@Entity
public class Order {
    @Column
    @Index(name = "idx_status")
    private OrderStatus status;
}

@Entity
public class Post {
    @Column
    @Index(name = "idx_created_at")
    private LocalDateTime createdAt;
}

2. Высокий приоритет

Поля в ORDER BY

SELECT * FROM posts ORDER BY created_at DESC LIMIT 10;

Поля в GROUP BY

SELECT category, COUNT(*) FROM products GROUP BY category;

Поля в JOIN условиях

SELECT * FROM orders o 
JOIN payments p ON o.id = p.order_id
WHERE o.user_id = ?;

Пример индекса:

@Entity
@Table(indexes = {
    @Index(name = "idx_category", columnList = "category"),
    @Index(name = "idx_created_at", columnList = "created_at DESC")
})
public class Product {
    private String category;
    private LocalDateTime createdAt;
}

3. Составные индексы (Composite Indexes)

Для часто используемых комбинаций:

@Entity
@Table(indexes = {
    @Index(
        name = "idx_user_status_date",
        columnList = "user_id, status, created_at DESC"
    )
})
public class Transaction {
    private UUID userId;
    private TransactionStatus status;
    private LocalDateTime createdAt;
}

Практический пример:

// Запрос использует индекс полностью
List<Transaction> txns = repository.findByUserIdAndStatusOrderByCreatedAtDesc(
    userId, 
    TransactionStatus.COMPLETED
);

// SQL: WHERE user_id = ? AND status = ? ORDER BY created_at DESC
// Индекс (user_id, status, created_at DESC) отлично подходит

4. Низкий приоритет (редко индексировать)

Поля с низкой селективностью

@Entity
public class User {
    @Column
    // Индекс на is_active НЕ НУЖЕН — только 2 значения
    private boolean isActive;
    
    @Column
    // Индекс на gender может быть бесполезен — только 3 значения
    private String gender;
}

BLOB/TEXT поля

@Entity
public class Article {
    @Lob
    private String content;  // Не индексируйте, слишком много данных
    
    @Index(name = "idx_title")  // Заголовок — ОК
    private String title;
}

Пример правильной схемы

@Entity
@Table(
    name = "users",
    indexes = {
        @Index(name = "idx_email", columnList = "email", unique = true),
        @Index(name = "idx_username", columnList = "username", unique = true),
        @Index(name = "idx_created_at", columnList = "created_at DESC"),
        @Index(name = "idx_status_created", columnList = "status, created_at DESC")
    }
)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
    
    @Column(unique = true)
    private String email;
    
    @Column(unique = true)
    private String username;
    
    @Column
    private UserStatus status;
    
    @Column
    private LocalDateTime createdAt;
    
    @Lob
    private String bio;  // Без индекса
}

Проверка использования индексов

// PostgreSQL
EXPLAIN ANALYZE
SELECT * FROM users WHERE email = test@example.com;

// MySQL
EXPLAIN
SELECT * FROM users WHERE email = test@example.com;

Антипаттерны

❌ Индексирование всех полей — увеличивает размер БД ❌ Полнотекстовый поиск в обычном индексе ❌ Индексы на вычисляемых столбцах без генерируемых индексов

Правило 80/20

Обычно 20% индексов решают 80% проблем производительности. Сначала индексируйте:

  1. PRIMARY KEY и FOREIGN KEYS
  2. Поля в WHERE условиях
  3. Поля в JOIN условиях
  4. Составные индексы для частых комбинаций

Затем профилируйте и добавляйте дополнительные индексы по необходимости.