Индексы в SQL: что это и как они ускоряют запросы

Указатель в конце книги, только для БД

Индексы в SQL: что это и как они ускоряют запросы

Когда в таблице всего тысяча строк, любой SQL-запрос выполняется мгновенно. Но когда проект вырастает, а строк становится десять миллионов, привычный SELECT * FROM orders WHERE customer_id = 123
вдруг начинает подвешивать базу на несколько секунд. Добавление оперативной памяти серверу здесь не поможет, поскольку решать архитектурные проблемы железом — плохая практика. Тогда приходится разбираться, как работают механизмы быстрого поиска данных под капотом СУБД.

ВАМ ПРИШЛО ПРИГЛАШЕНИЕ 💌
Приходите к нам в соцсети поделиться своим мнением и почитать, что пишут другие. А ещё там выходит дополнительный контент, которого нет на сайте — шпаргалки, опросы и разная дурка. В общем, вот тележка, вот ВК — велком!

Что такое индекс в базе данных

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

Допустим, у вас есть какая-то техническая книга. Если вам нужно найти главу про транзакции, вы не будете читать все 800 страниц подряд с первой до последней. Вы откроете алфавитный предметный указатель в конце, найдете слово «транзакции», увидите номер страницы и сразу откроете нужную. Индексы в БД — это и есть, по сути, такой предметный указатель.

Без индекса база данных вынуждена делать Full Table Scan, то есть полное сканирование: она берёт первую строку таблицы, сравнивает с вашим условием, потом вторую, третью — и так до самого конца. С индексом движок переходит к нужным строкам за логарифмическое время, отсекая ненужные данные.

Как индекс устроен внутри: B-tree

Существуют разные структуры индексов, но в большинстве современных баз данных по умолчанию используется B-tree, или B-дерево, — сбалансированная древовидная структура.

Не путайте его с обычным бинарным деревом. В бинарном дереве у каждого узла максимум два потомка, а в B-tree узлы могут хранить сразу много значений и ссылок. Благодаря этому дерево получается «широким и низким», а не «высоким и узким», и количество шагов при поиске остаётся небольшим даже на больших объёмах данных.

Внутри B-tree все значения отсортированы. Поиск работает как быстрый спуск от корня дерева через промежуточные узлы к «листьям», где уже лежат ссылки на реальные строки в таблице.

Такой подход дает важное преимущество: индекс ускоряет не только точные сравнения (=), но и диапазоны (<, >, BETWEEN), а также сортировку (ORDER BY), потому что данные уже структурированы в отсортированном виде.

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

Создание индекса: синтаксис и первый эффект

Индекс в SQL создается одной простой командой. Синтаксис в большинстве баз данных практически одинаковый.

Представим таблицу заказов orders, в которой уже есть миллионы строк. Чаще всего мы ищем заказы конкретного пользователя: например, по customer_id. Без индекса такой запрос заставляет базу просматривать таблицу целиком.

Исходный запрос выглядит так:

SELECT * FROM orders WHERE customer_id = 4567;

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

CREATE INDEX idx_orders_customer_id ON orders(customer_id);

Теперь, если мы заново выполним тот же SELECT, база обратится к новому индексу. Эффект от этого действия нельзя описать абстрактным «станет в сто раз быстрее»: ускорение всегда зависит от размера таблицы и уникальности данных. Реальную разницу мы увидим не с секундомером в руках, а через анализ плана запроса, который строит сама СУБД.

Если индекс в дальнейшем перестаёт быть полезным (например, изменился характер запросов или логика приложения), его можно удалить:

DROP INDEX idx_orders_customer_id;

Какие бывают индексы

Помимо стандартного B-tree, который используется для обычных колонок, в SQL есть несколько типов индексов под разные задачи. Они решают одну проблему — ускорение поиска, но делают это разными способами.

Важно помнить: PRIMARY KEY индексируется автоматически при создании таблицы, поэтому дополнительно создавать индекс на него вручную не нужно.

