Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Welcher Ansatz ist schneller, um alle POIs von MySQL/MariaDB mit PHP/Laravel zu erhalten

Welche Formel Sie für die Entfernung verwenden, spielt keine große Rolle. Viel wichtiger ist die Anzahl der Zeilen, die Sie lesen, verarbeiten und sortieren müssen. Im besten Fall können Sie einen Index für eine Bedingung in der WHERE-Klausel verwenden, um die Anzahl der verarbeiteten Zeilen zu begrenzen. Sie können versuchen, Ihre Standorte zu kategorisieren – aber es hängt von der Art Ihrer Daten ab, ob das gut funktioniert. Sie müssten auch herausfinden, welche "Kategorie" Sie verwenden sollen. Eine allgemeinere Lösung wäre die Verwendung eines RÄUMLICHEN INDEX und die ST_Within() Funktion.

Lassen Sie uns nun einige Tests durchführen..

In meiner DB (MySQL 5.7.18) habe ich die folgende Tabelle:

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

Die Daten stammen aus der Free World Cities Database und enthält 3173958 (3,1 Millionen) Zeilen.

Beachten Sie, dass geoPoint ist redundant und gleich POINT(longitude, latitude) .

Angenommen, der Benutzer befindet sich irgendwo in London

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

und Sie möchten den nächstgelegenen Ort aus den cities finden Tabelle.

Eine "triviale" Abfrage wäre

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

Das Ergebnis ist

988204 Blackwall 1085.8212159861014

Ausführungszeit:~ 4.970 Sek.

Wenn Sie die weniger komplexe Funktion ST_Distance() verwenden , erhalten Sie das gleiche Ergebnis mit einer Ausführungszeit von ~ 4.580 Sekunden - was kein so großer Unterschied ist.

Beachten Sie, dass Sie keinen Geopunkt in der Tabelle speichern müssen. Sie können genauso gut (point(c.longitude, c.latitude) verwenden statt c.geoPoint . Zu meiner Überraschung ist es sogar noch schneller (~3,6 Sek. für ST_Distance und ~4,0 Sek. für ST_Distance_Sphere ). Es könnte sogar noch schneller gehen, wenn ich keinen geoPoint hätte Spalte überhaupt. Aber das macht immer noch nicht viel aus, da Sie nicht wollen, dass der Benutzer wartet, also melden Sie sich für eine Antwort, wenn Sie es besser machen können.

Sehen wir uns nun an, wie wir den RÄUMLICHEN INDEX verwenden können mit ST_Within() .

Sie müssen ein Polygon definieren die den nächstgelegenen Standort enthält. Eine einfache Möglichkeit ist die Verwendung von ST_Buffer() was ein Polygon mit 32 Punkten erzeugt und fast ein Kreis ist*.

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

Das Ergebnis ist das gleiche. Die Ausführungszeit beträgt ~ 0,000 Sekunden (das ist, was mein Client (HeidiSQL ) sagt).

* Beachten Sie, dass @radius wird in Grad notiert und daher ähnelt das Polygon eher einer Ellipse als einem Kreis. Aber bei meinen Tests kam ich immer zum gleichen Ergebnis wie bei der einfachen und langsamen Lösung. Ich würde jedoch weitere Randfälle untersuchen, bevor ich es in meinem Produktionscode verwende.

Jetzt müssen Sie den optimalen Radius für Ihre Anwendung/Daten finden. Wenn es zu klein ist, erhalten Sie möglicherweise keine Ergebnisse oder verpassen den nächsten Punkt. Wenn es zu groß ist, müssen Sie möglicherweise zu viele Zeilen verarbeiten.

Hier einige Zahlen für den gegebenen Testfall:

  • @radius =0.001:Kein Ergebnis
  • @radius =0.01:genau ein Ort (irgendwie glücklich) - Ausführungszeit ~ 0.000 Sek.
  • @radius =0,1:55 Orte - Ausführungszeit ~ 0,000 Sek.
  • @radius =1.0:2183 Standorte - Ausführungszeit ~ 0,030 Sek.