linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it worksскоро
  • Уроки
  • База знаний
  • Собеседование
Часть VII — Индексы

$ глава 34 · 50 минут

GiST и SP-GiST

B-tree и hash отвечают на «равно» и «больше-меньше». Но есть запросы другого рода: «какие брони пересекаются по времени с этой?», «найди ближайшие точки», «какие диапазоны содержат значение». Линейный порядок тут не помогает - нужно дерево, понимающее «пересекается» и «содержит». Это GiST.

GiST - не один индекс, а каркас, на котором строят деревья поиска для геометрии, диапазонов, полнотекста и расстояний. Рядом стоит SP-GiST - для данных, которые естественно делятся на непересекающиеся области. Глава обзорная: цель - понять идею обоих и увидеть GiST в работе на диапазонах и exclusion constraint.

34.1 Дерево, понимающее «пересекается»

B-tree упорядочивает ключи в одну линию. Для двумерных и интервальных данных линии нет: у прямоугольника или временно́го интервала нет естественного «меньше». Зато есть операции пересечения (&&), содержания (@>), расстояния (<->).

GiST (Generalized Search Tree) - дерево, узлы которого хранят не разделители-значения, а предикаты, описывающие всё поддерево: «здесь лежат объекты внутри такого прямоугольника» или «диапазоны внутри такого интервала». Спуск идёт по тем поддеревьям, чей предикат не противоречит запросу. Какой оператор поддержан, как всегда, решает operator class (см. operator-classes) - для GiST это &&, @>, <@, <-> и подобные.

34.2 R-дерево над bounding box

Самый наглядный случай GiST - R-дерево для прямоугольников и диапазонов. Каждый внутренний узел хранит минимальный ограничивающий бокс (bounding box), покрывающий все его дочерние объекты.

[ bbox: весь регион ]
  ├── [ bbox: северо-запад ] → точки/боксы
  └── [ bbox: юго-восток ]   → точки/боксы

Поиск «что пересекается с X» спускается только в те узлы, чей бокс пересекается с X, отсекая остальные. Для диапазонов то же самое: узел хранит интервал, покрывающий дочерние интервалы, и запрос «пересекается с [a,b]» идёт лишь в подходящие ветви. Так GiST ускоряет geo-поиск и работу с range-типами.

34.3 penalty и picksplit

GiST - каркас: чтобы он заработал на новом типе, автор типа реализует набор support-функций. Две из них определяют качество дерева:

  • penalty - насколько «дорого» вставить объект в данное поддерево (как сильно придётся расширить его bounding box). Вставка идёт туда, где penalty минимальна, - чтобы боксы росли как можно меньше и меньше перекрывались.
  • picksplit - как разделить переполненную страницу на две, чтобы получившиеся боксы были компактными и мало пересекались.

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

34.4 Exclusion constraint

Одно из самых полезных применений GiST - запрет пересечений на уровне ограничения целостности. Обычный UNIQUE запрещает одинаковые значения; exclusion constraint запрещает пересекающиеся.

sql
CREATE TABLE room_booking (
  room int,
  during tstzrange,
  EXCLUDE USING gist (room WITH =, during WITH &&)
);

Читается так: «не должно быть двух строк, где room одинаковый (=) И интервалы during пересекаются (&&)». Это декларативный запрет двойного бронирования одной комнаты - база сама не пустит пересекающуюся вставку. Под капотом ограничение опирается на GiST-индекс, потому что только он умеет оператор &&. Подробнее - в gist-spgist.

34.4.1 Подводный камень: GiST бывает lossy

GiST может быть приблизительным (lossy): индекс по bounding box говорит «этот объект, возможно, подходит», но окончательно проверить должен сам кортеж. Поэтому в плане появляется Recheck Cond - повторная проверка условия на странице таблицы.

Это не баг, а природа пространственных индексов: bounding box огрубляет реальную геометрию (круг внутри прямоугольника), и точную принадлежность определяет recheck. Практическое следствие - GiST отсекает большинство непод­ходящих строк, но за финальной правдой всё равно идёт в таблицу, поэтому Index-Only Scan для него обычно невозможен.

34.5 SP-GiST: разбиение пространства

SP-GiST (Space-Partitioned GiST) решает похожие задачи, но другим приёмом. Вместо перекрывающихся боксов он делит пространство на непересекающиеся, неравные части - как quadtree для точек или radix-дерево для строк.

Ключевое отличие от GiST: разбиения не пересекаются, поэтому при поиске спуск идёт ровно в одну ветвь на каждом шаге, без захода в соседние. Это выгодно для данных, у которых есть естественное непересекающееся разбиение: точки на плоскости (каждая в своём квадранте), IP-адреса, текст по префиксу.

sql
CREATE INDEX ON places USING spgist (location);
CREATE INDEX ON hosts  USING spgist (addr inet_ops);

SP-GiST-деревья несбалансированные (глубина зависит от распределения данных), и это нормально для их задач. Выбор между GiST и SP-GiST - про природу данных: перекрываются ли объекты естественно (GiST) или делятся на непересекающиеся области (SP-GiST).

34.6 Когда какой

Короткая карта применений, чтобы не путать AM этой главы:

  • геометрия, диапазоны, exclusion constraint, поиск ближайших (<->, KNN) - GiST;
  • точки с естественным квадрантным делением, IP-сети, префиксы строк
    • SP-GiST;
  • равенство и порядок - B-tree;
  • содержание элементов в массивах/JSONB/полнотексте - GIN (следующая глава).

