JavaRush /Курсы /SQL SELF /Типичные проблемы при работе с индексами

Типичные проблемы при работе с индексами

SQL SELF
38 уровень , 4 лекция
Открыта

Даже самая современная машина заглохнет, если налить в неё вместо бензина лимонад. Так и с индексами в PostgreSQL. Они представляют собой очень мощный инструмент, но использовать его нужно с умом. Давайте посмотрим на несколько типичных проблем, связанными с индексами.

Проблема 1: избыточное индексирование

Для начала повторим тему пред-предыдущей лекции. Когда у вас слишком много индексов на одну таблицу, PostgreSQL вынужден работать с каждым из них для поддержания их актуальности. Это напрямую влияет на операции записи, обновления и удаления. Ведь каждый индекс нужно не только обновить, но и синхронизировать!

Допустим, у нас есть таблица students:

CREATE TABLE students (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(255) UNIQUE,
    age INTEGER,
    grade INTEGER
);

И вы решили создать индексы на каждый столбец «на всякий случай»:

CREATE INDEX idx_students_name ON students(name);
CREATE INDEX idx_students_age ON students(age);
CREATE INDEX idx_students_grade ON students(grade);

Теперь представьте, что вы вставляете 10 тысяч новых записей. PostgreSQL должен не только записать данные в таблицу, но и обновить все три индекса. Если данных много, то скорость записи падает, а производительность системы страдает.

Как избежать этой проблемы? Перед созданием индекса задайте себе два вопроса:

  1. Как часто этот столбец участвует в фильтрации (WHERE), сортировке (ORDER BY) или группировке (GROUP BY)?
  2. Станет ли запрос использовать этот индекс или всё равно выполнит полное сканирование таблицы?

Если ответ на оба вопроса — «редко» или «никогда», индекс вам не нужен.

Проблема 2: неправильный выбор столбцов для индекса

Создание индекса на маловариативных данных — это как пытаться налить чай в чашку с уже залепленной крышкой: результат будет практически бесполезен. Если в столбце всего 2-3 уникальных значения, PostgreSQL, скорее всего, сделает полное сканирование таблицы вместо использования индекса.

Пусть у нас есть таблица courses:

CREATE TABLE courses (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255),
    level VARCHAR(10) -- Может быть только 'Beginner', 'Intermediate' или 'Advanced'
);

И вы решили создать индекс на столбце level:

CREATE INDEX idx_courses_level ON courses(level);

Однако запрос:

SELECT * FROM courses WHERE level = 'Beginner';

может не использовать индекс, так как PostgreSQL просто подсчитает, что проще просканировать всю таблицу, чем заглядывать в индекс. Это особенно актуально для маленьких таблиц и маловариативных данных.

Поэтому индексы имеют смысл на столбцах с высокой кардинальностью (то есть большим числом уникальных значений). Для маловариативных данных лучше использовать другие механизмы оптимизации, например, партиционирование таблиц.

Проблема 3: устаревшие индексы

Иногда индексы создают, но потом забывают удалить, даже если они уже не используются. Это как файлы на рабочем столе: сначала их совсем немного — два, три, пять. А потом вдруг ловишь себя на том, что тратишь драгоценное время, блуждая взглядом от значка к значку в надежде найти нужный... Знакомо?

Допустим, мы создали индекс для старого функционала, а затем изменили логику запросов и добавили новый индекс. Старый индекс больше никому не нужен, но продолжает существовать, занимать место и замедлять операции записи.

Чтобы избежать, регулярно проверяйте и анализируйте индексы. PostgreSQL предоставляет удобную метрику:

SELECT
    relname AS table_name,
    indexrelname AS index_name,
    idx_scan AS total_scans
FROM
    pg_stat_user_indexes
WHERE
    idx_scan = 0;

Здесь idx_scan показывает, сколько запросов использовало тот или иной индекс. Если значение равно 0, значит индекс не используется и его можно удалить:

DROP INDEX idx_courses_level;

Проблема 4: индексы на столбцах с частыми обновлениями

Если на столбце, который вы часто обновляете, есть индекс, PostgreSQL вынужден перестраивать этот индекс при каждом изменении. Это может существенно замедлить производительность.

Представьте таблицу с данными о заказах:

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    status VARCHAR(20), -- Может изменяться несколько раз (например, "новый", "в процессе", "завершён")
    total NUMERIC(10, 2)
);

Вы создаёте индекс на столбце status, чтобы ускорить фильтрацию по статусам:

CREATE INDEX idx_orders_status ON orders(status);

Но если статус обновляется десятки раз для каждой записи, то индекс начинает портить производительность.

Чтобы избежать подобной ситуации, не создавайте индексы на столбцах с частыми изменениями. Если же необходимость индекса сохраняется, рассмотрите возможность использования частичных индексов:

CREATE INDEX idx_orders_status_partial
ON orders(status) 
WHERE status = 'в процессе';

Таким образом, индекс будет обновляться только для записей с указанным значением.

Проблема 5: Ограничения UNIQUE на ненужных столбцах

Уникальные индексы (UNIQUE) автоматически создаются для поддержания уникальности данных. Но если уникальность не является строго необходимой, то эти индексы создают ненужную нагрузку.

Допустим, мы создали таблицу с логами:

CREATE TABLE logs (
    id SERIAL PRIMARY KEY,
    message TEXT,
    created_at TIMESTAMP UNIQUE
);

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

Чтобы было всё хорошо, оставляйте ограничения UNIQUE только там, где это действительно нужно. В нашем примере, если уникальность по created_at необязательна, замените индекс на обычный:

CREATE INDEX idx_logs_created_at ON logs(created_at);

Проблема 6: неправильное использование комбинированных индексов

Комбинированные индексы (multi-column indexes) полезны, если запросы фильтруются или сортируются сразу по нескольким столбцам. Но такие индексы нужно создавать правильно, иначе они останутся неиспользованными.

Допустим, у нас есть индекс:

CREATE INDEX idx_students_name_grade ON students(name, grade);

Этот индекс используется, если запрос фильтрует или сортирует по обоим столбцам:

SELECT * FROM students WHERE name = 'Alice' AND grade = 90;

Но запрос:

SELECT * FROM students WHERE grade = 90;

не использует этот индекс, так как поле name идёт первым.

Во избежание проблемы создавайте комбинированные индексы только в том порядке, в котором они чаще всего используются в запросах. Если требуется фильтрация только по одному из столбцов, создайте отдельные индексы.

Полезные советы

Мониторьте использование индексов. У PostgreSQL есть системный представления pg_stat_user_indexes, где можно увидеть, какие индексы используются, а какие нет.

Оптимизируйте запросы вместе с индексами. Плохие запросы останутся плохими даже с индексами.

Не забывайте про удаление. Устаревшие индексы только занимают место и замедляют операции записи.

Вот и всё, друзья! Индексы — это мощный инструмент, но всегда помните, что с великой силой приходит великая ответственность. Используйте индексы осознанно, и ваша база данных будет работать словно ракета на базе SpaceX!

2
Задача
SQL SELF, 38 уровень, 4 лекция
Недоступна
Определение неиспользуемых индексов
Определение неиспользуемых индексов
1
Опрос
Проблемы избыточного индексирования, 38 уровень, 4 лекция
Недоступен
Проблемы избыточного индексирования
Проблемы избыточного индексирования
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