O que o PostgreSQL explain está me dizendo exatamente?

116

A saída de explicação do MySQL é bastante direta. O PostgreSQL é um pouco mais complicado. Também não consegui encontrar um bom recurso que o explique.

Você pode descrever o que exatamente o explain está dizendo ou pelo menos me apontar na direção de um bom recurso?

kjg
fonte

Respostas:

50

Explaining_EXPLAIN.pdf também pode ajudar.

Milen A. Radev
fonte
64
Estou perplexo porque as pessoas pensam que os slides são uma boa documentação técnica. Um vídeo da palestra pode ser útil, mas a densidade de informações dessa apresentação de slides é muito próxima de zero. Nos primeiros seis slides (1/5 do total), há exatamente 1 frase de conteúdo técnico: "• EXPLAIN funciona em qualquer DML, não apenas em SELECT (ou seja, UPDATE, DELETE e INSERT)". Meu maior mal-entendido é o que significa o tempo de "inicialização", e isso não é explicado em nenhum lugar nestes ~ 30 slides.
Mark E. Haase
80

A parte que sempre achei confusa é o custo inicial versus custo total. Procuro no Google sempre que me esqueço, o que me traz de volta aqui, o que não explica a diferença, e é por isso que estou escrevendo esta resposta. Isso é o que eu obtive da documentação do PostgresEXPLAIN , explicado como eu o entendo.

Aqui está um exemplo de um aplicativo que gerencia um fórum:

EXPLAIN SELECT * FROM post LIMIT 50;

Limit  (cost=0.00..3.39 rows=50 width=422)
  ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

Aqui está a explicação gráfica do PgAdmin:

explicação gráfica da primeira consulta

(Quando estiver usando o PgAdmin, você pode apontar o mouse para um componente para ler os detalhes de custo.)

O custo é representado como uma tupla, por exemplo, o custo de LIMITé cost=0.00..3.39e o custo da varredura sequencial posté cost=0.00..15629.12. O primeiro número na tupla é o custo inicial e o segundo número é o custo total . Como usei EXPLAINe não usei EXPLAIN ANALYZE, esses custos são estimativas, não medidas reais.

  • O custo inicial é um conceito complicado. Não representa apenas a quantidade de tempo antes do início do componente . Ele representa a quantidade de tempo entre o início da execução do componente (leitura de dados) e o momento em que o componente produz sua primeira linha .
  • O custo total é todo o tempo de execução do componente, desde o início da leitura dos dados até o término da gravação da saída.

Como complicação, os custos de cada nó "pai" incluem os custos de seus nós filhos. Na representação de texto, a árvore é representada por indentação, por exemplo, LIMITé um nó pai e Seq Scané seu filho. Na representação do PgAdmin, as setas apontam do filho para o pai - a direção do fluxo de dados - o que pode ser contra-intuitivo se você estiver familiarizado com a teoria dos grafos.

A documentação diz que os custos incluem todos os nós filhos, mas observe que o custo total do pai 3.39é muito menor do que o custo total do filho 15629.12. O custo total não é inclusivo porque um componente como LIMITnão precisa processar toda a sua entrada. Veja o EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;exemplo na documentação do PostgresEXPLAIN .

No exemplo acima, o tempo de inicialização é zero para ambos os componentes, porque nenhum dos componentes precisa fazer qualquer processamento antes de começar a gravar linhas: uma varredura sequencial lê a primeira linha da tabela e a emite. O LIMITlê sua primeira linha e a emite.

Quando um componente precisaria fazer muito processamento antes de começar a produzir qualquer linha? Existem várias razões possíveis, mas vamos ver um exemplo claro. Aqui está a mesma consulta de antes, mas agora contém uma ORDER BYcláusula:

EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;

Limit  (cost=23283.24..23283.37 rows=50 width=422)
  ->  Sort  (cost=23283.24..23859.27 rows=230412 width=422)
        Sort Key: body
        ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

E graficamente:

explicação gráfica da segunda consulta

Mais uma vez, a varredura sequencial ativada postnão tem custo inicial: ela começa a produzir linhas imediatamente. Mas a classificação tem um custo inicial significativo 23283.24porque precisa classificar a tabela inteira antes que possa produzir até mesmo uma única linha . O custo total da classificação 23859.27é apenas um pouco maior do que o custo inicial, refletindo o fato de que, uma vez que todo o conjunto de dados foi classificado, os dados classificados podem ser emitidos muito rapidamente.

Observe que o tempo de inicialização do LIMIT 23283.24é exatamente igual ao tempo de inicialização do tipo. Isso não ocorre porque LIMITele mesmo tem um alto tempo de inicialização. Na verdade, ele tem tempo de inicialização zero por si só, mas EXPLAINacumula todos os custos filhos de cada pai, de modo que o LIMITtempo de inicialização inclui a soma dos tempos de inicialização de seus filhos.

