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

Нужно ли дополнительно создавать индекс при создании первичного ключа?

2.0 Middle🔥 151 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью#ORM и Hibernate

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

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

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

Индекс при создании первичного ключа

Ответ: Нет, не нужно. Первичный ключ автоматически создаёт индекс в большинстве баз данных.

Как работает PRIMARY KEY

PostgreSQL

-- Создаём таблицу с PRIMARY KEY
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100)
);

-- PostgreSQL автоматически создаёт индекс!
-- Эквивалент:
CREATE UNIQUE INDEX users_pkey ON users(id);

Проверка:

-- Смотрим индексы
\d users;

-- Результат:
--  Table "public.users"
--  Column |          Type          | Collation | Nullable | Default
--  -------+------------------------+-----------+----------+---------
--  id     | bigint                 |           | not null |
--  name   | character varying(100) |           |          |
-- Indexes:
--     "users_pkey" PRIMARY KEY, btree (id)

MySQL

CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100)
);

-- MySQL тоже создаёт индекс автоматически
-- Проверка:
SHOW INDEX FROM users;

-- Результаты:
-- Table | Non_unique | Key_name | Seq_in_index | Column_name | ...
-- users |          0 | PRIMARY  |            1 | id          | ...

Почему PRIMARY KEY включает индекс

PRIMARY KEY требует:
1. UNIQUE - значения не должны повторяться
2. NOT NULL - значение не может быть null
3. INDEXED - для быстрого поиска по id

Индекс нужен для:

  • Быстрого поиска по PK: SELECT * FROM users WHERE id = 123 → O(log n)
  • Проверки уникальности при INSERT
  • Быстрого JOIN'а

Примеры в разных БД

PostgreSQL

-- ❌ ЛИШНЕЕ - индекс создастся автоматически
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100)
);
CREATE INDEX users_id_idx ON users(id);  -- Ненужный дубликат!

-- ✅ ПРАВИЛЬНО
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100)
);
-- Индекс уже есть автоматически

MySQL

-- ПРАВИЛЬНО - PRIMARY KEY создаёт индекс
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100)
);

-- Проверка
SHOW INDEX FROM users;
-- Видим индекс 'PRIMARY' на колонке id

Когда нужны дополнительные индексы

Дополнительные индексы нужны для других колонок:

CREATE TABLE users (
    id BIGINT PRIMARY KEY,        -- Индекс создан автоматически
    email VARCHAR(100) UNIQUE,    -- Индекс создан автоматически
    name VARCHAR(100),
    age INT,
    created_at TIMESTAMP
);

-- ДОБАВЛЯЕМ индексы на часто используемые колонки
CREATE INDEX idx_users_name ON users(name);           -- Часто ищем по имени
CREATE INDEX idx_users_age ON users(age);             -- Часто фильтруем по возрасту
CREATE INDEX idx_users_created_at ON users(created_at); -- Сортируем по дате

PRIMARY KEY vs UNIQUE INDEX

-- PRIMARY KEY автоматически создаёт UNIQUE INDEX
CREATE TABLE users (
    id BIGINT PRIMARY KEY  -- UNIQUE + NOT NULL + индекс
);

-- Это эквивалентно:
CREATE TABLE users (
    id BIGINT NOT NULL UNIQUE  -- UNIQUE создаёт индекс
);

-- Визуально:
-- PRIMARY KEY = UNIQUE INDEX + NOT NULL constraint

В Java/JPA

// В Hibernate/JPA автоматически создаются индексы
@Entity
@Table(name = "users")
public class User {
    
    @Id  // PRIMARY KEY - индекс создан в БД автоматически
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true)  // Создаст UNIQUE INDEX автоматически
    private String email;
    
    @Column
    private String name;
}

// При создании схемы Hibernate:
// CREATE TABLE users (
//     id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
//     email VARCHAR(255) UNIQUE,
//     name VARCHAR(255)
// );
// 
// Индексы создаст автоматически!

Миграции (Goose SQL)

-- ✅ ПРАВИЛЬНО - PRIMARY KEY создаст индекс
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    email VARCHAR(100) NOT NULL UNIQUE,
    name VARCHAR(100) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE
);

-- Если хочешь добавить индекс на другую колонку:
CREATE INDEX idx_users_email ON users(email);  -- Хотя UNIQUE уже имеет индекс
CREATE INDEX idx_users_name ON users(name);
CREATE INDEX idx_users_created_at ON users(created_at);

Миграция: до и после

-- Миграция 1: создаём таблицу
-- file: 0001_create_users.sql
CREATE TABLE users (
    id BIGINT PRIMARY KEY,  -- Индекс создан автоматически!
    email VARCHAR(100) UNIQUE,  -- Индекс создан автоматически!
    name VARCHAR(100),
    created_at TIMESTAMP
);

-- Миграция 2: добавляем индексы на часто используемые колонки
-- file: 0002_add_indexes.sql
CREATE INDEX idx_users_name ON users(name);
CREATE INDEX idx_users_created_at ON users(created_at);

-- Готово! Теперь все индексы на месте

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

С PRIMARY KEY (индекс есть):

SELECT * FROM users WHERE id = 123;  -- ⚡ Быстро (индекс)
-- Execution time: 0.001ms

Без индекса на другой колонке:

SELECT * FROM users WHERE name = 'John';  -- 🐌 Медленно (полная таблица)
-- Execution time: 45ms (на таблице из 1 млн строк)

С индексом на name:

CREATE INDEX idx_users_name ON users(name);
SELECT * FROM users WHERE name = 'John';  -- ⚡ Быстро
-- Execution time: 0.5ms

Типичная схема таблицы

CREATE TABLE orders (
    -- Primary Key - АВТОМАТИЧЕСКИЙ индекс
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    
    -- Foreign Key - рекомендуется индекс
    user_id BIGINT NOT NULL,
    
    -- Уникальное поле - АВТОМАТИЧЕСКИЙ индекс
    order_number VARCHAR(20) UNIQUE NOT NULL,
    
    -- Обычные поля - БЕЗ индексов (добавляем по необходимости)
    total_amount DECIMAL(10, 2),
    status VARCHAR(20),
    created_at TIMESTAMP WITH TIME ZONE,
    
    FOREIGN KEY(user_id) REFERENCES users(id)
);

-- ДОПОЛНИТЕЛЬНЫЕ индексы на часто используемые поля
CREATE INDEX idx_orders_user_id ON orders(user_id);      -- Для JOIN'ов
CREATE INDEX idx_orders_status ON orders(status);        -- Для фильтрации
CREATE INDEX idx_orders_created_at ON orders(created_at); -- Для сортировки

Контрольный список

Индексы создаются АВТОМАТИЧЕСКИ:

  • ✅ PRIMARY KEY
  • ✅ UNIQUE constraint
  • ✅ FOREIGN KEY (в большинстве БД)

Индексы нужно создавать ВРУЧНУЮ:

  • ❌ Колонки в WHERE (но не PK/UNIQUE)
  • ❌ Колонки в ORDER BY
  • ❌ Колонки в JOIN
  • ❌ Колонки в GROUP BY

Вывод

Не нужно дополнительно создавать индекс при PRIMARY KEY. Он создаётся автоматически.

Нужно создавать индексы на:

  • Колонки в WHERE условиях (если не PK)
  • Kolonки в JOIN'ах
  • Kolonки в ORDER BY

Правило большого пальца:

  • Если ты часто ищешь по колонке → создай индекс
  • Если ты редко ищешь → не создавай
  • Следи за production метриками (slow queries log)
Нужно ли дополнительно создавать индекс при создании первичного ключа? | PrepBro