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! :)
fonte
Respostas:
A execução da saída do Explain Anlayze por meio do explica.depesz.com destaca que a maior parte da lentidão vem dessa ação:
Isso foi indexado antes? Você pode indexá-lo agora?
Ao pesquisar nessa área problemática, também encontrei perguntas e respostas relacionadas em um site do Open Street Map:
fonte
O PostgreSQL pode usar apenas um núcleo para qualquer consulta. Ele alcança um bom desempenho paralelo com muitas consultas simultâneas, mas não se beneficia de grandes contagens principais para cargas de trabalho de apenas algumas consultas muito grandes. Portanto, se você estiver executando apenas uma consulta, 5% não é tão surpreendente, embora eu espere que seja 12% em um sistema de 8 núcleos.
A falta de iowait sugere que provavelmente não está sofrendo com E / S de disco.
Portanto - não parece ter gargalo na CPU ou na E / S.
É possível que a consulta seja simplesmente bloqueada por um tempo por um bloqueio? Verifique
pg_stat_activity
a consulta e participepg_locks
para ver se existem bloqueios não concedidos. (Existem consultas pré-definidas sobre o monitoramento de bloqueio de página).A próxima coisa a fazer é executar alguns testes do sistema de nível inferior. Execute
pg_test_fsync
, use os testes de E / S da CPU e do sysbench, etc. Se eles também apresentarem um desempenho ruim, leve-o ao seu provedor de hospedagem.Você também deve coletar
perf top -a
um pouco a saída, ver o que está realmente fazendo.fonte