J'ai une pile TileMill / PostGIS fonctionnant sur une machine virtuelle Ubuntu 12.04 à 8 cœurs sur un cloud OpenStack. C'est une reconstruction d'un système très similaire qui fonctionnait bien sur du matériel très similaire (même cloud, mais matériel physique différent, je crois) la semaine dernière. J'ai essayé de reconstruire la pile exactement la même qu'elle était (en utilisant certains scripts que j'avais construits).
Tout fonctionne, mais la base de données exécute les requêtes avec une lenteur atroce, ce qui se manifeste finalement avec une génération de tuiles très lente. Un exemple de requête (comptez le nombre de pubs dans un rayon de chaque ville en Australie), qui prenait auparavant quelque chose comme 10-20 secondes prend maintenant plus de 10 minutes:
explain (analyze, buffers) update places set pubs =
(select count(*) from planet_osm_point p where p.amenity = 'pub' and st_dwithin(p.way,places.way,scope)) +
(select count(*) from planet_osm_polygon p where p.amenity = 'pub' and st_dwithin(p.way,places.way,scope)) ;
Update on places (cost=0.00..948254806.93 rows=9037 width=160) (actual time=623321.558..623321.558 rows=0 loops=1)
Buffers: shared hit=132126300
-> Seq Scan on places (cost=0.00..948254806.93 rows=9037 width=160) (actual time=68.130..622931.130 rows=9037 loops=1)
Buffers: shared hit=132107781
SubPlan 1
-> Aggregate (cost=12.95..12.96 rows=1 width=0) (actual time=0.187..0.188 rows=1 loops=9037)
Buffers: shared hit=158171
-> Index Scan using planet_osm_point_index on planet_osm_point p (cost=0.00..12.94 rows=1 width=0) (actual time=0.163..0.179 rows=0 loops=9037)
Index Cond: (way && st_expand(places.way, (places.scope)::double precision))
Filter: ((amenity = 'pub'::text) AND (places.way && st_expand(way, (places.scope)::double precision)) AND _st_dwithin(way, places.way, (places.scope)::double precision))
Buffers: shared hit=158171
SubPlan 2
-> Aggregate (cost=104917.24..104917.25 rows=1 width=0) (actual time=68.727..68.728 rows=1 loops=9037)
Buffers: shared hit=131949237
-> Seq Scan on planet_osm_polygon p (cost=0.00..104917.24 rows=1 width=0) (actual time=68.138..68.716 rows=0 loops=9037)
Filter: ((amenity = 'pub'::text) AND (way && st_expand(places.way, (places.scope)::double precision)) AND (places.way && st_expand(way, (places.scope)::double precision)) AND _st_dwithin(way, places.way, (places.scope)::double precision))
Buffers: shared hit=131949237
Total runtime: 623321.801 ms
(J'inclus cette requête comme symptôme, pas directement le problème à résoudre. Cette requête particulière n'est exécutée qu'une fois par semaine environ.)
Le serveur dispose de 32 Go de RAM, et j'ai configuré Postgres comme suit (suivant les conseils trouvés sur le Web):
shared_buffers = 8GB
autovacuum = on
effective_cache_size = 8GB
work_mem = 128MB
maintenance_work_mem = 64MB
wal_buffers = 1MB
checkpoint_segments = 10
iostat
ne montre rien en cours de lecture, un peu de données en cours d'écriture (aucune idée où ni pourquoi) et 95% de CPU inactif:
avg-cpu: %user %nice %system %iowait %steal %idle
5.40 0.00 0.00 0.11 0.00 94.49
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
vda 0.20 0.00 0.80 0 8
vdb 2.30 0.00 17.58 0 176
Exemple de sortie de vmstat
:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
...
1 0 0 18329748 126108 12600436 0 0 0 18 148 140 5 0 95 0
2 0 0 18329400 126124 12600436 0 0 0 9 173 228 5 0 95 0
En m'accrochant aux pailles, j'ai déplacé le répertoire de données Postgres de vda à vdb mais bien sûr, cela n'a fait aucune différence.
Je suis donc perdu. Pourquoi Postgres n'utilise-t-il que 5% du processeur disponible alors qu'il n'attend aucune E / S? Je serais ravi de toute suggestion pour une enquête plus approfondie, d'autres outils, des choses aléatoires à essayer.
Mise à jour
J'ai instantané le serveur et l'ai lancé sur une autre partie du même cloud (une zone de disponibilité différente). Les résultats étaient un peu étranges. vmstat
sur ce serveur rapporte 12% d'utilisation du processeur (ce que je comprends maintenant comme la valeur attendue pour une seule requête Postgres sur une machine virtuelle à 8 cœurs) - bien que le temps d'exécution de la requête réelle soit pratiquement identique (630 secondes vs 623).
Je me rends compte maintenant que cette requête particulière n'est probablement pas un bon exemple pour cette raison: elle ne peut utiliser qu'un seul noyau, et c'est un update
(alors que le rendu des tuiles est juste select
s).
Je n'ai pas non plus remarqué dans le explain
qui planet_osm_polygon
n'utilise apparemment pas d'index. Cela pourrait bien être la cause, alors je vais poursuivre cela ensuite.
Update2
Le problème semble définitivement être que le ou les index planet_osm_polygon sont / ne sont pas utilisés. Il y en a deux (un créé par osm2pgsql, un créé par moi en suivant un guide aléatoire):
CREATE INDEX idx_planet_osm_polygon_tags
ON planet_osm_polygon
USING gist
(tags);
CREATE INDEX planet_osm_polygon_pkey
ON planet_osm_polygon
USING btree
(osm_id);
Les statistiques sur planet_osm_polygon et planet_osm_point sont assez révélatrices, je pense:
planet_osm_polygon:
Sequential Scans 194204
Sequential Tuples Read 60981018608
Index Scans 1574
Index Tuples Fetched 0
planet_osm_point:
Sequential Scans 1142
Sequential Tuples Read 12960604
Index Scans 183454
Index Tuples Fetched 43427685
Si j'ai bien lu, Postgres a fouillé le planet_osm_polygon 1574 fois, mais n'a jamais rien trouvé, donc a fait un nombre ridiculement élevé de recherches par force brute.
La nouvelle question: pourquoi?
Mystère résolu
Grâce à la réponse de Frederik Ramm , la réponse s'avère assez simple: il n'y avait pas d'index spatial, pour une raison quelconque. C'était insignifiant de les régénérer:
create index planet_osm_polygon_polygon on planet_osm_polygon using gist(way);
create index planet_osm_polygon_point on planet_osm_point using gist(way);
L'exécution de cette requête prend désormais 4,6 secondes. Les index spatiaux comptent! :)