Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN

Рейтинг: 66.7% · 13 голосов
Подробный курс по PostgreSQL: SQL по демобазе, типы данных и JSONB, индексы, транзакции и MVCC, VACUUM, EXPLAIN и планировщик, роли и привилегии, репликация, бэкап, настройка под нагрузку и под 1С. Актуально на 2026.
Ответить
Аватара пользователя
Egor_DBA
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN

Сообщение Egor_DBA »

Оглавление курса (47)
  1. Что такое PostgreSQL: история, философия, где применяется
  2. Урок 1. Что нового в PostgreSQL 15 и далее (15 -> 16 -> 17)
  3. Установка и первый запуск: Linux, Windows, Docker
  4. psql детально: метакоманды и работа в консоли
  5. Демобаза Авиаперевозки Postgres Pro: структура и развёртывание
  6. SELECT: проекция, фильтрация, сортировка
  7. Транзакции и ACID
  8. Уровни изоляции транзакций: Read Committed, Repeatable Read, Serializable
  9. Соединения таблиц (JOIN)
  10. Агрегация: GROUP BY, HAVING, GROUPING SETS
  11. Подзапросы и CTE (WITH), рекурсия
  12. Оконные функции
  13. Изменение данных: INSERT/UPDATE/DELETE, UPSERT, MERGE
  14. Числовые и булевы типы, NULL и трёхзначная логика
  15. Строки, текст, дата и время, таймзоны
  16. Массивы, диапазоны, enum, составные типы
  17. JSON и JSONB: операторы и индексация
  18. DDL: CREATE TABLE, схемы, ALTER
  19. Ограничения целостности
  20. Представления и материализованные представления
  21. Последовательности, IDENTITY, генерируемые столбцы
  22. Индексы: B-tree и когда индекс не используется
  23. Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN (вы здесь)
  24. Продвинутые индексы: частичные, по выражению, покрывающие
  25. Обслуживание индексов и таблиц: bloat, REINDEX, CONCURRENTLY
  26. Полнотекстовый поиск
  27. Роли и пользователи
  28. Привилегии: GRANT/REVOKE
  29. Row-Level Security и безопасность данных
  30. Подключение приложения: pg_hba.conf, SSL, пулы соединений
  31. Серверное программирование: функции и процедуры
  32. Триггеры и события
  33. Архитектура PostgreSQL: процессы и память
  34. MVCC: версии строк и видимость
  35. VACUUM, autovacuum, freeze и wraparound
  36. WAL, контрольные точки и долговечность
  37. Блокировки и взаимоблокировки
  38. Планировщик запросов
  39. EXPLAIN и EXPLAIN ANALYZE: чтение планов
  40. Конфигурация сервера: ключевые параметры
  41. Настройка под нагрузку и под 1С
  42. Параллельные запросы и партиционирование
  43. Резервное копирование и восстановление
  44. Репликация: потоковая и hot standby
  45. Логическая репликация и кластерные решения
  46. Внешние данные, расширения и сертификация
  47. Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE
Урок 22. Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN

В прошлом уроке мы разобрали B-tree - универсальный индекс postgresql, который ускоряет сравнения и сортировку. Но B-tree умеет не всё. Если вы ищете точку на карте, проверяете пересечение диапазонов дат, ищете элемент внутри массива или строите полнотекстовый поиск, B-tree вам не поможет: его модель упорядоченного дерева тут просто не подходит. Этот урок про остальные методы доступа: Hash, GiST, SP-GiST, GIN и BRIN. Разберём, какую задачу решает каждый и как выбрать индекс под конкретный запрос, а не по привычке.

Изображение

Как это работает

Главная идея: индекс - это не одна структура на все случаи, а семейство методов доступа. Каждый метод понимает свой набор операторов. B-tree понимает = < > и сортировку, потому что хранит ключи в порядке. Другие методы хранят данные иначе и поэтому отвечают на другие вопросы.

Hash хранит не сами значения, а их хеши, и умеет ровно один оператор - равенство. Диапазоны и сортировку он не поддерживает в принципе. До PostgreSQL 10 hash-индексы не писались в WAL и не переживали сбой, поэтому их избегали. Сейчас они полноценные, но выигрывают у B-tree редко - разве что на очень длинных ключах, где хеш короче самого значения.

GiST это обобщённое сбалансированное дерево, в узлах которого лежат не точные ключи, а описания областей: ограничивающий прямоугольник для геометрии, объединённый диапазон для range-типов. Спускаясь по дереву, мы отсекаем ветки, которые точно не пересекаются с запросом. Поэтому GiST отвечает на вопросы вида пересекается, содержит, ближайший сосед (KNN, оператор расстояния), и под него заточены геометрия, диапазоны, исключающие ограничения и полнотекстовый поиск.

SP-GiST это пространственное разбиение: quad-дерево, k-d дерево, radix-дерево (trie). Оно не балансируется, а делит пространство на непересекающиеся куски. Это выигрывает на неравномерно распределённых данных - точки, IP-адреса, префиксы строк, телефонные коды, где B-tree или GiST давали бы перекос.

GIN это инвертированный индекс. Если в одной строке много значений (элементы массива, ключи jsonb, слова документа), GIN строит отображение элемент -> список строк, где он встречается. Отсюда его сила: операторы содержит (@>), есть ли ключ (?), полнотекст @@. Цена - запись медленнее, потому что одна строка трогает много записей индекса (отсюда буфер fastupdate).

BRIN устроен принципиально иначе: он не указывает на строки, а хранит сводку (например min/max) по блокам таблицы. Он крошечный, но полезен только когда физический порядок строк совпадает с порядком значений - типичный случай это лог или таблица, растущая по времени или по возрастающему id. На перемешанных данных BRIN бесполезен. В PostgreSQL 16 ускорили параллельную сборку BRIN, но сама логика прежняя.

