Tengo una pila TileMill / PostGIS ejecutándose en una máquina virtual Ubuntu 12.04 de 8 núcleos en una nube OpenStack. Es una reconstrucción de un sistema muy similar que funcionaba muy bien en hardware muy similar (la misma nube, pero diferente hardware físico, creo) la semana pasada. Intenté reconstruir la pila exactamente igual que antes (usando algunos scripts que había creado).
Todo funciona, pero la base de datos está llevando a cabo consultas insoportablemente lentas, lo que en última instancia se manifiesta con una generación de mosaicos muy lenta. Una consulta de ejemplo (cuente el número de pubs dentro de un radio de cada ciudad en Australia), que anteriormente tomaba entre 10 y 20 segundos, ahora lleva más de 10 minutos:
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
(Incluyo esta consulta como un síntoma, no directamente el problema a resolver. Esta consulta en particular solo se ejecuta una vez por semana más o menos).
El servidor tiene 32 GB de RAM, y he configurado Postgres de la siguiente manera (siguiendo los consejos que se encuentran en la web):
shared_buffers = 8GB
autovacuum = on
effective_cache_size = 8GB
work_mem = 128MB
maintenance_work_mem = 64MB
wal_buffers = 1MB
checkpoint_segments = 10
iostat
muestra que no se lee nada, se escribe un poco de datos (ni idea de dónde ni por qué), y 95% de CPU inactiva:
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
Salida de muestra 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
Aferrándome a las pajitas, moví el directorio de datos de Postgres de vda a vdb pero, por supuesto, eso no hizo ninguna diferencia.
Entonces estoy perdido. ¿Por qué Postgres solo usa el 5% de la CPU disponible cuando no está esperando ninguna E / S? Agradecería cualquier sugerencia para una mayor investigación, otras herramientas, cosas al azar para probar.
Actualizar
Capturé instantáneamente el servidor y lo inicié en una parte diferente de la misma nube (una zona de disponibilidad diferente). Los resultados fueron un poco raros. vmstat
en este servidor informa un 12% de uso de la CPU (que ahora entiendo como el valor esperado para una sola consulta de Postgres en una máquina virtual de 8 núcleos), aunque el tiempo real de ejecución de la consulta es prácticamente idéntico (630 segundos frente a 623).
Ahora me doy cuenta de que esta consulta en particular probablemente no sea una buena muestra por este motivo: solo puede usar un núcleo, y es un update
(mientras que la representación en mosaico es solo select
s).
Tampoco me di cuenta de explain
que aparentemente planet_osm_polygon
no está usando un índice. Esa podría ser la causa, así que lo perseguiré a continuación.
Actualización2
El problema definitivamente parece ser que el / los índice (s) planet_osm_polygon se están / no se están utilizando. Hay dos (uno creado por osm2pgsql, uno creado por mí siguiendo alguna guía aleatoria):
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);
Las estadísticas en planet_osm_polygon y planet_osm_point son bastante reveladoras, creo:
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 leí bien, Postgres ha buscado el planet_osm_polygon 1574 veces, pero en realidad nunca ha encontrado nada, por lo que ha hecho una cantidad ridículamente grande de búsquedas de fuerza bruta.
La nueva pregunta: ¿por qué?
Misterio resuelto
Gracias a la respuesta de Frederik Ramm , la respuesta resulta ser bastante simple: no había índice espacial, por alguna razón. Era trivial regenerarlos:
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);
Ejecutar esa consulta ahora lleva 4,6 segundos. ¡Los índices espaciales son importantes! :)