Индексы в SQL

Небольшая памятка по работе индексов в базах данных, их настройке и применении.

Предисловие

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

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

Область применения

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

Например, у нас есть две таблицы: Blog и Tag. Таблицы имеют связь "многие ко многим" (ManyToMany), так как одна запись в блоге может иметь разные теги, как и каждый тег может принадлежать нескольким записям. Таким образом, у нас появляется третья таблица, назовём ей BlogTag, в которой есть всего два столбца: blog_id и tag_id ссылающиеся на записи в соответствующих таблицах. И в этой связующей таблице оба столбца должны быть не только проиндексированы, но и иметь внешние ключи, ссылающиеся на записи в соответствующих таблицах.

Как показала реальная практика, даже в небольших таблицах на ~100 000 записей, при использовании нескольких операций присоединения (JOIN), скорость выборки данных может увеличиться в разы. Я лично в своём опыте добивался прироста скорости обработки сложных запросов почти в десять раз, с ~2.7 до ~0.4 секунд просто проставив нужные индексы и прописав внешние ключи.

Типы индексов

B-tree

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

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

Такой индекс подойдёт для запросов следующих типов:

WHERE age = num;
WHERE age > num;
WHERE age < num;
WHERE name LIKE 'John%';

В то же время, этот индекс не подойдёт для запросов, у которых подстановка неопределённых значений находится в начале искомой строки, например:

WHERE name LIKE '%Doe';

Так как индекс B-tree делает обход по дереву, в данном случае начальная точка входа не определена и индекс не будет учтён, вместо этого БД обойдёт всю таблицу в поисках подходящих значений.

Это ограничение можно обойти, если добавить reverse индекс. В таком случае, запрос будет выглядеть иначе:

WHERE reverse(name) LIKE reverse('%Doe');

Для чего может использоваться reverse? Например, для поиска адресов email конкретных серверов.

WHERE reverse(email) LIKE reverse('%@gmail.com');

HASH

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

WHERE name = 'John Doe';

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

WHERE name LIKE 'John%';
WHERE name LIKE '%Doe';
WHERE age > num;
WHERE age < num;
WHERE name IS NULL;

Кроме того, из-за возможности хранения одинаковых значений в БД, для совпадающих хэшей применяются методы разрешения коллизий.