Оба, GiST и SP-GiST, lossy и требуют recheck, оба расширяемы через support-функции. Для большинства прикладных задач с диапазонами и геометрией стартовая точка - GiST; SP-GiST берут, когда данные явно просятся в непересекающееся разбиение.

Уроки в sandbox

lab-34.1. Запретить пересечения через GiST exclusion constraint

Построим таблицу бронирований с GiST-индексом по диапазону и запретом пересечений. Перед вставкой пересекающейся брони предскажи, пройдёт ли она.

  1. Создай таблицу с запретом пересечений: CREATE TABLE room_booking (room int, during tstzrange, EXCLUDE USING gist (room WITH =, during WITH &&));.

  2. Вставь непересекающиеся брони одной комнаты: INSERT INTO room_booking VALUES (1, '[2026-01-01 10:00, 2026-01-01 12:00)'), (1, '[2026-01-01 12:00, 2026-01-01 14:00)'); - проходит.

  3. Попробуй пересекающуюся: INSERT INTO room_booking VALUES (1, '[2026-01-01 11:00, 2026-01-01 13:00)'); - предскажи и проверь: ошибка conflicting key (exclusion).

  4. Заполни диапазонами таблицу cal: CREATE TABLE cal AS SELECT g id, tstzrange(now()+(g||'h')::interval, now()+((g+1)||'h')::interval) during FROM generate_series(1,50000) g; CREATE INDEX ON cal USING gist (during); ANALYZE cal;.

  5. Проверь, что GiST ускоряет пересечение: EXPLAIN SELECT * FROM cal WHERE during && tstzrange(now()+'100h', now()+'101h'); - используется индексный доступ по gist.

sandbox с автопроверкой - открыть в песочнице

Резюме

  • GiST - каркас деревьев поиска для операций пересечения, содержания и расстояния (&&, @>, <->), которых не понимают B-tree и hash.
  • Наглядный случай GiST - R-дерево: внутренние узлы хранят bounding box, покрывающий детей; поиск спускается только в пересекающиеся с запросом ветви.
  • Качество дерева задают support-функции penalty (куда вставлять) и picksplit (как делить страницу); плохой split даёт перекрытие боксов и медленный поиск.
  • Exclusion constraint (EXCLUDE USING gist) декларативно запрещает пересечения - например, двойное бронирование комнаты по времени.
  • GiST обычно lossy: bounding box огрубляет геометрию, поэтому в плане есть Recheck Cond и Index-Only Scan для него недоступен.
  • SP-GiST делит пространство на непересекающиеся части (quadtree, radix); спуск идёт в одну ветвь, выгодно для точек, IP-сетей, префиксов строк.
  • Карта: B-tree - порядок/равенство; GiST - пересечения/диапазоны/KNN; SP-GiST - непересекающиеся разбиения; GIN - содержание элементов.

Контрольные вопросы

  1. Почему для поиска «какие интервалы пересекаются с заданным» не годится B-tree?

    Показать ответ

    Потому что B-tree упорядочивает ключи в одну линию, а у интервалов нет естественного линейного порядка, отражающего пересечение: два интервала могут пересекаться, имея совсем разные «начала». Оператор пересечения && не входит в operator class B-tree. Нужен GiST: его узлы хранят предикаты (например, охватывающий интервал поддерева), и поиск спускается только в те ветви, чей предикат пересекается с запросом, отсекая остальные.

  2. Что делают support-функции penalty и picksplit?

    Показать ответ

    penalty оценивает, насколько дорого вставить объект в данное поддерево - то есть как сильно придётся расширить его bounding box; вставка идёт туда, где penalty минимальна, чтобы боксы росли меньше. picksplit определяет, как разделить переполненную страницу на две, чтобы получившиеся боксы были компактными и слабо пересекались. От их качества зависит, насколько хорошо дерево отсекает ветви при поиске: плохой picksplit даёт сильное перекрытие боксов, и поиск вынужден заходить во многие ветви.

  3. Что такое exclusion constraint и какой индекс под ним?

    Показать ответ

    Это ограничение целостности, запрещающее не одинаковые (как UNIQUE), а пересекающиеся значения. Например, EXCLUDE USING gist (room WITH =, during WITH &&) запрещает две строки с одинаковой комнатой и пересекающимися интервалами времени - декларативный запрет двойного бронирования. Под капотом он опирается на GiST-индекс, потому что проверка опирается на оператор &&, который умеет именно GiST, а не B-tree или hash.

  4. Почему у GiST в плане появляется Recheck Cond?

    Показать ответ

    Потому что GiST обычно lossy: индекс хранит огрублённое описание объекта (bounding box), которое говорит «возможно, подходит», но не гарантирует точного совпадения - прямоугольник грубее, чем реальная геометрия внутри. Поэтому после индексного отбора PostgreSQL перепроверяет точное условие на самой строке, что и отражается как Recheck Cond. Следствие: GiST отсекает большинство лишних строк, но за финальной проверкой идёт в таблицу, и Index-Only Scan обычно недоступен.

  5. Чем SP-GiST отличается от GiST по принципу?

    Показать ответ

    GiST допускает перекрывающиеся области (bounding box-ы детей могут пересекаться), и при поиске иногда приходится заходить в несколько ветвей. SP-GiST делит пространство на непересекающиеся, обычно неравные части (quadtree для точек, radix-дерево для строк), поэтому на каждом шаге спуск идёт ровно в одну ветвь. SP-GiST выгоден, когда у данных есть естественное непересекающееся разбиение - точки по квадрантам, IP-сети, текст по префиксу; его деревья при этом несбалансированные, и это нормально.

← Предыдущая33-hashСледующая →35-gin
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки