Por que o Postgres está 95% ocioso, sem E / S de arquivo?

8

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

Eu também não percebi no explainque aparentemente planet_osm_polygonnã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! :)

Steve Bennett
fonte
Sei que essa entrada é bastante antiga, mas estou enfrentando um problema semelhante. Não consigo criar planet_osm_polygon_point duas vezes, pois o índice já existe. No entanto, não importa realmente como o índice é chamado, certo?
Sebastian Borggrewe
Bem, se o índice existe, por que você deseja criar outro? Mas, em qualquer caso, você pode descartar o antigo ou renomear o novo.
Steve Bennett
Estou apenas perguntando, já que ambos os índices: criar índice planet_osm_polygon_point em planet_osm_polygon usando gist (way); crie o índice planet_osm_polygon_point em planet_osm_point usando gist (way); são nomeados planet_osm_polygon_point, o que parece um erro, a menos que esteja faltando alguma coisa.
Sebastian Borggrewe 13/01
Oh! Eu não entendi Sim, há um erro de digitação na minha resposta.
Steve Bennett
Obrigado Steve, você também pode corrigir o erro de digitação na sua resposta para referência futura. Obrigado.
Sebastian Borggrewe 18/01

Respostas:

4

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:

Seq Scan on planet_osm_polygon p 

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:

Mark Stosberg
fonte
Obrigado por apontar isso - eu perdi isso. De fato, existem dois índices nesta tabela. Atualizando minha pergunta com mais informações.
26513 Steve Bennett
Oh - esse link tinha a resposta. Sim, embora houvesse "um índice", era apenas no campo de ID, não no campo de geometria real ("caminho") - tão inútil para a indexação espacial. Os comentários de Frederik continham a resposta.
26513 Steve Bennett
4

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_activitya consulta e participe pg_lockspara 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 -aum pouco a saída, ver o que está realmente fazendo.

Craig Ringer
fonte