Посмотрим, какие бывают индексы и когда они применяются:

  • Уникальный (UNIQUE): не только ускоряет поиск, но и работает как ограничение целостности — не даст вставить в базу дубликат email или логина.
  • Составной (Composite): строится сразу по нескольким колонкам, например, по имени и фамилии.
  • Частичный (Partial): индексирует не всю таблицу, а только определённые строки. Например, WHERE status = ‘active’ — экономит место, если нас интересуют только активные пользователи.
  • Покрывающий (Covering): ситуация, когда все поля, нужные в SELECT, уже находятся внутри индекса. Тогда база может вообще не ходить в основную таблицу и брать данные напрямую из индекса — это самый быстрый сценарий чтения.

Отдельно существуют более специфичные индексы — GIN, GiST, Hash, а также кластерные индексы в MS SQL. Они используются для полнотекстового поиска, геоданных или физической организации хранения таблицы, но это уже более продвинутый уровень.

Составной индекс и порядок колонок

Составной индекс — один из самых частых источников ошибок у разработчиков, которые уже знакомы с индексами, но не до конца понимают их поведение.

Составной индекс работает не как «набор независимых колонок», а как строго упорядоченная структура. Здесь используется правило левого префикса: база данных использует индекс слева направо, не перескакивая через первую колонку.

Допустим, мы создали индекс:

CREATE INDEX idx_user_name ON users(last_name, first_name);

Он будет хорошо работать в двух случаях:

  • поиск по фамилии:
    WHERE last_name = ‘Williams’
  • поиск по фамилии и имени одновременно:
    WHERE last_name = ‘Williams’ AND first_name = ‘Ellie’

В обоих случаях база может использовать структуру индекса, потому что она начинается с last_name.

Но если попытаться искать только по имени: WHERE first_name = ‘Ellie’, индекс уже не поможет. Дело в том, что данные внутри него отсортированы сначала по last_name, и база не может «прыгнуть» сразу к first_name, пропуская первую колонку. Для неё это неупорядоченное пространство внутри дерева, поэтому приходится идти в полный перебор.

Поэтому порядок колонок в составном индексе — это ключевая часть его эффективности.

EXPLAIN: как проверить, что индекс работает

Понять, используется ли индекс в вашем запросе, можно с помощью команды EXPLAIN. В этом примере мы будем ориентироваться на PostgreSQL, потому что у него один из самых читаемых планов выполнения. В других СУБД инструмент работает аналогично, и меняется только формат вывода.

Чтобы не просто увидеть план, а еще и реальные цифры выполнения, используют EXPLAIN ANALYZE:

EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 4567;

После выполнения база покажет план запроса — по сути, маршрут, по которому она пошла, чтобы получить данные. Это и есть способ понять, использовался индекс или нет. В выводе стоит смотреть на два основных варианта:

  • Seq Scan (Sequential Scan): последовательное чтение таблицы целиком. Индекс не используется, и база просто проходит по всем строкам. На больших таблицах это самый дорогой вариант.
  • Index Scan / Index Only Scan / Bitmap Index Scan: признак того, что индекс был применён, и поиск идёт через него, а не через полный перебор.

Важно, что если вы добавили индекс, но видите Seq Scan, это не всегда ошибка. Оптимизатор базы не «слепо следует индексам», а выбирает самый дешёвый по стоимости вариант.

Например, если таблица маленькая (условно 100 строк), базе действительно быстрее прочитать её целиком, чем делать дополнительный переход через индекс. То же самое происходит, если запрос возвращает почти всю таблицу — в таких случаях полный скан оказывается выгоднее.

Большая скидка — 16% на все курсы Практикума

Если читаете эту статью, тогда вы точно разбираетесь в технологиях. Стать лучше и зарабатывать больше можно после курсов Практикума — по программированию, анализу данных и искусственному интеллекту.