Esse acúmulo de custos pode dificultar o entendimento do custo de execução de cada componente individual. Por exemplo, nosso LIMITtem tempo de inicialização zero, mas isso não é óbvio à primeira vista. Por esse motivo, várias outras pessoas criaram um link para o explain.depesz.com , uma ferramenta criada por Hubert Lubaczewski (também conhecido como depesz) que ajuda a entender EXPLAIN, entre outras coisas, subtraindo os custos dos filhos dos custos dos pais. Ele menciona algumas outras complexidades em uma curta postagem no blog sobre sua ferramenta.

Mark E. Haase
fonte
4
Obrigado por isso. Eu também gostaria de adicionar um visualizador de explicação que faz um trabalho ainda melhor ao exibir a saída (imo). tatiyants.com/pev
Jonathan Porter
Ótima resposta. E o seu comentário de que o custo inicial inclui o tempo até a primeira linha retornada me ajudou a entender por que o custo inicial do Sort não era apenas 15629,12.
Joel Wigton,
43

Ele é executado do mais recuado para o menos recuado e, acredito, da parte inferior da planta para o topo. (Portanto, se houver duas seções recuadas, a que está mais abaixo na página é executada primeiro e, quando se encontram, a outra é executada e a regra que as une é executada.)

A ideia é que em cada etapa haja 1 ou 2 conjuntos de dados que chegam e são processados ​​por alguma regra. Se for apenas um conjunto de dados, a operação é feita para esse conjunto de dados. (Por exemplo, verifique um índice para descobrir quais linhas você deseja, filtre um conjunto de dados ou classifique-o.) Se dois, os dois conjuntos de dados são as duas coisas que são indentadas posteriormente e são unidas pela regra que você vê. O significado da maioria das regras pode ser razoavelmente fácil de adivinhar (especialmente se você leu um monte de planos de explicação antes), no entanto, você pode tentar verificar itens individuais olhando na documentação ou (mais fácil) apenas jogando a frase em Google junto com algumas palavras-chave como EXPLAIN.

Obviamente, essa não é uma explicação completa, mas fornece contexto suficiente para que você possa descobrir o que quiser. Por exemplo, considere este plano de um banco de dados real:

explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';

------------------------------------------------------------------------------------------------------------------------------------------------------------

 Merge Join  (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
   Merge Cond: (a.orderid = b.orderid)
   ->  Sort  (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
         Sort Key: a.orderid
         Sort Method:  quicksort  Memory: 1695kB
         ->  Bitmap Heap Scan on orderitemattribute a  (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
               Recheck Cond: ((attributeid)::text = 'display-album'::text)
               ->  Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
                     Index Cond: ((attributeid)::text = 'display-album'::text)
   ->  Sort  (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
         Sort Key: b.orderid
         Sort Method:  quicksort  Memory: 76kB
         ->  Bitmap Heap Scan on orderitem b  (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
               Recheck Cond: ((productid)::text = 'ModernBook'::text)
               ->  Bitmap Index Scan on id_orderitem_productid  (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
                     Index Cond: ((productid)::text = 'ModernBook'::text)
 Total runtime: 842.134 ms
(17 rows)

Tente ler você mesmo e veja se faz sentido.

O que eu li é que o banco de dados primeiro verifica o id_orderitem_productidíndice, usando-o para encontrar as linhas que deseja orderitem, depois classifica o conjunto de dados usando uma classificação rápida (a classificação usada mudará se os dados não couberem na RAM) e, em seguida, deixa isso de lado.

Em seguida, ele faz orditematt_attributeid_idxa varredura para encontrar as linhas que deseja orderitemattributee, em seguida, classifica esse conjunto de dados usando uma classificação rápida.

Em seguida, ele pega os dois conjuntos de dados e os mescla. (Uma união de mesclagem é uma espécie de operação de "compactação" em que percorre os dois conjuntos de dados classificados em paralelo, emitindo a linha unida quando eles correspondem.)

Como eu disse, você trabalha da parte interna à parte externa, de baixo para cima.

user11318
fonte
22

Também está disponível uma ferramenta auxiliar online, Depesz , que destacará onde estão as partes caras dos resultados da análise.

também tem um, aqui estão os mesmos resultados , que para mim tornam mais claro onde está o problema.

Stephen Denne
fonte
20
explain-analyze.info parece estar inativo, mas concordo que explain.depesz.com é muito útil.
benvolioT
13

O PgAdmin mostrará uma representação gráfica do plano de explicação. Alternar entre os dois pode realmente ajudá-lo a entender o que significa a representação de texto. No entanto, se você apenas deseja saber o que ele está fazendo, pode sempre usar a GUI.

Grant Johnson
fonte
0

Se você instalar o pgadmin, há um botão Explicar que além de fornecer a saída de texto desenha diagramas do que está acontecendo, mostrando os filtros, classificações e mesclagens de subconjuntos que considero muito úteis para ver o que está acontecendo.

Greg
fonte