База данных: лучший способ выполнить запрос данных о географическом местоположении? - PullRequest
38 голосов
/ 28 ноября 2009

У меня есть база данных MySQL. Я храню дома в базе данных и выполняю буквально всего 1 запрос к базе данных, , но мне нужно, чтобы этот запрос выполнялся очень быстро , и это возвращает все дома в квадрате широта и долгота.

SELECT * FROM homes 
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

Как мне лучше всего сохранить свои геоданные, чтобы я мог выполнить этот запрос, чтобы быстрее отобразить все дома в окне геолокации?

В основном:

  • Использую ли я лучший оператор SQL для выполнения этого запроса быстрее всего?
  • Существует ли какой-либо другой метод, может быть, даже не использующий базу данных, чтобы я мог быстрее всего запросить результат поиска домов в пределах границ геолокации в штучной упаковке?

На случай, если это поможет, я включу следующую схему таблицы базы данных:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;

UPDATE

Я понимаю, что пространственная воля будет влиять на кривизну Земли, но меня больше всего интересует возвращение геоданных как можно быстрее. Если эти пакеты пространственной базы данных не возвращают данные быстрее, не советуйте пространственные расширения. Спасибо

ОБНОВЛЕНИЕ 2

Пожалуйста, обратите внимание, что никто из нижеприведенных действительно не ответил на вопрос. Я действительно с нетерпением жду любой помощи, которую я мог бы получить. Заранее спасибо.

Ответы [ 11 ]

13 голосов
/ 30 ноября 2009

Есть хорошая статья о производительности геолокации MySQL здесь .

РЕДАКТИРОВАТЬ Уверен, это использует фиксированный радиус. Кроме того, я не уверен на 100%, что алгоритм для вычисления расстояния является наиболее продвинутым (то есть он будет «сверлить» через Землю).

Что важно, так это то, что алгоритм дешев, чтобы дать вам ограничение по количеству рядов для правильного поиска расстояния.


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

Предварительно рассчитайте это или используйте хранимую процедуру, как предлагает источник:

# Pseudo code
# user_lon and user_lat are the source longitude and latitude
# radius is the radius where you want to search
lon_distance = radius / abs(cos(radians(user_lat))*69);
min_lon = user_lon - lon_distance;
max_lon = user_lon + lon_distance;
min_lat = user_lat - (radius / 69);
max_lat = user_lat + (radius / 69);
SELECT dest.*,
  3956 * 2 * ASIN(
    SQRT(
      POWER(
        SIN(
          (user_lat - dest.lat) * pi() / 180 / 2
        ), 2
      ) + COS(
        user_lat * pi() / 180
      ) * COS(
        dest.lat * pi() / 180
      ) * POWER(
        SIN(
          (user_lon - dest.lon) * pi() / 180 / 2
        ), 2
      )
    )
  ) as distance
FROM dest
WHERE 
  dest.lon between min_lon and max_lon AND
  dest.lat between min_lat and max_lat
HAVING distance < radius
ORDER BY distance
LIMIT 10
4 голосов
/ 28 ноября 2009

У меня была такая же проблема, и я написал блог из 3 частей. Это было быстрее, чем географический индекс.

Intro , Benchmark , SQL

2 голосов
/ 28 ноября 2009

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

Если наборы результатов достаточно малы, вы все равно можете вносить исправления в логику приложения (легче масштабировать по горизонтали, чем в базе данных), одновременно предоставляя возможность получать точные результаты.

Взгляните на geobox.py Брета Слаткина, в котором содержится отличная документация для подхода.

Я бы по-прежнему рекомендовал проверить PostgreSQL и PostGIS по сравнению с MySQL, если вы собираетесь выполнять более сложные запросы в обозримом будущем.

1 голос
/ 22 июня 2012

Вот трюк, который я использовал с некоторым успехом, - это создание областей округления. То есть, если у вас есть местоположение с 36.12345, -120.54321, и вы хотите сгруппировать его с другими местоположениями, находящимися в пределах полумильного (приблизительного) поля сетки, вы можете назвать его регион 36.12x-120.54, и все остальные местоположения с таким же регионом округления попадут в одно и то же поле.

Очевидно, что вы не получите чистый радиус, т. Е. Если местоположение, на которое вы смотрите, ближе к одному краю, чем к другому. Однако при такой настройке достаточно легко рассчитать восемь блоков, окружающих ящик вашего основного местоположения. Для остроумия:

[36.13x-120.55][36.13x-120.54][36.13x-120.53]
[36.12x-120.55][36.12x-120.54][36.12x-120.53]
[36.11x-120.55][36.11x-120.54][36.11x-120.53]

Извлеките все местоположения с соответствующими метками округления, а затем, как только вы получите их из базы данных, вы можете выполнить расчеты расстояния, чтобы определить, какие из них использовать.

1 голос
/ 30 ноября 2009

Придерживаясь вашего текущего подхода, вы должны сделать одно изменение, Вместо того, чтобы индексировать geolat и geolong отдельно, у вас должен быть составной индекс:

KEY `geolat_geolng` (`geolat`, `geolng`),

В настоящее время ваш запрос будет использовать только один из двух индексов.

1 голос
/ 28 ноября 2009

Используемые вами индексы действительно являются индексами B-дерева и поддерживают ключевое слово BETWEEN в вашем запросе. Это означает, что оптимизатор может использовать ваши индексы, чтобы найти дома в вашей «коробке». Это, однако, не означает, что он всегда будет использовать индексы. Если вы укажете диапазон, который содержит слишком много «хитов», индексы не будут использоваться.

0 голосов
/ 11 декабря 2017

Поскольку MySQL 5.7 mysql может использовать геоиндекс, такой как ST_Distance_Sphere () и ST_Contains (), что улучшает производительность.

0 голосов
/ 20 августа 2016

Вы можете рассмотреть возможность создания отдельной таблицы 'GeoLocations', которая имеет первичный ключ ('geolat', 'geolng') и имеет столбец, который содержит home_id, если в данном конкретном геолокации есть дом. Это должно позволить оптимизатору искать диапазон географических местоположений, которые будут отсортированы на диске для получения списка home_ids. Затем вы можете выполнить объединение со своей таблицей 'homes', чтобы найти информацию об этих home_ids.

CREATE TABLE IF NOT EXISTS `GeoLocations` (
`geolat` decimal(10,6) NOT NULL,
`geolng` decimal(10,6) NOT NULL,
`home_id` int(10) NULL
PRIMARY KEY  (`geolat`,`geolng`)
);

SELECT GL.home_id
FROM GeoLocations GL
INNER JOIN Homes H
 ON GL.home_id = H.home_id
WHERE GL.geolat between X and Y
 and GL.geolng between X and Y
0 голосов
/ 30 мая 2012

Очень хорошая альтернатива - MongoDB с ее геопространственной индексацией .

0 голосов
/ 30 ноября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...