Mysql
 sql >> база данни >  >> RDS >> Mysql

Кой подход е по-бърз за получаване на всички POI от MySQL/MariaDB с PHP/Laravel

Коя формула използвате за разстоянието няма голямо значение. Много по-важен е броят на редовете, които трябва да прочетете, обработите и сортирате. В най-добрия случай можете да използвате индекс за условие в клаузата WHERE, за да ограничите броя на обработените редове. Можете да опитате да категоризирате местоположенията си - но това зависи от естеството на вашите данни, дали това ще работи добре. Вие също ще трябва да разберете коя "категория" да използвате. По-общо решение би било използването на ПРОСТРАНИЧЕН ИНДЕКС и ST_Within() функция.

Сега нека проведем някои тестове...

В моята DB (MySQL 5.7.18) имам следната таблица:

CREATE TABLE `cities` (
    `cityId` MEDIUMINT(9) UNSIGNED NOT NULL AUTO_INCREMENT,
    `country` CHAR(2) NOT NULL COLLATE 'utf8mb4_unicode_ci',
    `city` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
    `accentCity` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
    `region` CHAR(2) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
    `population` INT(10) UNSIGNED NULL DEFAULT NULL,
    `latitude` DECIMAL(10,7) NOT NULL,
    `longitude` DECIMAL(10,7) NOT NULL,
    `geoPoint` POINT NOT NULL,
    PRIMARY KEY (`cityId`),
    SPATIAL INDEX `geoPoint` (`geoPoint`)
) COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB

Данните идват от Безплатна база данни за световни градове и съдържа 3173958 (3,1M) реда.

Имайте предвид, че geoPoint е излишен и е равен на POINT(longitude, latitude) .

Считайте, че потребителят се намира някъде в Лондон

set @lon = 0.0;
set @lat = 51.5;

и искате да намерите най-близкото местоположение от cities таблица.

Една "тривиална" заявка би била

select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
order by dist
limit 1

Резултатът е

988204 Blackwall 1085.8212159861014

Време за изпълнение:~ 4,970 сек

Ако използвате по-малко сложната функция ST_Distance() , получавате същия резултат с време на изпълнение от ~ 4,580 сек – което не е толкова голяма разлика.

Имайте предвид, че не е необходимо да съхранявате гео точка в таблицата. Можете също да използвате (point(c.longitude, c.latitude)). вместо c.geoPoint . За моя изненада е дори по-бърз (~3,6 секунди за ST_Distance и ~4.0 сек за ST_Distance_Sphere ). Може да е дори по-бързо, ако нямах geoPoint колона изобщо. Но това все още няма голямо значение, тъй като не искате потребителят да чака, така че влезте за повторно време, ако можете да направите по-добре.

Сега нека разгледаме как можем да използваме ПРОСТРАНИЧЕН ИНДЕКС с ST_Within() .

Трябва да дефинирате многоъгълник който ще съдържа най-близкото местоположение. Един прост начин е да използвате ST_Buffer() който ще генерира многоъгълник с 32 точки и е почти кръг*.

set @point = point(@lon, @lat);
set @radius = 0.1;
set @polygon = ST_Buffer(@point, @radius);

select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
where st_within(c.geoPoint, @polygon)
order by dist
limit 1

Резултатът е същият. Времето за изпълнение е ~ 0,000 сек (това е, което моят клиент (HeidiSQL ) казва).

* Имайте предвид, че @radius се отбелязва в градуси и по този начин многоъгълникът ще прилича повече на елипса, отколкото на кръг. Но в моите тестове винаги получавах същия резултат като при простото и бавно решение. Въпреки това бих проучил повече крайни случаи, преди да го използвам в производствения си код.

Сега трябва да намерите оптималния радиус за вашето приложение/данни. Ако е твърде малък - може да нямате резултати или да пропуснете най-близката точка. Ако е твърде голям - може да се наложи да обработите твърде много редове.

Ето някои числа за дадения тестов случай:

  • @radius =0,001:Няма резултат
  • @radius =0,01:точно едно местоположение (вид късмет) – Време за изпълнение ~ 0,000 сек.
  • @radius =0,1:55 местоположения – Време за изпълнение ~ 0,000 сек.
  • @radius =1.0:2183 местоположения – Време за изпълнение ~ 0,030 сек.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Копиране/дублиране на база данни без използване на mysqldump

  2. mysql форматиране на дата с php

  3. Postgresl select count(*) отнема много време

  4. Не може да се създаде Multi-AZ Aurora RDS екземпляр

  5. Как да изпълним скрипт на PowerShell в Task Scheduler?