Mauvaises performances lors de l'utilisation d'index spatiaux dans MySQL

13

Re-poster une question posée sur Stack Overflow quand il a été suggéré que ce serait un meilleur forum.

J'essaie une petite expérience pour pousser un ensemble de données qui n'est pas géospatial mais qui lui convient assez bien et je trouve les résultats quelque peu troublants. L'ensemble de données est constitué de données génomiques, par exemple le génome humain où nous avons une région d'ADN où des éléments comme les gènes occupent des coordonnées de début et de fin spécifiques (notre axe X). Nous avons plusieurs régions d'ADN (chromosomes) qui occupent l'axe Y. Le but est de ramener tous les éléments qui coupent deux coordonnées X le long d'une seule coordonnée Y, par exemple LineString (START 1, END 2).

La théorie semblait solide, alors je l'ai poussée dans un projet de génome basé sur MySQL et j'ai trouvé une structure de table comme:

CREATE TABLE `spatial_feature` (
  `spatial_feature_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `external_id` int(10) unsigned NOT NULL,
  `external_type` int(3) unsigned NOT NULL,
  `location` geometry NOT NULL,
  PRIMARY KEY (`spatial_feature_id`),
  SPATIAL KEY `sf_location_idx` (`location`)
) ENGINE=MyISAM;

external_idreprésente l'identifiant de l'entité que nous avons encodé dans ce tableau et en external_typecode la source. Tout avait l'air bien et j'ai inséré des données préliminaires (30 000 lignes) qui semblaient bien fonctionner. Lorsque cela a dépassé la barre des 3 millions de lignes, MySQL a refusé d'utiliser l'index spatial et a été plus lent lorsqu'il a été contraint de l'utiliser (40 secondes contre 5 secondes en utilisant une analyse complète de la table). Lorsque davantage de données ont été ajoutées, l'index a commencé à être utilisé, mais la dégradation des performances a persisté. Le fait de forcer l'index a ramené la requête à 8 secondes. La requête que j'utilise ressemble à:

select count(*)
from spatial_feature
where MBRIntersects(GeomFromText('LineString(7420023 1, 7420023 1)'), location);

Les données qui y entrent sont très denses le long des dimensions Y (pensez-y comme si vous aviez enregistré la position de chaque bâtiment, cabine téléphonique, boîte postale et pigeon sur une très longue route). J'ai fait des tests sur le comportement des R-Index avec ces données en Java et d'autres sur le terrain les ont appliquées avec succès aux formats de fichiers plats. Cependant, personne ne les a appliqués aux bases de données AFAIK qui est le but de ce test.

Quelqu'un a-t-il vu un comportement similaire lors de l'ajout de grandes quantités de données à un modèle spatial qui n'est pas très disparate le long d'un axe particulier? Le problème persiste si j'inverse l'utilisation des coordonnées. J'exécute la configuration suivante si c'est une cause

  • MacOS 10.6.6
  • MySQL 5.1.46
andeyatz
la source

Réponses:

5

MySQL, comme PostGIS, stocke ses données d'index spatial dans une structure R-tree afin qu'il puisse trouver des trucs rapidement. Un arbre R, comme un arbre B, est organisé de telle manière qu'il est optimisé pour ne récupérer qu'une petite fraction du total des données dans le tableau. Il est en fait plus rapide d'ignorer l'index pour les requêtes qui doivent lire une grande partie du tableau pour renvoyer des données ou effectuer une énorme jointure, un cas classique qui donne lieu à de nombreux forums de bases de données [affiches] se plaignant d'une requête qui renvoie la moitié de leur table "n'utilisant pas le nouvel index qu'ils viennent de créer."

De http://rickonrails.wordpress.com/2009/03/30/big-ole-mysql-spatial-table-optimization-tricks/

Si vous pouvez mettre toutes vos données de table en mémoire, vos performances sont bonnes. Si / quand vous devez commencer à faire des lectures sur disque, les performances deviennent rapidement mauvaises. Faites-vous des modèles d'utilisation de la mémoire de votre instance mysql pour les deux cas: 30k lignes vs 3000k lignes?

tmarthal
la source
Je pense que cela pourrait être plus proche de la question. TBH c'est le R-index que je veux; les autres calculs spatiaux sont un bon bonus car cela devrait être fait dans la couche API sous l'ancien système. J'ai essayé un peu de réglage mais l'augmentation des tampons de clés n'a pas aidé (d'autres tampons n'aideront pas ici comme le tampon de table car c'est une requête de 1 table sur mon serveur personnel). Ce qui est étrange, c'est que MySQL enfonce ma machine dans le sol lorsque les requêtes sont exécutées (100% pendant l'exécution des requêtes). Cela dit, il fait un scan complet de la table, alors ce n'est peut-être pas si étrange
andeyatz
5

Quelque chose ne va pas avec votre installation mysql ou les paramètres .ini. Je viens de tester un index géospatial sur mon ancien mac (10.6.8 / MySQL 5.2). Cette configuration est similaire à la vôtre et j'ai testé le grand vidage de géodonnées ( 9 millions d'enregistrements ). J'ai fait cette requête:

SET @radius = 30;
SET @center = GeomFromText('POINT(51.51359 7.465425)');
SET @r = @radius/69.1;
SET @bbox = CONCAT('POLYGON((', 
  X(@center) - @r, ' ', Y(@center) - @r, ',', 
  X(@center) + @r, ' ', Y(@center) - @r, ',', 
  X(@center) + @r, ' ', Y(@center) + @r, ',', 
  X(@center) - @r, ' ', Y(@center) + @r, ',', 
  X(@center) - @r, ' ', Y(@center) - @r, '))' 
);

SELECT geonameid, SQRT(POW( ABS( X(point) - X(@center)), 2) + POW( ABS(Y(point) - Y(@center)), 2 ))*69.1 
AS distance
FROM TABLENAME AS root
WHERE Intersects( point, GeomFromText(@bbox) ) 
AND SQRT(POW( ABS( X(point) - X(@center)), 2) + POW( ABS(Y(point) - Y(@center)), 2 )) < @r 
ORDER BY distance; 

Cela n'a pris que 0,0336 sec.

J'utilise la requête ci-dessus, par exemple pour les comparaisons entre les tables où la table d'où viennent les valeurs lat / lng pour @center a un INDEX simple de city_latitude / city_longitude et le 9-12 Mio. table de geonames.org a un index géospatial.

Et je voulais juste ajouter que lorsque quelqu'un insère les données volumineuses dans une table, il pourrait être plus performant d'ajouter l'index après INSERT. Sinon, cela prendra plus de temps pour chaque ligne que vous ajoutez ... [mais ce n'est pas important]

sebilasse
la source
Wow c'est vraiment bien. Maintenant, je ne sais pas ce que je faisais mal dans mes propres tests. Une chose qui pourrait être à l'origine d'un problème est la nature de mes ensembles de données par rapport aux ensembles de données géospatiales plus traditionnels. Cela dit, je ne fais que deviner et je n'ai aucune base pour cela. C'est génial de voir que vous n'avez pas besoin de forcer l'index en mémoire pour obtenir la vitesse.
andeyatz
La clause WHERE avec le rayon pourrait filtrer une bonne partie de la table à l'aide d'un index.
tmarthal
2

Avez-vous pensé à le diviser en deux colonnes 1D au lieu d'une seule colonne 2D?

L'optimiseur pourrait étouffer toutes les données similaires et avoir deux colonnes avec une plus grande variété pourrait aider.

Vous pouvez également vérifier l'ordre dans lequel les articles sont vérifiés. J'ai eu un problème dans Oracle Spatial où je faisais une recherche sur le nom de famille et un filtre IN_REGION. Oracle a décidé que le moyen le plus rapide était d'utiliser le nom de famille, puis de vérifier la région. Permettez-moi de vous dire que faire une vérification dans la région de tous les Robinson de Cleveland est lent . Je me souviens que je devais passer un argument spécifique à Oracle pour le forcer à utiliser l'index spatial en premier.

Mark Robinson
la source
Malheureusement, une dimension est beaucoup moins peuplée qu'une autre dimension. Pour mettre cela en contexte, le génome humain possède 24 chromosomes uniques (22 paires et les deux chromosomes sexuels) ainsi que des sacs de données qui ont été assemblés à différents niveaux. Ce qui signifie que si vous mappez des éléments au cas d'utilisation de base, il ne s'agit que de 24 identifiants uniques dans une dimension. L'espoir initial était que l'index R-tree aurait pu effectuer non seulement des vérifications de plage chevauchantes plus performantes, mais aussi différencier ces régions dans une seule requête.
andeyatz