¿Por qué Postgres está inactivo al 95%, sin E / S de archivo?

8

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. vmstaten 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 selects).

Tampoco me di cuenta de explainque aparentemente planet_osm_polygonno 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! :)

Steve Bennett
fuente
Me doy cuenta de que esta entrada es bastante antigua, sin embargo, estoy experimentando un problema similar. No puedo crear planet_osm_polygon_point dos veces, ya que el índice ya existe. Sin embargo, realmente no importa cómo se llame el índice, ¿verdad?
Sebastian Borggrewe
Bueno, si el índice existe, ¿por qué quieres crear otro? Pero en cualquier caso, puede descartar el antiguo o cambiar el nombre del nuevo.
Steve Bennett
Solo estoy preguntando desde ambos índices: crear index planet_osm_polygon_point en planet_osm_polygon usando gist (way); crear índice planet_osm_polygon_point en planet_osm_point usando gist (way); se llaman planet_osm_polygon_point, lo que parece un error a menos que me falte algo.
Sebastian Borggrewe
Oh! No entendi Sí, hay un error tipográfico en mi respuesta.
Steve Bennett el
Gracias Steve, ¿podrías también corregir el error tipográfico en tu respuesta para futuras referencias? Gracias.
Sebastian Borggrewe

Respuestas:

4

Ejecutar su salida de Explain Anlayze a través de explicar.depesz.com destaca que la mayor parte de la lentitud proviene de esta acción:

Seq Scan on planet_osm_polygon p 

¿Fue eso indexado antes? ¿Puedes indexarlo ahora?

Al buscar esa área problemática, también encontré un Q&A relacionado en un sitio de Open Street Map:

Mark Stosberg
fuente
Gracias por señalar esto, me lo perdí. De hecho, hay dos índices en esta tabla. Actualizando mi pregunta con más información.
Steve Bennett
Oh, ese enlace tenía la respuesta. Sí, aunque había "un índice", estaba solo en el campo ID, no en el campo de geometría real ("forma"), por lo que es inútil para la indexación espacial. Los comentarios de Frederik contenían la respuesta.
Steve Bennett
4

PostgreSQL solo puede usar un núcleo para cualquier consulta dada. Logra un buen rendimiento paralelo con muchas consultas simultáneas, pero no se beneficia de grandes recuentos de núcleos para cargas de trabajo de solo un par de consultas muy grandes. Entonces, si solo está ejecutando una sola consulta, el 5% no es tan sorprendente, aunque esperaría que sea el 12% en un sistema de 8 núcleos.

La falta de iowait sugiere que probablemente no esté sufriendo por E / S de disco.

Entonces, no parece tener un cuello de botella en la CPU o en las E / S.

¿Es posible que la consulta simplemente esté bloqueada por un tiempo por un candado? Verifique pg_stat_activityla consulta y únase a pg_lockspara ver si hay bloqueos no otorgados. (Hay consultas enlatadas sobre la supervisión de bloqueo de Pg).

Lo siguiente que debe hacer es ejecutar algunas pruebas de sistema de nivel inferior. Ejecute pg_test_fsync, use las pruebas de CPU y E / S de sysbench, etc. Si estos también funcionan miserablemente, créelo con su proveedor de alojamiento.

También debe recopilar la perf top -asalida por un momento, ver lo que realmente está haciendo.

Craig Ringer
fuente