Eu tenho uma pilha TileMill / PostGIS em execução em uma VM Ubuntu 12.04 de 8 núcleos em uma nuvem OpenStack. É uma reconstrução de um sistema muito semelhante que estava funcionando muito bem em hardware muito semelhante (mesma nuvem, mas hardware físico diferente, acredito) na semana passada. Eu tentei reconstruir a pilha exatamente da mesma maneira que era (usando alguns scripts que eu construí).
Tudo funciona, mas o banco de dados está realizando consultas de forma torturante e lenta, o que se manifesta em última análise com a geração de blocos muito lenta. Uma consulta de exemplo (conte o número de bares em um raio de todas as cidades da Austrália), que anteriormente levava algo entre 10 e 20 segundos, agora leva mais 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
(Estou incluindo essa consulta como sintoma, não diretamente o problema a ser resolvido. Essa consulta específica é executada apenas uma vez por semana, aproximadamente.)
O servidor possui 32 GB de RAM e eu configurei o Postgres da seguinte maneira (seguindo os conselhos encontrados na Web):
shared_buffers = 8GB
autovacuum = on
effective_cache_size = 8GB
work_mem = 128MB
maintenance_work_mem = 64MB
wal_buffers = 1MB
checkpoint_segments = 10
iostat
mostra nada sendo lido, alguns dados sendo gravados (sem ideia de onde ou por quê) e 95% da CPU ociosa:
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
Saída de amostra 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
Segurando palhinhas, mudei o diretório de dados do Postgres de vda para vdb, mas é claro que isso não fez diferença.
Então, eu estou perdido. Por que o Postgres usa apenas 5% da CPU disponível quando não está aguardando nenhuma E / S? Gostaria de receber sugestões de outras investigações, outras ferramentas, coisas aleatórias para tentar.
Atualizar
Tirei um instantâneo do servidor e o iniciei em uma parte diferente da mesma nuvem (uma zona de disponibilidade diferente). Os resultados foram um pouco estranhos. vmstat
neste servidor relata 12% de uso da CPU (que agora entendo como o valor esperado para uma única consulta do Postgres em uma VM de 8 núcleos) - embora o tempo real de execução da consulta seja praticamente idêntico (630 segundos vs 623).
Agora percebo que essa consulta em particular provavelmente não é uma boa amostra por esse motivo: ela pode usar apenas um núcleo e é um update
(enquanto a renderização do bloco é apenas select
s).
Eu também não percebi no explain
que aparentemente planet_osm_polygon
não está usando um índice. Essa poderia ser a causa, então vou atrás disso.
Update2
Definitivamente, o problema parece ser que os índices planet_osm_polygon estão / não estão sendo usados. Existem dois (um criado por osm2pgsql, outro criado por mim seguindo um guia aleatório):
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);
As estatísticas em planet_osm_polygon e planet_osm_point são bastante reveladoras, eu acho:
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
Se eu entendi direito, o Postgres pesquisou o planet_osm_polygon 1574 vezes, mas nunca realmente encontrou nada, por isso fez um número ridiculamente grande de pesquisas de força bruta.
A nova pergunta: por quê?
Mistério resolvido
Graças à resposta de Frederik Ramm , a resposta acaba sendo bastante simples: não havia índice espacial, por algum motivo. Foi trivial regenerá-los:
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);
A execução dessa consulta agora leva 4,6 segundos. Os índices espaciais são importantes! :)