#1 Tue 18 May 2010 18:03
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Optimisation requête POSTGIS pour données volumineuses
Bonjour à tous,
Voici notre problématique : nous devons extraire depuis une base POSTGIS des données vecteurs que nous traduisons ensuite en vecteur (shape pour les tests) avec OGR.
Le problème vient de la lenteur des requêtes dans POSTGIS.
A titre d'exemple, la conversion du bati de la bd topo (+ de 1 millions d'objets), depuis une emprise régionale vers un shape d'emprise départementale (350 milles objets) met environ 6 h.
voici la requête que nous avions déjà optimisée après plusieurs tests :
Code:
ogr2ogr -f "ESRI Shapefile" -select "liste des champs" -where "bdtopo_bati_indifferencie_2010_l93.the_geom && (select the_geom from extent_decoupe where gid=1) and ST_Intersects (bdtopo_bati_indifferencie_2010_l93.the_geom,(select the_geom from extent_decoupe where gid=1))" "/DATA/bdtopo_bati_indifferencie.shp" PG:"dbname='*****' host='****' port='****' user='****' password='****' tables=bdtopo_bati_indifferencie" -nlt MULTIPOLYGON25D
Voici l'optimisation déjà réalisée
Concernant l'optimisation de POSTGRES :
- modification des fichiers de configuration pour attribuer plus de mémoire aux traitements
- utilisation d'index spatiaux
- "nettoyage" avec la fonction SQL VACCUM
- création de CLUSTER
Concernant l'optimisation de la requête de POSTGIS :
- dans la clause WHERE la première requête filtre sur le rectangle englobant
- la deuxième requête realise l'intersection plus finement avec l'opérateur ST_Intersects (lui même utilisant les index)
- l'utilisation de l'opérateur ST_Relate a également été testé pour remplacé St_Intersects mais sans grand changement
Concernant l'optimisation d'OGR :
- les paramètres combinés de "-select" et de "-where" s'avèrent plus rapide que d'utiliser uniquement le "-sql"
Dernier détail : tout est installé sur serveur 2 * 2 coeur + 4Go de ram
Malgré tout cela, les requêtes (postgis semble être en cause directement) s'avèrent très longues.
Nous sommes preneurs d'un retour d'exprérience dans la manipulation de gros volume de données et de tout avis éclairé dans l'optimisation de postgis, d'ogr, du serveur,...
Merci
Dernière modification par Nicolas Granier (Tue 18 May 2010 18:06)
Hors ligne
#2 Tue 18 May 2010 18:58
Re: Optimisation requête POSTGIS pour données volumineuses
Quelques remarques :
- le st_instersects est redondant avec le premier test de l'opérateur &&. Ce dernier est fait automatiquement
Et quelques questions :
- Quelle est la largeur des données ? Y a t-il beaucoup de champs ?
- quel est la requete exacte envoyée à postgresql du coup ?
- Quel est le nombre d'enregistrements qui sont censés etre renvoyés ?
- Quels sont les disques de la machine ?
- le fichier shape qui est généré l'est il bien sur la machine locale ?
Et pour la suite il faudrait faire un explain de la requete, pour voir si :
- les indexes sont bien utilisés
- les estimations de nombre d'enregistrements renvoyés sont bien corrects
Dans les tests à faire aussi il faudrait vérifier que c'est bien la requete postgresql qui prend du temps en faisant la requete avec un create table plutot que ogr2ogr.
Si le problème à la fin est simplement qu'il faut extraire un grand nombre d'enregistrements, alors la solution c'est de la RAM, de la RAM, un SAN avec le plus de spindle possible.
Hors ligne
#3 Wed 19 May 2010 09:06
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 611
Re: Optimisation requête POSTGIS pour données volumineuses
Perso, je trouve que 6h c'est vraiment long !
A chaud, Trois autres pistes :
1. créer une table intermédiaire qui de la requete "select the_geom from extent_decoupe where gid=1" et créer un index spatial sur cette nouvelle table. Puis faire un Vacuum analyse sur cette nouvelle table.
Un explain analyse devrait permettre de savoir si les index spatiaux des 2 tables sont bien utilisé.
2. Améliorer la performance de postgresql en touchant au fichier postgresql.conf. Car par défaut, il me semble que postgresql n'utilise pas bien la RAM. Cependant, je crois que vous avez déjà essayé d'y remedier
3. selon l'utilisation future, faire une jointure sur les centroides des polygones bati. Et ne pas oublier de faire un index spatial sur les centroides.
Pascal PLUVINET
Hors ligne
#4 Wed 19 May 2010 09:35
- michCarto
- Participant actif
- Date d'inscription: 8 Sep 2009
- Messages: 106
Re: Optimisation requête POSTGIS pour données volumineuses
Oui c'est vrai Pascal à raison , il vaut mieux toujours décomposer dans des tables temporaires le travail ....
vous pouvez aussi mettre ce message sur www.postgresql.org coté postgres/postgis il vous répondront rapidement
Michel
Hors ligne
#5 Wed 19 May 2010 09:43
- michCarto
- Participant actif
- Date d'inscription: 8 Sep 2009
- Messages: 106
Re: Optimisation requête POSTGIS pour données volumineuses
www.postgresql.fr pardon !!!
Hors ligne
#6 Wed 19 May 2010 10:25
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Re: Optimisation requête POSTGIS pour données volumineuses
Bonjour, Merci de ces réponses
Voici les compléments aux questions :
- Quelle est la largeur des données ? Y a t-il beaucoup de champs ? 7 champs uniquement
- quel est la requete exacte envoyée à postgresql du coup ? la requête est envoyée en morceau par OGR.
Elle correspond à
Code:
SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export WHERE the_geom && ST_GeomFromText('POLYGON((799607 6232734, 799607 6315411, 926824 6315411,926824 6232734,799607 6232734))',310024140) AND ST_RELATE (public.bdtopo_bati_indifferencie_2008_l93_export.the_geom,(SELECT the_geom FROM public.extent_decoupe_export WHERE gid = 1),'2********');
pour l'exemple avec le ST_relate à la place du ST_Intersects
- Quel est le nombre d'enregistrements qui sont censés etre renvoyés ? environ 320 000
- Quels sont les disques de la machine ? 2 disques SAS de 73 Go, 10 000 tour/min en raid1
- le fichier shape qui est généré l'est il bien sur la machine locale ? non sur un disque distant partagé entre plusieurs serveurs
En réponse à P. Pluvinet, la géométrie extraite ici par la requete "select the_geom from extent_decoupe where gid=1" est issue d'une table temporaire (il s'agit de l'union de toutes les communes (de BD TOPO) sur un département.
Si je spécifie uniquement extent_decoupe.the_geom sans clause where aucun résultat n'est renvoyé.
Le centroïde du bâti n'est pas non plus optimal car ce dernier bien que situé dans le polygone bati peut être hors de l'intersection alors que le polygone du bâti lui peut s'intersecter. Certes il y a peu de bâti à cheval d'une limite de département mais il y en a quelques uns (du moins géographiquement).
Nous allons persévérer avec les requêtes EXPLAIN
A titre comparatif, Arcgis pour ne citer que lui fait cette intersection en 2 minutes 30 à partir de fichiers shape indexés... :0
Dernière modification par Nicolas Granier (Wed 19 May 2010 10:26)
Hors ligne
#7 Wed 19 May 2010 10:42
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1549
Re: Optimisation requête POSTGIS pour données volumineuses
Bonjour, Merci de ces réponses
Voici les compléments aux questions :
- Quelle est la largeur des données ? Y a t-il beaucoup de champs ? 7 champs uniquement
- quel est la requete exacte envoyée à postgresql du coup ? la requête est envoyée en morceau par OGR.
Elle correspond àCode:
SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export WHERE the_geom && ST_GeomFromText('POLYGON((799607 6232734, 799607 6315411, 926824 6315411,926824 6232734,799607 6232734))',310024140) AND ST_RELATE (public.bdtopo_bati_indifferencie_2008_l93_export.the_geom,(SELECT the_geom FROM public.extent_decoupe_export WHERE gid = 1),'2********');pour l'exemple avec le ST_relate à la place du ST_Intersects
- Quel est le nombre d'enregistrements qui sont censés etre renvoyés ? environ 320 000
- Quels sont les disques de la machine ? 2 disques SAS de 73 Go, 10 000 tour/min en raid1
- le fichier shape qui est généré l'est il bien sur la machine locale ? non sur un disque distant partagé entre plusieurs serveurs
En réponse à P. Pluvinet, la géométrie extraite ici par la requete "select the_geom from extent_decoupe where gid=1" est issue d'une table temporaire (il s'agit de l'union de toutes les communes (de BD TOPO) sur un département.
Si je spécifie uniquement extent_decoupe.the_geom sans clause where aucun résultat n'est renvoyé.
Le centroïde du bâti n'est pas non plus optimal car ce dernier bien que situé dans le polygone bati peut être hors de l'intersection alors que le polygone du bâti lui peut s'intersecter. Certes il y a peu de bâti à cheval d'une limite de département mais il y en a quelques uns (du moins géographiquement).
Nous allons persévérer avec les requêtes EXPLAIN
A titre comparatif, Arcgis pour ne citer que lui fait cette intersection en 2 minutes 30 à partir de fichiers shape indexés... :0
Bonjour,
Que renvoie EXPLAIN ANALYSE ?
Nico
Hors ligne
#8 Wed 19 May 2010 11:05
Re: Optimisation requête POSTGIS pour données volumineuses
Perso, je trouve que 6h c'est vraiment long !
A chaud, Trois autres pistes :
1. créer une table intermédiaire qui de la requete "select the_geom from extent_decoupe where gid=1" et créer un index spatial sur cette nouvelle table. Puis faire un Vacuum analyse sur cette nouvelle table.
Un explain analyse devrait permettre de savoir si les index spatiaux des 2 tables sont bien utilisé.
Je présumais que si on a une clause «where gid = 1» on ne retirera qu'un seul enregistrement. L'index spatial et le vacuum analyze sont donc inutiles dans ce cas. Mais on peut toujours essayer de faire cette table temporaire, j'ai vu des cas ressemblant ou ce genre de sous requete posait des problèmes.
Sans un EXPLAIN de la requete on va pas pouvoir déterminer réellement le souci en tout cas.
Hors ligne
#9 Wed 19 May 2010 11:24
- j.rolland
- Participant occasionnel
- Lieu: MONTPELLIER
- Date d'inscription: 12 Sep 2005
- Messages: 40
Re: Optimisation requête POSTGIS pour données volumineuses
Bonjour,
Vous pouvez modifier votre ligne de commande pour ogr2ogr en mettant PGB a la place de PG, ce qui permet l'utilisation d'un curseur binaire et donc améliorer le temps de traitement.
ogr2ogr -f "ESRI Shapefile" ... PGB:"dbname=....
cordialement,
Jérôme
Jérôme ROLLAND
FIT-ESIC
Hors ligne
#10 Wed 19 May 2010 13:34
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 611
Re: Optimisation requête POSTGIS pour données volumineuses
Je présumais que si on a une clause «where gid = 1» on ne retirera qu'un seul enregistrement. L'index spatial et le vacuum analyze sont donc inutiles dans ce cas.
Peut-on en être sûr? J'avoue que les index spatiaux c'est un peu une boite noire pour moi. J'ai tendance à en créer sur toutes mes colonnes geometry car j'ai de la place sur mon disque dur. Quelqu'un aurait un lien ou une doc qui explique réellement ce qu'est un index et particulièrement un index GIST sous Postgis? Merci d'avance.
Pour Nicolas Granier, je peux dire qu'avec un ordi similaire, je fais des requêtes bien plus complexes qui prennent beaucoup moins de temps. (ce genre de requête ne devrait pas prendre plus d'1 quart d'heure, voire 1/2 heure!) Par contre, je décompose souvent mes requêtes: c'est souvent plus lisibles et ca va souvent plus vite.
Pour l'export j'utilise pgsql2shp, c'est relativement rapide. Je connais moins ogr2ogr pour les import/export avec postgis. Je l'utilise plutôt pour des conversions MIF/MID en shape.
Dernière modification par ppluvinet (Wed 19 May 2010 13:35)
Pascal PLUVINET
Hors ligne
#11 Wed 19 May 2010 13:55
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Re: Optimisation requête POSTGIS pour données volumineuses
Le rséultat de l'EXPLAIN ANALYSE est à venir, il tourne depuis presque 2 heures maintenant...
Pour Nicolas Granier, je peux dire qu'avec un ordi similaire, je fais des requêtes bien plus complexes qui prennent beaucoup moins de temps. (ce genre de requête ne devrait pas prendre plus d'1 quart d'heure, voire 1/2 heure!) Par contre, je décompose souvent mes requêtes: c'est souvent plus lisibles et ca va souvent plus vite
Je suis entièrement d'accord qu'il ne faudrait pas plus d'1/4 d'heure à cette requête. C'est pour cela que j'ai fait un post.
Par contre ma requête est déjà décomposée, j'ai créé un couche temporaire pour l'intersection, je ne vois pas quoi faire de plus.
Bien noté pour la connexion à postgres depuis OGR avec PGB. Je vai tester aussi, mais la lenteur de la requête vient bien de POSTGIS.
Quelque chose encore ne me semble pas clair.
J'ai décomposé ma requête en créant une couche temporaire "extent_decoupe". Cette couche a été crée suite à une union de la géométrie de toutes les communes d'un même département. Elle ne possède qu'un seul objet. Certes l'objet contient beaucoup de point c'est le contour d'un département de BD TOPO.
Lorsque j'utilise l'opérateur ST_Relate ou ST_Intersects, je suis obligé de passer une clause WHERE gid= 1 pour avoir un résultat.
Un simple ST_Relate (maCouche.the_geom, monExtentDeDecoupe.the_geom) ne me donne aucun résultat.
Est-ce normal ? Y a t-il une autre façon de procédé ?
Je poste le resultat de la fonction EXPLAIN dès qu'elle s'achève (en espérant qu'elle s'achève)
Merci de vos retours
Dernière modification par Nicolas Granier (Wed 19 May 2010 13:58)
Hors ligne
#12 Wed 19 May 2010 14:08
Re: Optimisation requête POSTGIS pour données volumineuses
Je présumais que si on a une clause «where gid = 1» on ne retirera qu'un seul enregistrement. L'index spatial et le vacuum analyze sont donc inutiles dans ce cas.
Peut-on en être sûr? J'avoue que les index spatiaux c'est un peu une boite noire pour moi. J'ai tendance à en créer sur toutes mes colonnes geometry car j'ai de la place sur mon disque dur. Quelqu'un aurait un lien ou une doc qui explique réellement ce qu'est un index et particulièrement un index GIST sous Postgis? Merci d'avance.
Un index d'une base de données, spatial ou pas, peut etre comparé à l'index d'une encyclopédie par exemple. Cela permet d'aller plus rapidement à l'article concerné. Mais si on a qu'un seul article dans l'encyclopédie, ou un très petit nombre, il sera plus rapide de parcourir tous les articles, que de lire l'index, puis aller à la page de l'article recherché.
Suivant les estimations de nombres d'enregistrement, qui sont faites à partir des statistiques calculées sur les données, les requetes seront effectuées en utilisant ou pas les indexes. Par exemple, faites une requete pouvant utiliser un index sur une table de 10 enregistrements, vous verrez que le planner postgresql va faire un scan séquentiel de toute la table plutot qu'utiliser l'index. Ça peut etre le cas sur des tables bien plus grosse suivant la selectivité de la condition et la distribution des valeurs dans la table.
Pour les indexes géométriques PostGIS, ils sont basés sur GIST, qui est une structure d'indexes généralistes de PostgreSQL. L'implémentation des indexes géométriques est un RTREE.
Pour plus d'info sur les RTREE et leur fonctionnement, on peut commencer par là :
http://en.wikipedia.org/wiki/R-tree
La page traduite en français n'existe pas (encore), mais les dessins donnent déjà une bonne indication du fonctionnement.
Pour les indexes au sens plus général, on peut commencer par ici :
http://fr.wikipedia.org/wiki/Index_%28b … C3%A9es%29
et
http://fr.wikipedia.org/wiki/Arbre_B
Hope this helps, mais on s'éloigne du sujet initial, qu'on ne résoudra pas à l'aveuglette sans plus d'info cependant.
Hors ligne
#13 Wed 19 May 2010 14:14
Re: Optimisation requête POSTGIS pour données volumineuses
Le rséultat de l'EXPLAIN ANALYSE est à venir, il tourne depuis presque 2 heures maintenant...
[snip]
Je poste le resultat de la fonction EXPLAIN dès qu'elle s'achève (en espérant qu'elle s'achève)
En attendant que le EXPLAIN ANALYZE se termine, tu peux faire un EXPLAIN tout court qui donnera deja pas mal d'informations.
EXPLAIN ANALYZE fait une mesure du temps d'exécution réél, il faut donc que la requete s'exécute. EXPLAIN tout court donne juste le plan de requête sans l'executer, et ça suffit pour voir l'utilisation des indexes notamment.
Il faudrait aussi avoir une idée de la répartition de tes données : combien d'enregistrements dans la table au total, combien doivent être renvoyés ?
Hors ligne
#14 Wed 19 May 2010 14:31
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 611
Re: Optimisation requête POSTGIS pour données volumineuses
Je ne comprend pas le Gid = 1
Perso, j'aurais fait une requête qui ressemble à :
Code:
CREATE TABLE AS SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export a, departement b WHERE ST_INTERSECTS (a.the_geom,b.the_geom);
Pour ton explain analyse tu peux réduire la taille de ta requete . Par exemple :
Code:
explain analyse SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export a, departement b WHERE ST_INTERSECTS (a.the_geom,b.the_geom) LIMIT 10000;
Pascal PLUVINET
Hors ligne
#15 Wed 19 May 2010 14:34
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 611
Re: Optimisation requête POSTGIS pour données volumineuses
Merci vincentp.
La comparaison avec une encyclopédie permet une bonne vulgarisation!
Encore merci
Pascal PLUVINET
Hors ligne
#16 Wed 19 May 2010 14:37
- Nicolas Granier
- Participant assidu
- Date d'inscription: 19 Apr 2007
- Messages: 271
Re: Optimisation requête POSTGIS pour données volumineuses
Ok merci pour le tuyau
Voici le résultat d 'EXPLAIN
Code:
"Seq Scan on _ng_differentiel_bdtopo_bati_indifferencie_2008_l93_2 (cost=1.01..73880.16 rows=126028 width=67)" " Filter: ((the_geom && '0103000020CC977A12010000000500000000000000EE66284100000080A7C6574100000000EE662841000000C06417584100000000D0482C41000000C06417584100000000D0482C4100000080A7C6574100000000EE66284100000080A7C65741'::geometry) AND st_relate(the_geom, $0, '2********'::text))" " InitPlan" " -> Seq Scan on _ng_differentiel_extent_decoupe (cost=0.00..1.01 rows=1 width=32)" " Filter: (gid = 1)"
Nombre d'enregistrements dans la table bati au total => 1 118 610
Nombre d'enregistrements devant être retournés => environ 320 000
A ce que je crois comprendre les index ne sont pas utilisés, (ce qui serait normal sur la couche extend_decoupe) mais pas normal sur la couche de bâti.
Hors ligne
#17 Wed 19 May 2010 14:57
Re: Optimisation requête POSTGIS pour données volumineuses
Je ne comprend pas le Gid = 1
Perso, j'aurais fait une requête qui ressemble à :Code:
CREATE TABLE AS SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export a, departement b WHERE ST_INTERSECTS (a.the_geom,b.the_geom);Pour ton explain analyse tu peux réduire la taille de ta requete . Par exemple :
Code:
explain analyse SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export a, departement b WHERE ST_INTERSECTS (a.the_geom,b.the_geom) LIMIT 10000;
Sauf qu'ajouter le limit a de grande chance de changer le plan de requête ! On ne compare alors pas les memes choses.
Mais effectivement j'écrirais aussi la requete du haut, sauf si vraiment il y a des raisons fines pour utiliser le st_relate (vis à vis des bordures par exemple).
Hors ligne
#18 Wed 19 May 2010 15:16
Re: Optimisation requête POSTGIS pour données volumineuses
Ok merci pour le tuyau
Voici le résultat d 'EXPLAINCode:
"Seq Scan on _ng_differentiel_bdtopo_bati_indifferencie_2008_l93_2 (cost=1.01..73880.16 rows=126028 width=67)" " Filter: ((the_geom && '0103000020CC977A12010000000500000000000000EE66284100000080A7C6574100000000EE662841000000C06417584100000000D0482C41000000C06417584100000000D0482C4100000080A7C6574100000000EE66284100000080A7C65741'::geometry) AND st_relate(the_geom, $0, '2********'::text))" " InitPlan" " -> Seq Scan on _ng_differentiel_extent_decoupe (cost=0.00..1.01 rows=1 width=32)" " Filter: (gid = 1)"Nombre d'enregistrements dans la table bati au total => 1 118 610
Nombre d'enregistrements devant être retournés => environ 320 000
A ce que je crois comprendre les index ne sont pas utilisés, (ce qui serait normal sur la couche extend_decoupe) mais pas normal sur la couche de bâti.
Effectivement pas d'index utilisé. La sélectivité est faible ceci dit, on renvoie beaucoup d'enregistrements comparé à la taille totale. Dans l'absolu l'utilisation des indexes pourrait etre pénalisante dans le cas ou le filtre sur chaque ligne est faible, et que les données sont séquentielles. Mais dans le cas présent je pense qu'on gagnerait à les utiliser.
Les pistes à suivre :
- voir le résultat du explain analyze pour avoir les temps détaillés de la requete
- tester une requete avec st_intersects plutot que st_relate et vérifier par explain l'utilisation indexes
- relancer la requete avec set «enable_seqscan = off;» avant pour forcer l'utilisation des indexes (à ne faire QUE pour des tests de debug du planner !) et voir le temps d'exécution
- mettre la requete dans un create table pour etre bien sur que ce n'est pas l'envoi des données qui est long
Si ça n'avance rien, il peut y avoir :
- des soucis liés à la comparaison d'une grosse géométrie avec beaucoup d'autres
- des problèmes de config de postgresql
- des problèmes de disques pas assez rapide ou mal configurés
Hors ligne
#19 Wed 19 May 2010 15:20
- sylpingus
- Participant occasionnel
- Lieu: Aix en Provence
- Date d'inscription: 9 Jan 2006
- Messages: 34
Re: Optimisation requête POSTGIS pour données volumineuses
Bonjour à tous,
je me permets de prendre le fil de la discussion en cours de route. Je suis un collègue de travail à Nicolas Granier et je me penche également sur la problématique évoquée.
Pour répondre à la question de Pascal concernant le GDI=1, nous utilisons une table où on stocke l'emprise sur laquelle l'extraction des données doit être réalisée (sous forme d'agrégat de communes). Du coup, dans notre process, on connait le GID qui correspond à la demande d'extraction et c'est pour cela qu'on utilise la requête telle qu'elle et que nous ne souhaitons pas créer une table temporaire à chaque demande d'extraction.
Pour ce qui est des tables de données, nous les avons importées avec shp2pgsql sans l'option -I qui permet de créer les index spatiaux à la volée, que nous créons après avec une commande telle que "CREATE INDEX bdtopo_bati_indifferencie_2008_l93_export_gist ON bdtopo_bati_indifferencie_2008_l93_export using gist (the_geom gist_geometry_ops);".
Compte tenu du nombre d'objets de cette table (1200000), nous avons lancé un "VACUUM ANALYZE bdtopo_bati_indifferencie_2008_l93_export;" sur la table et pour optimiser encore la chose : "CLUSTER bdtopo_bati_indifferencie_2008_l93_export_gist ON bdtopo_bati_indifferencie_2008_l93_export ;"
La table qui stocke les emprises de découpage fait l'objet de VACUUM ANALYZE réguliers. De notre côté, il nous semble que toutes les conditions d'optimisation sont réunies pour accélérer la requête. De plus, j'ai "tuner" le postgresql.conf" pour qu'il corresponde à notre configuration matérielle, en suivant ces tutoriaux : http://softlibre.gloobe.org/doku.php/fo … ationpgsql et http://www.revsys.com/writings/postgres … mance.html. Même si nous n'avons que 2Go de RAM, le traitement ne devrait pas être aussi long...
Nous rencontrons les mêmes problèmes de lenteur (c'est un euphémisme) sur de l'extraction de données linéaires (avec 1800000 de données dans la table d'origine).
Une dernière précision, j'ai fait un test en essayant de forcer l'utilisation des index spatiaux en modifiant les informations suivantes de le fichier de conf : ENABLE_SEQSCAN=OFF et random_page_cost=1, mais rien ne change dans le EXPLAIN.
Enfin, j'ai une certitude : le problème ne vient pas d'ogr (que je connais mieux) mais bien du temps d'exécution de la requête... Je me souviens de précédents test, il y a longtemps, où ce type de process fonctionnait bien.
Une dernière précision : notre serveur PG est en version 8.3.3 et postgis en version 1.4.1. nous ne bénéficions des considérables améliorations sur le ST_RELATE de la version 1.5.0 de postgis mais quand même...
Encore merci pour toutes vos réponses et en espérant que nous arriverons à trouver une solution...
Sylvain
Hors ligne
#20 Wed 19 May 2010 15:31
- sylpingus
- Participant occasionnel
- Lieu: Aix en Provence
- Date d'inscription: 9 Jan 2006
- Messages: 34
Re: Optimisation requête POSTGIS pour données volumineuses
Re bonjour,
je rebondis par rapport à la réponse de Vincent (au passage, merci pour les retours détaillés) :
** on va essayer d'avoir le retour d'EXPLAIN ANALYZE (mais pas d'ici demain matin vu la lenteur....) et on le communiquera dès que possible.
** la requête avec ST_INTERSECTS n'est pas plus rapide. De plus, ST_INTERSECTS nous récupère également les polygones extérieurs aux polygones d'extraction et qui ont une limite commune (que nous ne voulons pas extraire), d'où le ST_RELATE ('2********').
** J'ai fait le test avec ENABLE_SEQSCAN=OFF et/ou random_page_cost=1 sans plus de succès.
** J'ai également fait le test de lancer la requête dans un create table hier soir. Au bout de 7h00 de traitement toujours pas de résultat...
** Pour les problèmes de config de postgres, même si c'était le cas, ça ne devrait pas être aussi long, à mon avis. De plus, teste les requêtes sur notre serveur et sur une install locale en parallèle, sans plus de succès...
** Concernant les soucis liés à la comparaison d'une grosse géométrie avec beaucoup d'autres, j'avais pensé à cela au début. En effet, le polygone qui doit servir de base à l'extraction est un agrégat de communes issues de la BD TOPO de l'IGN, ce qui veut dire le nombre de vertex de ce polygone est assez conséquent. Les polygônes du bati ont quant à eux beaucoup moins de points. Si c'est le cas, je ne vois pas comment résoudre le problème, malheureusement.
Encore merci pour tous vos retours. Je continue à creuser avec Nicolas. A ce rythme nous arriverons bientôt au pays du soleil levant .
Sylvain
Hors ligne
#21 Wed 19 May 2010 15:35
- j.rolland
- Participant occasionnel
- Lieu: MONTPELLIER
- Date d'inscription: 12 Sep 2005
- Messages: 40
Re: Optimisation requête POSTGIS pour données volumineuses
Quelques questions :
- Dans la table geometry_columns quelles sont les valeurs des champs cooord_dimension,srid et type , pour les deux tables qui interviennent dans votre requête ?
- Sous PgAdmin III si vous consultez l'index spatial de la table bati_indifferencie est ce que cela correspond a ci-dessous ? ou du moins a quelque chose de similaire.
CREATE INDEX bati_indifferencie_geom_idx
ON bati_indifferencie
USING gist
(the_geom);
Pour votre requête l'emploi de && doit effectivement activer l'emploi de l'index spatial sur la table bdtopo_bati_indifferencie_2008_l93_export, et doit donc être conservé.
Jérôme
Jérôme ROLLAND
FIT-ESIC
Hors ligne
#22 Wed 19 May 2010 15:51
- sylpingus
- Participant occasionnel
- Lieu: Aix en Provence
- Date d'inscription: 9 Jan 2006
- Messages: 34
Re: Optimisation requête POSTGIS pour données volumineuses
Dans la table geometry_columns nous avons les valeurs suivantes :
---- bdtopo_bati_indifferencie_2008_l93 :
*** coord_dimension : 4
*** srid : 310024140 (qui correspond au lambert 93 du registre IGNF)
*** type : multipolygon
---- extent_decoupe_export
*** coord_dimension : 2
*** srid : 310024140 (qui correspond au lambert 93 du registre IGNF)
*** type : multipolygon
Les dimensions ne sont pas les mêmes mais ça ne pose pas de problèmes, normalement.
L'extrait de PGAdminIII nous donne bien :
CREATE INDEX bdtopo_bati_indifferencie_2008_l93_the_geom_gist
ON bdtopo_bati_indifferencie_2008_l93
USING gist
(the_geom);
En effet, nous utilisons le && car la fonction ST_RELATE ne l'utilise pas de manière implicite (tout du moins dans la version 1.4.1).
Hors ligne
#23 Wed 19 May 2010 16:04
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 611
Re: Optimisation requête POSTGIS pour données volumineuses
Nouvelle piste : ne pas faire l'agrégat des communes du département et faire une requette du style :
Code:
CREATE TABLE bati_reg AS SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export a, communes b WHERE ST_INTERSECTS (a.the_geom,b.the_geom) and b.dept = 'numerodudep' ;
Pour un polygone bati , il est + rapide de savoir s'il intersecte une commune que de savoir s'il intersecte un département entier.
Dernière modification par ppluvinet (Wed 19 May 2010 16:08)
Pascal PLUVINET
Hors ligne
#24 Wed 19 May 2010 16:30
- j.rolland
- Participant occasionnel
- Lieu: MONTPELLIER
- Date d'inscription: 12 Sep 2005
- Messages: 40
Re: Optimisation requête POSTGIS pour données volumineuses
Pourquoi la table bdtopo_bati_indifferencie_2008_l93 a-t-elle pour dimension 4 ?
si cela n'est pas indispensable :
passer la en dimension 2 :
- en faisant sauter la contrainte enforce_dims... sur la table,
- en mettant a jour la table geometry_columns,
- mettre a jour la table
UPDATE bdtopo_bati_indifferencie_2008_l93 SET the_geom = force_2d(the_geom);
VACUUM FULL ANALYZE bdtopo_bati_indifferencie_2008_l93 ;
- remettre la contrainte enforce_dims...
et alors relancer l'export.
Jérôme
Jérôme ROLLAND
FIT-ESIC
Hors ligne
#25 Wed 19 May 2010 16:40
- sylpingus
- Participant occasionnel
- Lieu: Aix en Provence
- Date d'inscription: 9 Jan 2006
- Messages: 34
Re: Optimisation requête POSTGIS pour données volumineuses
Pour répondre à Pascal :
L'emprise d'extraction n'est pas nécessairement un département mais un ensemble de communes (qui peut des fois monter à un peu plus de 700). Du coup, pour un ensemble de communes qui ne correspond pas à un département, il faudrait avoir une requête semblable à celle-ci :
Code:
CREATE TABLE bati_reg AS SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export a, communes b WHERE ST_INTERSECTS (a.the_geom,b.the_geom) AND b.codeinsee = 'codeinsee' AND b.codeinsee = 'codeinsee' AND b.codeinsee = 'codeinsee' ....;
En multipliant les codeinsee demandés, ça fait assez lourd et ça ne plait pas du tout à Postgis (nous avions fait des tests en amont). En fait, la solution la plus efficiente après nos tests en amont était de stocker l'emprise demandée sous forme d'un objet unique stocker dans une table ad-hoc.
Pour répondre à Jérôme :
La couche bâti de la BD TOPO de l'IGN est en quatre dimensions car le shape d'origine sont en XYZM pour stocker la composante alti des bâti. Etant donné que nous sommes notamment censés restituer en sortie des shape XYZM, nous sommes obligés de conserver la dimension 4 quand elle existe. De plus nous avons fait des tests en 2D et ça ne marchait pas mieux, malheureusement.
Hors ligne
#26 Wed 19 May 2010 17:44
- j.rolland
- Participant occasionnel
- Lieu: MONTPELLIER
- Date d'inscription: 12 Sep 2005
- Messages: 40
Re: Optimisation requête POSTGIS pour données volumineuses
Je suis tout a fait d'accord avec vous pour :
En multipliant les codeinsee demandés, ça fait assez lourd et ça ne plait pas du tout à Postgis (nous avions fait des tests en amont). En fait, la solution la plus efficiente après nos tests en amont était de stocker l'emprise demandée sous forme d'un objet unique stocker dans une table ad-hoc.
Encore d'autres questions :
- Quels paramètres avez vous modifiés dans le fichier postgresql.conf après tuning ?
- Avez vous vérifié la géométrie stockée dans la table extent_decoupe ?
select ST_IsSimple(the_geom),ST_IsValidReason(the_geom) from extent_decoupe where gid=1
Jérôme
Jérôme ROLLAND
FIT-ESIC
Hors ligne
#27 Wed 19 May 2010 17:51
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 611
Re: Optimisation requête POSTGIS pour données volumineuses
En multipliant les codeinsee demandés, ça fait assez lourd et ça ne plait pas du tout à Postgis (nous avions fait des tests en amont). En fait, la solution la plus efficiente après nos tests en amont était de stocker l'emprise demandée sous forme d'un objet unique stocker dans une table ad-hoc.
il est possible de faire une table intermediaire ENS_COM des communes concernées:
Code:
WHERE b.codeinsee IN (select codeinsee from ENS_COM)
L'objet unique me parait pas la meilleur solution. Garder le contour des communes réduits énormément les temps de calculs. Je viens de faire le test.
Dernière modification par ppluvinet (Wed 19 May 2010 17:52)
Pascal PLUVINET
Hors ligne
#28 Wed 19 May 2010 18:12
Re: Optimisation requête POSTGIS pour données volumineuses
Bonsoir,
quelques tests sur la table bati_indifferencie (telle que fournie dans la bd_topo) en Languedoc-Roussillon (745000 objets environ) et le département du GARD (353 communes).
Je n'avais jamais testé jusqu'à aujourd'hui mais en effet, il semble bien plus efficace de ne pas agréger les objets avant l'intersection, quit à faire un distinct si des géométries se trouvaient à cheval sur 2 communes.
intersection avec le département : Total runtime: 243869.209 ms
Code:
SELECT ign_bd_topo.bati_indifferencie.* FROM ign_bd_topo.bati_indifferencie JOIN departement ON st_intersects(departement.geometrie, ign_bd_topo.bati_indifferencie.geometrie) WHERE departement.num_insee='30';
intersection avec les communes du département (where num_insee like '30%') : Total runtime: 16039.480 ms
Code:
SELECT ign_bd_topo.bati_indifferencie.* FROM ign_bd_topo.bati_indifferencie JOIN commune ON st_intersects(commune.geometrie, ign_bd_topo.bati_indifferencie.geometrie) WHERE commune.num_insee like '30%';
intersection avec les communes du département (sous-requête) : Total runtime: 15228.998 ms (18484 ms avec DISTINCT)
Code:
SELECT ign_bd_topo.bati_indifferencie.* FROM ign_bd_topo.bati_indifferencie JOIN commune ON st_intersects(commune.geometrie, ign_bd_topo.bati_indifferencie.geometrie) WHERE commune.num_insee IN (select num_insee from commune where num_insee like '30%');
Dernière modification par Mathieu BOSSAERT (Thu 20 May 2010 09:28)
Mathieu BOSSAERT
Association GeoRezo
Hors ligne
#29 Wed 19 May 2010 18:23
- sylpingus
- Participant occasionnel
- Lieu: Aix en Provence
- Date d'inscription: 9 Jan 2006
- Messages: 34
Re: Optimisation requête POSTGIS pour données volumineuses
re bonjour,
pour répondre à Jérôme :
** select ST_IsSimple(the_geom),ST_IsValidReason(the_geom) from extent_decoupe where gid=1 me donne bien IsSimple = t et IsValidReason = Valid Geometry
** Nous avons modifié les éléments suivants dans le postgresql.conf (pour mémoire 2 quadri coeur XEON à 2Ghz et 2 Go de RAM) :
shared_buffers = 128MB
work_mem = 128MB
random_page_cost = 3.0
effective_cache_size = 800MB
pour répondre à Pascal :
compte tenu des contraintes que nous avons au niveau de l'extraction, nous ne pouvons nous contenter d'un ST_INTERSECTS. La synthaxe mentionnée fonctionne-t-elle avec un ST_RELATE, par exemple :
Code:
CREATE TABLE bati_reg AS SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export a, communes b WHERE ST_RELATE (a.the_geom,b.the_geom) AND b.codeinsee IN (select codeinsee from ENS_COM);
Je n'en suis pas certain car ST_RELATE ne fait pas un appel implicite aux index spatiaux dans la version 1.4.1 de postgis, il me semble. D'où la nécessité de passer par un &&.
De plus, imaginons que l'on cherche à extraire les communes correspondant à une emprise donnée, il ne faut pas que les communes qui bordent l'emprise soient extraites, ce qui serait le cas avec un ST_INTERSECTS. C'est pour cela que nous utilisons un ST_RELATE ('2********') (en gros vérifier que l'intersection des deux polygones soit un polygone).
En passant par une table intermédiaire de commune qui correspondent à l'emprise cela reviendrait à utiliser la commande suivante ? :
Code:
SELECT id,prec_plani,prec_alti,origin_bat,hauteur,z_min,z_max FROM bdtopo_bati_indifferencie_2008_l93_export WHERE the_geom && ST_GeomFromText('POLYGON((799607 6232734, 799607 6315411, 926824 6315411,926824 6232734,799607 6232734))',310024140) AND ST_RELATE (public.bdtopo_bati_indifferencie_2008_l93_export.the_geom,(SELECT the_geom FROM public.communes WHERE codeinsee IN (select codeinsee from ENS_COM)),'2********');
Et je ne suis pas sur que les résultats soient identiques à ce que l'on cherche, malheureusement. En effet, la fonction relate va raisonner sur l'emprise de chaque commune considérée et non sur l'emprise globale du polygone d'extraction.
Je vais quand même faire le test pour valider mon hypothèse.
En tout cas merci pour vos retours.
Hors ligne
#30 Thu 20 May 2010 11:29
- sylpingus
- Participant occasionnel
- Lieu: Aix en Provence
- Date d'inscription: 9 Jan 2006
- Messages: 34
Re: Optimisation requête POSTGIS pour données volumineuses
Re bonjour,
j'ai fait quelques tests sur la base des remarques de Mathieu BOSSAERT. Avant de vous faire part des résultats, j'aurais une question pour Mathieu. La couche commune sur laquelle vous avez fait les tests est-elle la couche commune de la BD TOPO ?
D'autre part, vos tests très intéressants portent sur la fonction st_intersects. Avez-vous fait des tests avec la fonction st_relate qui nous intéresse plus spécifiquement ?
Pour mes tests, je me suis basé dans un premier temps sur une extraction de la table communes de la bd_topo pour les département du 13 (qui compte 119 communes). Voici mes résultats :
Premier test :
Code:
SELECT DISTINCT bdtopo_commune_2010_l93.* FROM bdtopo_commune_2010_l93 JOIN extract_coms_bdtopo_2010 ON st_intersects(extract_coms_bdtopo_2010.the_geom,bdtopo_commune_2010_l93.the_geom) WHERE extract_coms_bdtopo_2010.code_insee like '13%';
Résultat : 155 communes extraites en 17605 ms. Cependant la sélection donne 155 communes alors qu'il n'en faudrait que 119 (problème du st_intersects dans certains cas d'où notre volonté d'utliser un st_relate qui permet des requêtes plus fines).
Un EXPLAIN ANALYZE de cette requête donne :
Code:
" -> Sort (cost=121.04..121.45 rows=163 width=75076) (actual time=17486.460..17487.632 rows=801 loops=1)" " Sort Key: bdtopo_commune_2010_l93.gid, bdtopo_commune_2010_l93.arrondisst, bdtopo_commune_2010_l93.canton, bdtopo_commune_2010_l93.code_insee, bdtopo_commune_2010_l93.depart, bdtopo_commune_2010_l93.id, bdtopo_commune_2010_l93.multican, bdtopo_commune_2010_l93.nom, bdtopo_commune_2010_l93.popul, bdtopo_commune_2010_l93.prec_plani, bdtopo_commune_2010_l93.region, bdtopo_commune_2010_l93.statut, bdtopo_commune_2010_l93.the_geom" " Sort Method: quicksort Memory: 228kB" " -> Nested Loop (cost=0.00..115.06 rows=163 width=75076) (actual time=74.385..17179.119 rows=801 loops=1)" " Join Filter: _st_intersects(extract_coms_bdtopo_2010.the_geom, bdtopo_commune_2010_l93.the_geom)" " -> Seq Scan on extract_coms_bdtopo_2010 (cost=0.00..22.04 rows=117 width=76575) (actual time=0.100..12.254 rows=119 loops=1)" " Filter: ((code_insee)::text ~~ '13%'::text)" " -> Index Scan using bdtopo_commune_2010_l93_the_geom_gist on bdtopo_commune_2010_l93 (cost=0.00..0.53 rows=1 width=75076) (actual time=0.072..0.202 rows=8 loops=119)" " Index Cond: (extract_coms_bdtopo_2010.the_geom && bdtopo_commune_2010_l93.the_geom)" "Total runtime: 17814.884 ms"
Les index spatiaux sont bien utilisés.
Deuxième test : utilisation de st_relate sans index spatiaux
Code:
SELECT DISTINCT bdtopo_commune_2010_l93.* FROM bdtopo_commune_2010_l93 JOIN extract_coms_bdtopo_2010 ON st_relate(bdtopo_commune_2010_l93.the_geom, extract_coms_bdtopo_2010.the_geom,'2********') WHERE extract_coms_bdtopo_2010.code_insee like '13%';
Dans ce cas, pas d'utlisation des index spatiaux car pas d'utilisation du &&.
Résultat : 119 communes extraites en 209441 ms
Troisième test :utilisation de st_relate avec index spatiaux
Code:
SELECT DISTINCT bdtopo_commune_2010_l93.* FROM bdtopo_commune_2010_l93 JOIN extract_coms_bdtopo_2010 ON st_relate(bdtopo_commune_2010_l93.the_geom, extract_coms_bdtopo_2010.the_geom,'2********') WHERE bdtopo_commune_2010_l93.the_geom && ST_GeomFromText('POLYGON((799607 6232734, 799607 6315411, 926824 6315411,926824 6232734,799607 6232734))',310024140)AND extract_coms_bdtopo_2010.code_insee like '13%';
Résultat : 119 communes extraites en 80977ms
Un EXPLAIN ANALYZE de cette requête donne :
Code:
"Unique (cost=157075.01..157372.58 rows=218 width=75076) (actual time=66888.704..66924.207 rows=119 loops=1)" " -> Sort (cost=157075.01..157096.27 rows=8502 width=75076) (actual time=66888.697..66888.845 rows=119 loops=1)" " Sort Key: bdtopo_commune_2010_l93.gid, bdtopo_commune_2010_l93.arrondisst, bdtopo_commune_2010_l93.canton, bdtopo_commune_2010_l93.code_insee, bdtopo_commune_2010_l93.depart, bdtopo_commune_2010_l93.id, bdtopo_commune_2010_l93.multican, bdtopo_commune_2010_l93.nom, bdtopo_commune_2010_l93.popul, bdtopo_commune_2010_l93.prec_plani, bdtopo_commune_2010_l93.region, bdtopo_commune_2010_l93.statut, bdtopo_commune_2010_l93.the_geom" " Sort Method: quicksort Memory: 49kB" " -> Nested Loop (cost=22.15..626.10 rows=8502 width=75076) (actual time=149.132..66880.523 rows=119 loops=1)" " Join Filter: st_relate(bdtopo_commune_2010_l93.the_geom, extract_coms_bdtopo_2010.the_geom, '2********'::text)" " -> Index Scan using bdtopo_commune_2010_l93_the_geom_gist on bdtopo_commune_2010_l93 (cost=0.00..30.07 rows=218 width=75076) (actual time=0.100..4.685 rows=226 loops=1)" " Index Cond: (the_geom && '0103000020CC977A12010000000500000000000000EE66284100000080A7C6574100000000EE662841000000C06417584100000000D0482C41000000C06417584100000000D0482C4100000080A7C6574100000000EE66284100000080A7C65741'::geometry)" " -> Materialize (cost=22.15..23.32 rows=117 width=76575) (actual time=0.002..0.410 rows=119 loops=226)" " -> Seq Scan on extract_coms_bdtopo_2010 (cost=0.00..22.04 rows=117 width=76575) (actual time=0.025..1.696 rows=119 loops=1)"" Filter: ((code_insee)::text ~~ '13%'::text)" "Total runtime: 66924.572 ms"
Quatrième test :utilisation de st_relate avec croisement avec un polygone assemblé des communes (notre méthode initiale)
Code:
SELECT * FROM bdtopo_commune_2010_l93 WHERE the_geom && ST_GeomFromText('POLYGON((799607 6232734, 799607 6315411, 926824 6315411,926824 6232734,799607 6232734))',310024140) AND ST_RELATE (public.bdtopo_commune_2010_l93.the_geom,(SELECT the_geom FROM public.extent_decoupe_polygone WHERE gid = 1),'2********');
Résultat : 155 communes extraites en 68057 ms. A en croire les résultats, cette méthode est la plus rapide dans ce cas de figure.
Un EXPLAIN ANALYZE donne :
Code:
"Index Scan using bdtopo_commune_2010_l93_the_geom_gist on bdtopo_commune_2010_l93 (cost=1.01..31.62 rows=73 width=75076) (actual time=354.028..59181.370 rows=119 loops=1)" " Index Cond: (the_geom && '0103000020CC977A12010000000500000000000000EE66284100000080A7C6574100000000EE662841000000C06417584100000000D0482C41000000C06417584100000000D0482C4100000080A7C6574100000000EE66284100000080A7C65741'::geometry)" " Filter: st_relate(the_geom, $0, '2********'::text)" " InitPlan 1 (returns $0)" " -> Seq Scan on extent_decoupe_polygone (cost=0.00..1.01 rows=1 width=3684968) (actual time=0.079..0.081 rows=1 loops=1)" " Filter: (gid = 1)" "Total runtime: 59182.008 ms"
Les index spatiaux sont bien utilisés.
Pour conclure, toutes les méthodes sont bonnes mais concernant notre cas précis l'utilisation du st_relate sur de gros ensembles d'objets ne semble pas bien être géré par Postgis, malheureusement. Quant au fait d'utiliser le croisement avec les communes plutôt qu'avec un polygone unique constitué de l'agrégat des communes souhaitées, je ne sais pas que dire pour trancher compte tenue des résultats mentionnés ci-dessus.
De ce que je comprends, PostGIS n'est pas si performant que cela sur des fonctions de requêtes topologiques "avancées" telles que st_relate.
Dans tous les cas, si vous avez des remarques et suggestions nous sommes preneurs car nous bloquons un peu malgré les tests que nous avons pu mener et vos remarques pertinentes.
Hors ligne