La combinaison clé / valeur Hstore et requête spatiale est trop lente pour gérer des extraits OSM plus volumineux

13

J'essaie de calculer des statistiques pour les données OSM en utilisant PostgreSQL 9.3.5 et PostGIS 2.1.4. J'ai commencé avec un petit extrait de Bavière que j'ai téléchargé sur Geofabrik. Le schéma db est le schéma API 0.6 normal, les données ont été importées via l'approche de vidage dans Postgres (en utilisant les scripts pgsnapshot_schema_0.6 * .sql fournis avec l'osmose). ANALYSER LE VIDE a également été réalisé.

La seule chose sur mesure que j'utilise est une table de polygones qui contient des multipolygones pour toutes les relations de limites administratives. La géométrie du polygone n'a été simplifiée en aucune façon.

Ce que j'essaie maintenant d'atteindre, c'est de compter tous les nœuds qui se trouvent à l'intérieur des limites admin = 6 de la Bavière. Voici ma requête SQL:

SELECT relpoly.id, count(node) 
FROM bavaria.relpolygons relpoly, bavaria.nodes node
WHERE relpoly.tags @> '"boundary"=>"administrative","admin_level"=>"6"'::hstore 
AND ST_Intersects(relpoly.geom, node.geom)
GROUP BY relpoly.id;

L'exécution de cette requête est terrible car Postgres effectue une jointure de boucle imbriquée et analyse tous les nœuds pour chaque limite admin = 6. Pour info, la bavière est divisée en 98 admin = 6 polygones et il y a environ 30 millions de nœuds dans l'extrait de bavière.

Est-il possible d'éviter cette exécution de requête sous-optimale et de dire à Postgres de ne scanner tous les nœuds qu'une seule fois (par exemple, en incrémentant un compteur pour le polygone correspondant dans le jeu de résultats ou en utilisant des astuces)?

Éditer:

1) un indice spatial existe sur les nœuds bavarois:

CREATE INDEX idx_nodes_geom ON bavaria.nodes USING gist (geom);

2) le plan de requête ressemble à ceci:

HashAggregate  (cost=284908.49..284908.75 rows=26 width=103)
  ->  Nested Loop  (cost=111.27..283900.80 rows=201537 width=103)
        ->  Bitmap Heap Scan on relpolygons relpoly  (cost=4.48..102.29 rows=26 width=5886)
              Recheck Cond: (tags @> '"boundary"=>"administrative", "admin_level"=>"6"'::hstore)
              ->  Bitmap Index Scan on relpolygons_geom_tags  (cost=0.00..4.47 rows=26 width=0)
                    Index Cond: (tags @> '"boundary"=>"administrative", "admin_level"=>"6"'::hstore)
        ->  Bitmap Heap Scan on nodes node  (cost=106.79..10905.50 rows=983 width=127)
              Recheck Cond: (relpoly.geom && geom)
              Filter: _st_intersects(relpoly.geom, geom)
              ->  Bitmap Index Scan on idx_nodes_geom  (cost=0.00..106.55 rows=2950 width=0)
                    Index Cond: (relpoly.geom && geom)

3)

J'ai créé les deux index suivants, mais le plan de requête (et le runtime) n'ont pas changé

CREATE INDEX relpolygons_tags_boundary on bavaria.relpolygons( (tags->'boundary') );
CREATE INDEX relpolygons_tags_admin on bavaria.relpolygons( (tags->'admin_level') );
ANALYZE bavaria.relpolygons;
Alf Kortig
la source
1
Avez-vous un index spatial dans bavaria.nodes?
user30184
oui, j'ai édité la question et fourni des informations sur l'index sur les nœuds et le plan de requête
Alf Kortig
3
Deux options. 1 - ajoutez un index pour les balises hstore. 2 - extrayez les balises que vous utilisez pour votre requête ( boundaryet admin_level) dans des colonnes supplémentaires sur la table et utilisez-les directement.
BradHards
Voir edit (3): deux index ont été ajoutés, mais aucun changement n'a été apporté au plan de requête ni à l'exécution.
Alf Kortig
Après quelques tests, je ne sais plus si j'ai créé les bons index dans (3). Jusqu'à présent, j'ai réussi à créer un index pour les -> et? opérateurs hstore. Cependant, j'utilise @> dans ma requête
Alf Kortig

Réponses:

5

La meilleure façon d'indexer les balises hstore est d'utiliser les index GIN ou GIST qui, à partir des documents , prennent en charge les @>,?,? & Et? | opérateurs , qui est, les recherches sur les clés et les paires clé / valeur. Votre approche de l'utilisation d'une fonction pour extraire les balises d'un index B-tree est raisonnable, mais parce que vous vérifiez également des paires clé / valeur spécifiques, l'analyseur a choisi une analyse complète de la table.

Je n'ai pas accès à bavaria.relpolygons, mais sur la base d'une requête similaire pour OSM UK sur les limites de vitesse et les balises d'autoroute, je l'obtiens pour mon explication sur la requête suivante:

SELECT count(*) 
 FROM ways 
WHERE tags @> 'highway=>motorway'::hstore 
 AND tags @> 'maxspeed=>"50 mph"'::hstore;


Aggregate  (cost=48.66..48.67 rows=1 width=0)
    ->  Index Scan using ix_ways_tags_gist on ways  (cost=0.42..48.64 rows=11 width=0)
     Index Cond: ((tags @> '"highway"=>"motorway"'::hstore) AND (tags @> '"maxspeed"=>"50 mph"'::hstore))

qui montre un balayage d'index direct (en utilisant l'index gist), ce qui est encourageant pour une table avec 10 millions de lignes. L'index a été créé avec le simple:

CREATE INDEX ix_ways_tags_gist ON ways USING gist (tags);

Bien que je ne puisse pas vous vérifier la condition spatiale, je suppose que c'est moins sélectif que

OERE relpoly.tags @> '"border" => "administrative", "admin_level" => "6"' :: hstore.

et ne serait donc utilisé que pour une condition de revérification.

Il y a aussi cette excellente réponse SO sur la différence entre les indices GIN et GIST . La conclusion générale est que les index GIN, bien que plus grands et plus lents à construire, sont beaucoup plus rapides en cas de problèmes de récupération de texte.

Désolé de répondre si tard, mais j'ai récemment effectué un travail similaire sur OSM et hstore, et j'ai découvert que non seulement j'ai une fois posé cette question, mais que je pouvais maintenant y répondre: D.

John Powell
la source