🎯 GiST индексы: когда B-tree сдох, а GIN перебор

GiST (Generalized Search Tree) — индекс для нестандартной хуйни, которую обычные индексы не переваривают. Геоданные, диапазоны, полнотекст, кастомные типы.
Если данные не укладываются в "больше-меньше-равно", твой выбор.

🗺️ PostGIS: координаты не работают с B-tree

Задача: найти все кафе в радиусе 1км. B-tree на lat/lng = Seq Scan и пиздец.

-- Не делай так (B-tree на координатах):
CREATE INDEX idx_fail ON locations(lat, lng);
SELECT * FROM locations
WHERE lat BETWEEN 55.74 AND 55.76 AND lng BETWEEN 37.60 AND 37.62;
-- Найдет квадрат, не круг. Кафе из соседнего района тоже попадут

-- Правильно (GiST + PostGIS):
CREATE EXTENSION postgis;
ALTER TABLE locations ADD COLUMN geom geography(POINT, 4326);
UPDATE locations SET geom = ST_SetSRID(ST_MakePoint(lng, lat), 4326);

CREATE INDEX idx_locations_geom ON locations USING GIST (geom);

SELECT * FROM locations
WHERE ST_DWithin(geom, ST_MakePoint(37.617635, 55.755826)::geography, 1000);
-- Execution: 12ms на 5M точек vs 4500ms Seq Scan

Бенчмарк: 5M точек, радиус 1км от центра Москвы. B-tree квадрат: 180ms + ложные срабатывания. GiST круг: 12ms, точные результаты.

🐕 Первое сравнение: геометрия

Пёс ищет еду в квадратной комнате — проверяет все углы (B-tree квадрат). Умный пёс ищет в радиусе от своей миски — только актуальная зона (GiST круг). Второй находит быстрее и без лишнего.

📝 Полнотекстовый поиск: GiST vs GIN

GIN быстрее для поиска. GiST меньше весит и быстрее обновляется.

CREATE TABLE articles (id SERIAL, title TEXT, content TEXT, tsv tsvector);

-- GIN: быстрый поиск
CREATE INDEX idx_gin ON articles USING GIN (tsv);
-- Размер: 450MB | Поиск: 8ms | INSERT: 120ms

-- GiST: быстрая вставка
CREATE INDEX idx_gist ON articles USING GIST (tsv);
-- Размер: 280MB (на 38% меньше!) | Поиск: 25ms | INSERT: 45ms

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

Когда GiST: частые INSERT/UPDATE (логи, новости, чаты), размер индекса критичен.
Когда GIN: поиск важнее вставок, read-heavy база.

"GIN всегда лучше для полнотекста"

Реальность: Новостной сайт, 500 статей/час. GIN обновление 120ms × 500 = 16 секунд CPU/час. GiST: 45ms = 6 секунд. Экономия 240 часов CPU/месяц =

"Геоданные сложные, используем два WHERE на lat/lng"

Реальность: Квадрат вместо круга = пользователь видит кафе в 5км, но не видит в 900м за углом. Конверсия -15%, потому что Иванов И.И. "упростил".

```sql
-- Бронирование переговорок:
CREATE TABLE bookings (
id SERIAL PRIMARY KEY,
room_id INT,
time_range tstzrange
);

CREATE INDEX idx_bookings_time ON bookings USING GIST (time_range);

-- Найти пересечения:
SELECT * FROM bookings
WHERE room_id = 5 AND time_range && tstzrange('2025-11-14 14:00', '2025-11-14 16:00');

-- EXCLUSION: физически запретить двойное бронирование
CREATE TABLE bookings (
room_id INT,
time_range tstzrange,
EXCLUDE USING GIST (room_id WITH =, time_range WITH &&)
);
-- База сама гарантирует непересечение, двойное бронирование невозможно
```

Спрашиваешь собаку "ты свободен 14:00-16:00?" B-tree проверяет начало и конец отдельно, путается. GiST понимает диапазоны целиком: пёс занят 15:00-17:00 = пересечение есть, слот занят. Логика правильная. 🚀 PostgreSQL 16: параллельное построение

До PG16: GiST строился в один поток. 100M строк = 40 минут.
PostgreSQL 16:

CREATE INDEX CONCURRENTLY idx_locations_geom ON locations USING GIST (geom);
-- Автоматически распараллеливает
-- 8 cores: 40 минут → 12 минут | 16 cores: 40 минут → 7 минут

SET max_parallel_maintenance_workers = 4; -- дефолт 2, можно до CPU cores


Прирост PG16: построение GiST +60-70% скорость, rebuild в 3-4 раза быстрее, VACUUM +40%.

🔥 Кастомные типы
-- Диапазоны дат:
CREATE INDEX idx_date_ranges ON events USING GIST (daterange(start_date, end_date));

-- IP адреса:
CREATE EXTENSION btree_gist;
CREATE INDEX idx_ip_ranges ON networks USING GIST (ip_range);

-- Массивы с пересечениями:
CREATE INDEX idx_tags ON articles USING GIST (tags gist__intbig_ops);

B-tree = овчарка: "сидеть", "лежать", "охранять". Команды простые, работает быстро.
GiST = служебный пёс: поиск взрывчатки, наркотиков, людей в завалах. Сложнее тренировать, медленнее в базовых командах, но умеет то, что овчарка физически не может.
Пытаешься научить овчарку искать людей в завалах (геопоиск). Нужен специально обученный пёс.

Практический чеклист
Используй GiST:

Геоданные (PostGIS): координаты, полигоны
Диапазоны: tstzrange, daterange, int4range
Полнотекст с частыми UPDATE
EXCLUSION constraints

НЕ используй:

Обычные числа/строки (B-tree быстрее)
JSONB/массивы (GIN эффективнее)
Полнотекст read-heavy (GIN быстрее)

Итого: GiST в PostgreSQL 16
PostGIS? GiST обязателен. Диапазоны? GiST. Полнотекст с частыми вставками? GiST. Все остальное? Смотри другие индексы.
В PG16: параллельное построение (+60%), обнови max_parallel_maintenance_workers с дефолтных 2 до числа cores.