SQL и примеры

В демобазе Авиаперевозки у аэропортов есть координаты типа point. GiST по ним даёт поиск ближайших аэропортов:

Код: Выделить всё

CREATE INDEX idx_airports_coord ON airports USING gist (coordinates);

-- 5 аэропортов, ближайших к точке (оператор <-> это KNN)
SELECT airport_name, city
FROM airports
ORDER BY coordinates <-> point '(37.62, 55.75)'
LIMIT 5;
GIN пригодится для jsonb и массивов. Допустим, мы храним маршрут билета как массив кодов аэропортов:

Код: Выделить всё

ALTER TABLE tickets ADD COLUMN route text[];

CREATE INDEX idx_tickets_route ON tickets USING gin (route);

-- быстро находим билеты, чей маршрут содержит и SVO, и LED
SELECT ticket_no FROM tickets
WHERE route @> ARRAY['SVO','LED'];
BRIN хорош на больших, упорядоченных по вставке таблицах. Перелёты по сути растут по времени вылета:

Код: Выделить всё

CREATE INDEX idx_fl_brin ON flights USING brin (scheduled_departure)
  WITH (pages_per_range = 64);

-- запрос по узкому окну дат отсекает целые диапазоны блоков
SELECT count(*) FROM flights
WHERE scheduled_departure >= '2017-08-01'
  AND scheduled_departure <  '2017-08-08';
EXCLUDE-ограничение через GiST не даёт пересечься занятым интервалам, чего обычным UNIQUE не выразить. Чтобы смешать равенство и пересечение в одном GiST, нужно расширение btree_gist:

Код: Выделить всё

CREATE EXTENSION IF NOT EXISTS btree_gist;
-- запрет на пересекающиеся периоды для одного ресурса:
-- EXCLUDE USING gist (resource_id WITH =, during WITH &&)
Хеш-индекс для строгого равенства по номеру билета:

Код: Выделить всё

CREATE INDEX idx_bp_ticket_hash ON boarding_passes USING hash (ticket_no);
Частые грабли
  • Ставят hash, ожидая ускорения диапазонов и ORDER BY - он умеет только =, для остального остаётся B-tree.
  • BRIN на таблице, где данные перемешаны (нет корреляции физического порядка и значения), бесполезен: проверьте correlation в pg_stats.
  • GIN сильно замедляет INSERT/UPDATE. На потоке вставок не забывайте про fastupdate и gin_pending_list_limit.
  • Забывают, что нужный класс операторов лежит в расширении: pg_trgm для триграмм, btree_gist чтобы смешать = и && в одном GiST.
  • GIN и GiST по jsonb/массивам бывают огромными - проверяйте размер через \di+ или pg_relation_size, прежде чем катить в прод.
  • Создание индекса блокирует таблицу. На бою всегда CREATE INDEX CONCURRENTLY (подробно в следующих уроках).
Мини-лаба
  • Разверните демобазу bookings и подключитесь: psql -d demo.
  • Создайте GiST-индекс по airports(coordinates) и выполните KNN-запрос с ORDER BY coordinates <-> point. Сравните план через EXPLAIN до и после индекса.
  • Добавьте в tickets текстовый массив route, заполните парой значений и постройте GIN-индекс; проверьте оператор @>.
  • Постройте BRIN по flights(scheduled_departure) и сравните его размер с B-tree по тому же столбцу через \di+.
  • Посмотрите correlation для scheduled_departure в pg_stats и объясните, почему BRIN тут уместен.
  • Через EXPLAIN (ANALYZE, BUFFERS) сравните чтение блоков BRIN против B-tree на запросе по неделе.
Контрольные вопросы
  • Почему hash-индекс не ускоряет ORDER BY и запросы с диапазоном?
  • В чём принципиальная разница между GiST и SP-GiST по способу организации данных?
  • Какой индекс выберете для поиска по jsonb-документам и почему именно GIN?
  • От какого свойства таблицы зависит, будет ли BRIN полезен?
  • Зачем нужно расширение btree_gist и какую задачу решает EXCLUDE-ограничение?
  • Что изменилось для hash-индексов начиная с PostgreSQL 10?
👍1 ❤️2 🔥1 😄 🤔
Аватара пользователя
fpgahacker
Сообщения: 1
Зарегистрирован: 14 май 2026, 05:12

Re: Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN

Сообщение fpgahacker »

А btree_gist обязателен для EXCLUDE? У меня период через tstzrange и без расширения завелось, а вот когда добавил room_id с равенством - сразу ошибка про класс операторов. Теперь понятно почему.
👍2 ❤️1 🔥 😄 🤔
Аватара пользователя
seniorheap
Сообщения: 1
Зарегистрирован: 18 май 2026, 03:44

Re: Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN

Сообщение seniorheap »

Сделал BRIN по дате на таблице событий, размер реально в сотни раз меньше btree. Но пока не сделал CLUSTER, прироста почти не было - корреляция гуляла. Проверяйте pg_stats, это не для красоты.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Индексы: B-tree и когда индекс не используется
Следующая глава →
Продвинутые индексы: частичные, по выражению, покрывающие

Все главы курса «PostgreSQL: от первого запроса до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: частичный и покрывающий индекс в postgresql когда нуженjsonb в postgresql операторы и индексацияпартиционирование таблиц postgresql по датематериализованное представление postgresql как обновлятьpostgresql какой индекс выбрать btree gin gist brin

Вернуться в «PostgreSQL: от первого запроса до продакшена»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость