Что такое GIN и GiST индексы в PostgreSQL и когда их использовать?

JuniorPostgreSQL · Backend·Обновлено 10 сентября 2026
Коротко
GIN и GiST — это специализированные типы индексов PostgreSQL для сложных типов данных: массивов, JSONB, полнотекстового поиска, геометрии и диапазонов. GIN оптимален для данных, которые редко меняются, но требуют быстрого поиска по множеству значений (например, JSONB, full-text search), а GiST лучше подходит для операций пересечения, включения и близости (геометрия, диапазоны) и быстрее обновляется.

Зачем нужны GIN и GiST

Обычный B-tree индекс хорошо работает для скалярных значений и операций сравнения (=, <, >), но плохо подходит, когда одно значение колонки логически содержит несколько «элементов»: массив, JSON-документ, текст для полнотекстового поиска или геометрическую фигуру. Для таких случаев PostgreSQL предлагает два специализированных типа индексов — GIN и GiST.

GIN (Generalized Inverted Index)

GIN строит инвертированный индекс: для каждого возможного «элемента» (слова, ключа JSONB, элемента массива) хранится список строк таблицы, где этот элемент встречается. Это похоже на индекс в конце книги — от термина к списку страниц.

Особенности:

  • очень быстрый поиск по вхождению значения (@>, ?, &&, полнотекстовый поиск);
  • индекс получается компактным для чтения, но дорогим при обновлении, поэтому GIN — это выбор для данных, которые часто читают и редко пишут;
  • есть механизм fastupdate, который буферизует изменения, чтобы снизить накладные расходы на вставку.

Типичные применения:

  • полнотекстовый поиск по tsvector;
  • поиск по массивам (int[], text[]);
  • поиск по ключам и значениям в jsonb (@>, ?, ?|, ?&).

GiST (Generalized Search Tree)

GiST — это сбалансированное дерево, которое поддерживает произвольные предикаты через кастомные операторные классы. Вместо точного соответствия оно умеет отвечать на вопросы «пересекается ли», «содержит ли», «ближе ли», что делает его основой для индексов по геометрии, диапазонам и exclusion constraints.

Особенности:

  • поддерживает операции пересечения, включения, ближайшего соседа (<->, оператор KNN);
  • обновляется быстрее, чем GIN, поэтому лучше переносит частые изменения данных;
  • является «lossy»-индексом: может возвращать больше строк, чем нужно, и PostgreSQL перепроверяет их точным условием (recheck).

Типичные применения:

  • геометрические и геопространственные данные (PostGIS активно использует GiST);
  • диапазонные типы (tsrange, int4range, daterange);
  • exclusion constraints (например, запрет пересекающихся временных интервалов);
  • поиск ближайших точек (ORDER BY point <-> '(0,0)').

Как выбрать между GIN и GiST

Критерий GIN GiST
Скорость чтения выше ниже
Скорость записи/обновления ниже выше
Размер индекса обычно больше обычно меньше
Типовые задачи full-text search, JSONB, массивы геометрия, диапазоны, KNN, exclusion constraints

Правило по умолчанию: если данные читают намного чаще, чем пишут (JSONB-каталог, поиск по тексту) — GIN. Если нужны операции близости/пересечения или таблица часто обновляется — GiST.

Пример

-- GIN индекс для полнотекстового поиска
CREATE INDEX idx_articles_search ON articles USING GIN (to_tsvector('russian', body));

-- GIN индекс для JSONB
CREATE INDEX idx_profile_data ON users USING GIN (profile_data);

-- GiST индекс для диапазона дат бронирования
CREATE INDEX idx_bookings_range ON bookings USING GiST (during);

Что хочет услышать интервьюер

Кандидат объясняет, что B-tree не подходит для составных/сложных типов данных, и называет альтернативу — GIN и GiST

Понимание, что GIN — инвертированный индекс, оптимальный для чтения, но дорогой при записи

Понимание, что GiST — дерево поиска с поддержкой пересечений/близости, быстрее обновляется, но медленнее читается

Кандидат приводит конкретные примеры применения: full-text search и JSONB для GIN, геометрия/диапазоны для GiST

Умение соотнести выбор индекса с характером нагрузки (read-heavy vs write-heavy)

Пример: GIN индекс для JSONB и полнотекстового поиска

-- Поиск по ключам и значениям внутри jsonb
CREATE INDEX idx_users_profile ON users USING GIN (profile_data);

SELECT * FROM users WHERE profile_data @> '{"role": "admin"}';

-- Полнотекстовый поиск по статье
CREATE INDEX idx_articles_fts ON articles USING GIN (to_tsvector('russian', body));

SELECT * FROM articles
WHERE to_tsvector('russian', body) @@ to_tsquery('russian', 'postgresql & индекс');

Пример: GiST индекс для диапазонов и exclusion constraint

-- Диапазон бронирования переговорной
CREATE TABLE bookings (
  room_id int,
  during tsrange
);

CREATE INDEX idx_bookings_during ON bookings USING GiST (during);

-- Запрещаем пересекающиеся брони одной комнаты
ALTER TABLE bookings
  ADD CONSTRAINT no_overlap
  EXCLUDE USING GiST (room_id WITH =, during WITH &&);

Типичные ошибки

Путают GIN и GiST местами или считают их взаимозаменяемыми без разницы в поведении

Не знают, что GIN плохо переносит частые обновления, и ставят его на активно изменяемую таблицу без fastupdate

Не могут привести конкретный пример оператора (`@>`, `?`, `<->`), для которого нужен именно этот тип индекса

Забывают, что GiST — lossy-индекс и PostgreSQL делает recheck по точному условию

Считают, что эти индексы заменяют B-tree полностью, а не дополняют его для специфичных типов данных

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее