sql >> Database >  >> RDS >> Mysql

Welke aanpak is sneller om alle POI's uit MySQL/MariaDB te halen met PHP/Laravel

Welke formule je voor de afstand gebruikt maakt niet zoveel uit. Veel belangrijker is het aantal rijen dat je moet lezen, verwerken en sorteren. In het beste geval kunt u een index gebruiken voor een voorwaarde in de WHERE-component om het aantal verwerkte rijen te beperken. U kunt proberen uw locaties te categoriseren - maar het hangt af van de aard van uw gegevens of dat goed gaat werken. U moet ook weten welke "categorie" u moet gebruiken. Een meer algemene oplossing zou zijn om een ​​SPATIAL INDEX . te gebruiken en de ST_Within() functie.

Laten we nu wat tests uitvoeren.

In mijn DB (MySQL 5.7.18) heb ik de volgende tabel:

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

De gegevens zijn afkomstig van Free World Cities Database en bevat 3173958 (3,1 miljoen) rijen.

Merk op dat geoPoint is redundant en gelijk aan POINT(longitude, latitude) .

Bedenk dat de gebruiker zich ergens in Londen bevindt

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

en je wilt de dichtstbijzijnde locatie vinden van de cities tafel.

Een "triviale" vraag zou zijn

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

Het resultaat is

988204 Blackwall 1085.8212159861014

Uitvoeringstijd:~ 4.970 sec

Als je de minder complexe functie ST_Distance() . gebruikt , krijg je hetzelfde resultaat met een uitvoeringstijd van ~ 4.580 sec - wat niet zo veel verschil is.

Merk op dat u geen geopunt in de tabel hoeft op te slaan. U kunt net zo goed gebruik maken van (point(c.longitude, c.latitude) in plaats van c.geoPoint . Tot mijn verbazing is het zelfs nog sneller (~3,6 sec voor ST_Distance en ~4,0 sec voor ST_Distance_Sphere ). Het zou nog sneller zijn als ik geen geoPoint . had kolom helemaal niet. Maar dat maakt nog steeds niet zoveel uit, omdat je niet wilt dat de gebruiker wacht, dus log in op een reactie, als je het beter kunt doen.

Laten we nu eens kijken hoe we de SPATIAL INDEX . kunnen gebruiken met ST_Within() .

U moet een veelhoek definiëren die de dichtstbijzijnde locatie zal bevatten. Een eenvoudige manier is om ST_Buffer() . te gebruiken die een polygoon met 32 ​​punten genereert en bijna een cirkel* is.

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

Het resultaat is hetzelfde. De uitvoeringstijd is ~ 0.000 sec (dat is wat mijn cliënt (HeidiSQL ) zegt).

* Merk op dat de @radius wordt genoteerd in graden en dus zal de veelhoek meer op een ellips dan op een cirkel lijken. Maar in mijn tests kreeg ik altijd hetzelfde resultaat als met de eenvoudige en langzame oplossing. Ik zou echter meer randgevallen onderzoeken voordat ik het in mijn productiecode gebruik.

Nu moet u de optimale straal voor uw toepassing/gegevens vinden. Als het te klein is, krijgt u mogelijk geen resultaten of mist u het dichtstbijzijnde punt. Als het te groot is, moet u mogelijk te veel rijen verwerken.

Hier enkele cijfers voor de gegeven testcase:

  • @radius =0.001:Geen resultaat
  • @radius =0.01:precies één locatie (soort van geluk) - Uitvoeringstijd ~ 0.000 sec
  • @radius =0.1:55 locaties - Uitvoeringstijd ~ 0.000 sec
  • @radius =1,0:2183 locaties - Uitvoeringstijd ~ 0,030 sec


  1. Dubbele unicode-invoerfout op de unieke kolom - mysql

  2. Kon JPA EntityManager niet openen voor transactie; geneste uitzondering is javax.persistence.PersistenceException

  3. MySQL in Docker retourneert De door de server gevraagde authenticatiemethode onbekend bij de client

  4. Testen van Android SQLite-database-eenheden