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

Всегда ли индексируется первичный ключ?

1.8 Middle🔥 201 комментариев
#Основы Java

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

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

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

# Индексируется ли первичный ключ

Короткий ответ

ДА, первичный ключ (Primary Key) ВСЕГДА автоматически индексируется в современных СУБД. Это одна из основных особенностей первичного ключа.

Почему первичный ключ индексируется

1. Уникальность

Первичный ключ должен быть уникальным. СУБД создаёт индекс для быстрой проверки уникальности при вставке новых данных.

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

-- При вставке новой записи БД проверяет:
-- "Есть ли уже запись с id = 5?"
-- Индекс позволяет это сделать быстро (O(log n) вместо O(n))
INSERT INTO users (id, name, email) VALUES (5, 'Ivan', 'ivan@mail.com');

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

Поиск по первичному ключу должен быть максимально быстрым.

-- Поиск по ID - это одна из самых частых операций
SELECT * FROM users WHERE id = 5; -- Быстро благодаря индексу

3. Связь с внешними ключами

Другие таблицы часто ссылаются на первичный ключ через внешние ключи (FK).

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    user_id INT,
    amount DECIMAL(10, 2),
    FOREIGN KEY (user_id) REFERENCES users(id) -- Индекс нужен для быстрой проверки
);

Как выглядит индекс первичного ключа

-- В PostgreSQL можно увидеть индекс
CREATE TABLE products (
    product_id INT PRIMARY KEY,
    name VARCHAR(100)
);

-- PostgreSQL автоматически создаёт индекс
-- Можно увидеть его:
\d products
-- Результат покажет индекс "products_pkey"

Типы индексов для первичного ключа

В зависимости от СУБД используются разные типы индексов:

1. B-Tree индекс (наиболее распространённый)

-- Большинство СУБД используют B-Tree для PRIMARY KEY
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,  -- Автоматически создаётся B-Tree индекс
    name VARCHAR(100),
    salary DECIMAL(10, 2)
);

-- B-Tree индекс позволяет:
SELECT * FROM employees WHERE emp_id = 10;           -- O(log n)
SELECT * FROM employees WHERE emp_id BETWEEN 5 AND 15; -- O(log n + k)
SELECT * FROM employees ORDER BY emp_id;             -- Быстро

2. Хеш индекс (редко)

Некоторые СУБД (например, MySQL с определёнными движками) используют хеш индексы.

-- MySQL с HASH индексом
CREATE TABLE users (
    user_id INT PRIMARY KEY USING HASH,
    username VARCHAR(100)
);

-- Хеш индекс быстрый для точного поиска
SELECT * FROM users WHERE user_id = 5; -- Очень быстро

-- Но не поддерживает диапазоны
SELECT * FROM users WHERE user_id BETWEEN 1 AND 10; -- Медленно

Составной первичный ключ

Если первичный ключ состоит из нескольких полей, индексируются все они.

CREATE TABLE course_enrollments (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    enrollment_date DATE,
    PRIMARY KEY (student_id, course_id)
);

-- Создаётся индекс на пару (student_id, course_id)
-- Оба поля индексируются вместе

-- Быстрый поиск по полному ключу:
SELECT * FROM course_enrollments WHERE student_id = 1 AND course_id = 5;

-- Быстрый поиск по первой части ключа:
SELECT * FROM course_enrollments WHERE student_id = 1;

-- Медленный поиск только по второй части (индекс не используется):
SELECT * FROM course_enrollments WHERE course_id = 5; -- Может быть медленно

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

import javax.persistence.*;

// JPA/Hibernate автоматически создаёт индекс для @Id
@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;  // Первичный ключ - ВСЕГДА индексируется
    
    @Column(name = "username")
    private String username;
    
    @Column(name = "email")
    private String email;
    
    // getters/setters
}

// JPA Repository использует индекс первичного ключа
public interface UserRepository extends JpaRepository<User, Long> {
    // findById() использует индекс - O(log n)
}

// Использование
UserRepository repo = ...
User user = repo.findById(5L).orElse(null); // Быстро - индекс используется

Может ли первичный ключ быть без индекса?

Теоретически НЕТ в стандартных СУБД. Создание первичного ключа всегда подразумевает создание индекса.

Однако в некоторых редких случаях:

-- SQLite: может работать без явного индекса (использует ROWID)
-- Но это специфика SQLite

CREATE TABLE test (
    id INTEGER PRIMARY KEY, -- В SQLite это просто alias для ROWID
    value TEXT
);

Размер первичного ключа и производительность

// ПЛОХО - первичный ключ слишком большой
@Entity
public class User {
    @Id
    private String fullDescriptionOfUser; // Очень длинный первичный ключ
    // Индекс будет больше, медленнее
}

// ХОРОШО - компактный первичный ключ
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // Компактный INT или LONG
    
    @Column(unique = true)
    private String email; // Если нужна уникальность - отдельный индекс
}

Проверка индексов в разных СУБД

PostgreSQL

-- Посмотреть индексы таблицы
\d users

-- Или запрос
SELECT indexname FROM pg_indexes WHERE tablename = 'users';

MySQL

-- Посмотреть индексы
SHOW INDEXES FROM users;

-- Или
SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'users';

SQL Server

-- Посмотреть индексы
SP_HELPINDEX 'users';

-- Или
SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID('users');

Может ли быть несколько первичных ключей?

НЕТ, в таблице может быть только один первичный ключ.

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

CREATE TABLE users (
    id INT PRIMARY KEY,              -- Один первичный ключ
    email VARCHAR(100) UNIQUE,       -- Уникальный индекс (но не первичный)
    username VARCHAR(100) UNIQUE,    -- Ещё один уникальный индекс
    phone VARCHAR(20) UNIQUE         -- Ещё один
);

-- id - первичный ключ (индексируется)
-- email, username, phone - альтернативные ключи (тоже индексируются)

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

-- С индексом первичного ключа
SELECT * FROM users WHERE id = 5;              -- O(log n) - быстро
INSERT INTO users VALUES (5, 'Ivan', '...');   -- O(log n) - быстро
UPDATE users SET name = 'Alex' WHERE id = 5;   -- O(log n) - быстро
DELETE FROM users WHERE id = 5;                -- O(log n) - быстро

-- Без индекса (если бы он был - гипотетически)
SELECT * FROM users WHERE id = 5;              -- O(n) - медленно
INSERT INTO users VALUES (5, 'Ivan', '...');   -- O(n) - медленно

Резюме

  1. Первичный ключ ВСЕГДА индексируется в стандартных СУБД
  2. Индекс создаётся автоматически при определении PRIMARY KEY
  3. Тип индекса обычно B-Tree (сбалансированное дерево)
  4. Индекс гарантирует уникальность и обеспечивает быструю поиск
  5. Индекс составного первичного ключа включает все поля
  6. Это одна из главных особенностей первичного ключа

Отсутствие индекса на первичном ключе — это серьёзная проблема производительности, которую правильно спроектированная СУБД не допустит.