Logo Craft Homelab Docs Контакты Telegram
Geo-данные в БД — PostGIS, индексы, запросы Трендовые github проекты в нашем телеграм канале. Подпишись 👉
Fri Feb 20 2026

Geo-данные в БД: PostGIS, индексы

Geo-данные в базах данных — это не просто координаты на карте. Это сложная структура, требующая специальных подходов к хранению, индексации и запросам. Когда ваше приложение начинает работать с геоданными по-настоящему, стандартные реляционные БД оказываются беспомощными.

Архитектурные вызовы геоданных в БД

Проблема геоданных в реляционных БД начинается с их фундаментальной природы. Стандартные SQL-запросы не предназначены для работы с пространственными отношениями — расстоянием, пересечением, содержанием. Вы можете хранить широту и долготу как числа, но запросить “найти все точки в радиусе 1 км от данной” без специализированных расширений становится болезненным упражнением.

PostGIS решает эту проблему, превращая PostgreSQL в полноценную spatial database. Но что под капотом?

Как PostGIS работает под капотом

PostGIS — это расширение PostgreSQL, добавляющее поддержку географических объектов. Его архитектура основана на трех ключевых компонентах:

  1. Типы данных: PostGIS определяет специальные типы, такие как GEOMETRY и GEOGRAPHY. Разница между ними фундаментальна: GEOMETRY работает с плоской проекцией (квадратной сеткой), в то время как GEOGRAPHY учитывает кривизну Земли.

  2. Функции и операторы: Сотни функций для пространственных операций — от ST_Distance до ST_Contains. Но настоящая магия происходит в реализации этих функций.

  3. Индексы: Специализированные индексы (R-tree, GiST) для эффективного пространственного поиска.

Когда вы выполняете пространственный запрос, PostGIS сначала использует индекс для сокращения набора кандидатов, а затем применяет точную функцию к оставшимся объектам. Это два этапа — фильтрация и уточнение.

Практический пример: Поиск точек в радиусе

Давайте рассмотрим реальный сценарий — поиск всех кофеен в радиусе 500 метров от пользователя. На первый взгляд, запрос кажется простым:

SELECT *
FROM cafes
WHERE ST_Distance(geom, ST_MakePoint(37.7749, -122.4194)::geography) <= 500;

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

Решение — добавить пространственный индекс:

CREATE INDEX idx_cafes_geom ON cafes USING GIST (geom);

Но даже с индексом есть нюансы. Для GEOGRAPHY тип данных, PostGIS использует индексы GiST (Generalized Search Tree). GiST — это сбалансированное дерево, которое позволяет выполнять не только точные совпадения, но и диапазонные запросы.

Важно: для GEOMETRY тип данных вы можете использовать индексы R-tree, которые иногда быстрее, но не учитывают кривизну Земли.

Оптимизация запросов: Трюки из реального проекта

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

Проблема в том, что индекс GiST эффективен для фильтрации по грубой оценке (прямоугольник, содержащий круг), но точное вычисление расстояния для всех кандидатов дорогое.

Решение двухшаговое:

-- Сначала грубая фильтрация по квадрату, содержащем круг
SELECT *
FROM cafes
WHERE geom && ST_Expand(ST_MakePoint(37.7749, -122.4194)::geography, 500);

-- Затем точная проверка расстояния для результатов
SELECT *
FROM cafes
WHERE ST_Distance(geom, ST_MakePoint(37.7749, -122.4194)::geography) <= 500;

Но в PostGIS это можно сделать одним запросом, используя оператор <->, который автоматически использует двухфазную индексную стратегию:

SELECT *
FROM cafes
WHERE ST_Distance(geom, ST_MakePoint(37.7749, -122.4194)::geography) <= 500;

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

Узкие места, о которых нужно знать

  1. Точность vs. Производительность: Чем выше точность вычислений, тем медленнее запросы. Для большинства приложений доставки или поиска достаточно метровой точности.

  2. Размер индексов: Spatial занимают много места. В нашем проекте индексы занимали до 40% от общего размера таблицы.

  3. Миграция данных: Если вы начинаете с обычных числовых полей для координат, миграция на PostGIS — нетривиальная задача. Требуется преобразование данных и перестроение всей архитектуры.

  4. Операции с большими полигонами: Запросы вроде “найти все точки внутри многоугольника” могут быть медленными, если полигон большой и сложный. В таких случаях стоит рассмотреть предварительное разбиение полигона на части.

  5. Кэширование: Spatial запросы редко кэшируются эффективно из-за их сложности. Разработчики часто полагаются на кэширование на уровне приложения.

Альтернативы и их компромиссы

PostGIS — не единственное решение:

  1. MongoDB с GeoJSON: Более гибкий в схемы, но менее мощный для сложных пространственных запросов. Идеально для простых приложений типа “найти ближайших”.

  2. Специализированные базы данных (SpatiaLite, Oracle Spatial): Могут быть быстрее для специфических задач, но часто имеют более высокую стоимость или менее открытые лицензии.

  3. Внешние сервисы (Google Maps API): Просты в использовании, но зависят от внешнего сервиса и платные при масштабировании.

Вывод: Когда использовать PostGIS?

Используйте PostGIS, когда:

  • Ваше приложение relies на сложных пространственных запросах
  • Требуется высокая точность вычислений
  • Вы уже используете PostgreSQL и хотите избежать сложностей интеграции с другой БД
  • У вас есть команда с опытом работы с PostGIS

Избегайте PostGIS, если:

  • Ваше приложение выполняет только простые запросы типа “найти ближайших”
  • Вы не готовы инвестировать в оптимизацию и администрирование spatial индексов
  • Ваш проект имеет строгие ограничения по размеру базы данных

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