До 17 сентября на все курсы действует скидка 16%, она применится автоматически при оплате. Потом цены станут выше, поэтому не откладывайте!

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

Цена индексов: почему не индексируют всё подряд

Распространённая ошибка новичков — проиндексировать каждую колонку в таблице «на всякий случай». Кажется, что так база всегда будет работать быстрее, но на практике всё наоборот.

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

Но главная стоимость — это скорость записи. Каждая операция INSERT, UPDATE и DELETE заставляет базу не только изменить строку, но и обновить все связанные индексы. По сути, каждый B-tree нужно поддерживать в актуальном состоянии. И чем больше индексов, тем больше работы выполняет СУБД при каждой записи.

Если на таблице висит 10–15 индексов, обычная вставка строки перестает быть дешёвой операцией — база тратит заметное время на синхронизацию всех структур.

Поэтому основное правило такое: индексировать нужно только те колонки, которые действительно участвуют в WHERE, JOIN и ORDER BY в частых запросах. Всё остальное — уже лишняя нагрузка без выгоды.

Типовые ошибки при работе с индексами

Соберём главные ошибки индексации, которые убивают производительность баз данных на продакшене:

  1. Функции над колонкой в WHERE. Запрос вида WHERE DATE(created_at) = ‘2026-01-01’ ломает использование индекса по created_at. База сначала вынуждена вычислить функцию для каждой строки, и только потом сравнивать значения. В таких случаях лучше использовать диапазоны дат без преобразований.
  2. Поиск через LIKE с % в начале. Поскольку поиск начинается с «любого символа», базе приходится делать полный перебор строк.
  3. Игнорирование правила левого префикса. Если у вас есть составной индекс, но вы используете только правую часть колонок, индекс становится бесполезным. База не может «перепрыгнуть» через первую колонку в структуре B-tree.
  4. Низкая селективность (уникальность) индекса. Индекс на колонку вроде is_active часто бесполезен, если 90–99% строк имеют одно и то же значение. В таких случаях оптимизатору проще сделать последовательный проход по таблице, чем использовать индекс.
  5. Забытые индексы. Со временем логика приложения меняется, старые запросы исчезают, а индексы под них остаются. Они мёртвым грузом лежат на диске и тормозят любые операции записи. Периодически их нужно находить и удалять.

Понимание того, как СУБД строит планы запросов, как хранит данные и почему отказывается использовать ваши индексы — это тот водораздел, который отделяет начинающего разработчика от крепкого инженера. Именно по этим нюансам тимлиды гоняют кандидатов на собеседованиях. Если вы хотите глубоко понимать архитектуру данных, писать сложные и быстрые запросы, а не просто копировать SELECT из туториалов, обратите внимание на курс «Аналитик данных» от Практикума, где тема оптимизации SQL разбирается на реальных промышленных объёмах данных.

Советуем дополнительно почитать

Что такое MLOps — что происходит с моделью после torch.export: версионирование, мониторинг и отслеживание дрейфа.

Локальные нейросети на ПК: 10 лучших инструментов для запуска AI без облака в 2026 году — готовые модели, которые поднимаются на своём железе, и требования к памяти под каждую.

OpenAI API в Python: как подключить ChatGPT к приложению — альтернативный путь для тех, кому нужна не своя модель, а чужая по запросу.

Кто такой Data Engineer и как им стать — кто готовит данные, которые потом попадают в Dataset и DataLoader.

Теория вероятности в машинном обучении: с формулами и примерами кода — математика под функциями потерь: почему ошибка считается именно так и что означает её значение.

Бонус для читателей

Если вам интересно погрузиться в мир ИТ и при этом немного сэкономить, держите наш промокод на курсы Практикума. Вы получите скидку при оплате, поможет со льготной ипотекой и даст безлимит на маркетплейсах. Ладно, окей, это просто скидка, без остального, но хорошая.

Вам может быть интересно
medium
[anycomment]
Exit mobile version