PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

PostgreSQL EXPLAIN – Quais são os custos da consulta?

Compreendendo o custo EXPLAIN do Postgres


EXPLAIN é muito útil para entender o desempenho de uma consulta do Postgres. Ele retorna o plano de execução gerado pelo planejador de consultas do PostgreSQL para uma determinada instrução. A EXPLAIN O comando especifica se as tabelas referenciadas em uma instrução serão pesquisadas usando uma varredura de índice ou uma varredura sequencial.

Algumas das primeiras coisas que você notará ao revisar a saída de um EXPLAIN command são as estatísticas de custo, então é natural se perguntar o que elas significam, como são calculadas e como são usadas.

Resumindo, o planejador de consultas do PostgreSQL está estimando quanto tempo a consulta levará (em uma unidade arbitrária), com um custo inicial e um custo total para cada operação. Mais sobre isso mais tarde. Quando ele tem várias opções para executar uma consulta, ele usa esses custos para escolher a opção mais barata e, portanto, mais rápida.

Em que unidade estão os custos?


Os custos estão em uma unidade arbitrária. Um mal-entendido comum é que eles estão em milissegundos ou alguma outra unidade de tempo, mas esse não é o caso.

As unidades de custo são ancoradas (por padrão) a uma única leitura de página sequencial que custa 1,0 unidade (seq_page_cost ). Cada linha processada adiciona 0,01 (cpu_tuple_cost ), e cada leitura de página não sequencial adiciona 4.0 (random_page_cost ). Existem muitas outras constantes como esta, todas configuráveis. Esse último é um candidato particularmente comum, pelo menos em hardware moderno. Nós vamos olhar mais para isso daqui a pouco.

Custos de Inicialização


Os primeiros números que você vê depois de cost= são conhecidos como o “custo inicial”. Esta é uma estimativa de quanto tempo levará para buscar a primeira linha . Como tal, o custo inicial de uma operação inclui o custo de seus filhos.

Para uma varredura sequencial, o custo de inicialização geralmente será próximo de zero, pois pode começar a buscar linhas imediatamente. Para uma operação de classificação, será maior porque uma grande parte do trabalho precisa ser feita antes que as linhas possam começar a ser retornadas.

Para ver um exemplo, vamos criar uma tabela de teste simples com 1000 nomes de usuário:
CREATE TABLE users ( id bigint GERADO SEMPRE COMO IDENTITY PRIMARY KEY, username text NOT NULL);INSERT INTO users (username)SELECT 'person' || nFROM generate_series(1, 1000) AS n;ANALISAR usuários;

Vamos dar uma olhada em um plano de consulta simples, com algumas operações:
EXPLAIN SELECT * FROM users ORDER BY username;QUERY PLAN |----------------------------------- ---------------------------+Classificar (custo=66,83..69,33 linhas=1000 largura=17) | Chave de classificação:nome de usuário | -> Seq Scan em usuários (cost=0.00..17.00 rows=1000 width=17)|

No plano de consulta acima, como esperado, o custo estimado de execução da instrução para o Seq Scan é 0.00 , e para o Sort é 66.83 .

Custos totais


A segunda estatística de custo, após o custo inicial e os dois pontos, é conhecida como “custo total”. Esta é uma estimativa de quanto tempo levará para retornar todas as linhas .

Vejamos o plano de consulta de exemplo novamente:
PLANO DE CONSULTAS |------------------------------------------------------- ------------------+Classificar (custo=66,83..69,33 linhas=1000 largura=17) | Chave de classificação:nome de usuário | -> Seq Scan em usuários (cost=0.00..17.00 rows=1000 width=17)|

Podemos ver que o custo total do Seq Scan a operação é 17.00 . Para o Sort operação é 69,33, que não é muito mais do que seu custo inicial (como esperado).

Os custos totais geralmente incluem o custo das operações que os precedem. Por exemplo, o custo total da operação Sort acima inclui o da Seq Scan.

Uma exceção importante é LIMIT cláusulas, que o planejador usa para estimar se pode abortar mais cedo. Se ele precisar apenas de um pequeno número de linhas, cujas condições são comuns, ele pode calcular que uma escolha de varredura mais simples é mais barata (provavelmente mais rápida).

Por exemplo:
EXPLAIN SELECT * FROM users LIMIT 1;QUERY PLAN |------------------------------------ --------------------------+Limite (custo=0.00..0.02 linhas=1 largura=17) | -> Seq Scan em usuários (cost=0.00..17.00 rows=1000 width=17)|

Como você pode ver, o custo total relatado no nó Seq Scan ainda é 17,00, mas o custo total da operação Limit é relatado como 0,02. Isso ocorre porque o planejador espera que ele tenha que processar apenas 1 linha de 1.000, então o custo, neste caso, é estimado em 1.000 do total.

Como os custos são calculados


Para calcular esses custos, o planejador de consultas do Postgres usa tanto constantes (algumas das quais já vimos) quanto metadados sobre o conteúdo do banco de dados. Os metadados são muitas vezes referidos como “estatísticas”.

As estatísticas são coletadas por meio de ANALYZE (não deve ser confundido com o EXPLAIN parâmetro de mesmo nome) e armazenado em pg_statistic . Eles também são atualizados automaticamente como parte do autovacuum.

Essas estatísticas incluem várias coisas muito úteis, como aproximadamente o número de linhas que cada tabela possui e quais são os valores mais comuns em cada coluna.

Vejamos um exemplo simples, usando os mesmos dados de consulta de antes:
EXPLAIN SELECT count(*) FROM users;QUERY PLAN |------------------------------------------------- --------------------------+Agregado (custo=19.50..19.51 linhas=1 largura=8) | -> Seq Scan em usuários (cost=0.00..17.00 rows=1000 width=0)|

No nosso caso, as estatísticas do planejador sugeriram que os dados da tabela foram armazenados em 7 páginas (ou blocos) e que 1000 linhas seriam retornadas. Os parâmetros de custo seq_page_cost , cpu_tuple_cost e cpu_operator_cost foram deixados em seus padrões de 1 , 0.01 e 0.0025 respectivamente.

Assim, o custo total do Seq Scan foi calculado como:
Custo total de Seq Scan=(leituras de páginas sequenciais estimadas * seq_page_cost) + (linhas estimadas retornadas * cpu_tuple_cost)=(7 * 1) + (1000 * 0,01)=7 + 10,00=17,00

E para o Agregado como:
Custo total de Aggregate=(custo de Seq Scan) + (linhas estimadas processadas * cpu_operator_cost) + (linhas estimadas retornadas * cpu_tuple_cost)=(17,00) + (1000 * 0,0025) + (1 * 0,01) =17,00 + 2,50 + 0,01=19,51 

Como o planejador usa os custos


Como sabemos que o Postgres escolherá o plano de consulta com o menor custo total, podemos usá-lo para tentar entender as escolhas feitas. Por exemplo, se uma consulta não estiver usando um índice que você espera, você pode usar configurações como enable_seqscan para desencorajar maciçamente certas opções de plano de consulta. A essa altura, você não deve se surpreender ao saber que configurações como essa funcionam aumentando os custos!
Os números de linha são uma parte extremamente importante da estimativa de custos. Eles são usados ​​para calcular estimativas para diferentes ordens de junção, algoritmos de junção, tipos de varredura e muito mais. As estimativas de custo de linha que estão muito atrasadas podem levar a que as estimativas de custo sejam muito atrasadas, o que pode resultar em uma escolha de plano abaixo do ideal.

Usando EXPLAIN ANALYZE para obter um plano de consulta


Quando você escreve instruções SQL no PostgreSQL, a função ANALYZE O comando é fundamental para otimizar as consultas, tornando-as mais rápidas e eficientes. Além de exibir o plano de consulta e as estimativas do PostgreSQL, o EXPLAIN ANALYZE opção executa a consulta (cuidado com UPDATE e DELETE !), e mostra o tempo de execução real e o número de contagem de linhas para cada etapa do processo de execução. Isso é necessário para monitorar o desempenho do SQL.

Você pode usar EXPLAIN ANALYZE para comparar o número estimado de linhas com as linhas reais retornadas por cada operação.

Vejamos um exemplo, usando os mesmos dados novamente:
PLANO DE CONSULTAS |------------------------------------------------------- -------------------------------------------------- -------------+Classificar (custo=66,83..69,33 linhas=1000 largura=17) (tempo real=20,569..20,684 linhas=1000 loops=1) | Chave de classificação:nome de usuário | Método de classificação:quicksort Memória:102kB | -> Seq Scan em usuários (custo=0,00..17.00 linhas=1000 largura=17) (tempo real=0,048..0.596 linhas=1000 loops=1)|Tempo de planejamento:0,171 ms |Tempo de execução:20,793 ms | 
Podemos ver que o custo total de execução ainda é 69,33, sendo a maior parte da operação Sort, e 17,00 vindo do Sequential Scan. Observe que o tempo de execução da consulta é pouco menos de 21ms.

Verificação sequencial x Varredura de índice


Agora, vamos adicionar um índice para tentar evitar esse tipo caro de toda a tabela:
​​CREATE INDEX people_username_idx ON users (username);EXPLAIN ANALYZE SELECT * FROM users ORDER BY username;QUERY PLAN |----------------------- -------------------------------------------------- -------------------------------------------------- ------+Index Scan usando people_username_idx em usuários (custo=0,28..28.27 linhas=1000 largura=17) (tempo real=0,052..1.494 linhas=1000 loops=1)|Tempo de planejamento:0,186 ms |Execução Tempo:1,686 ms |

Como você pode ver, o planejador de consultas agora escolheu um Index Scan, pois o custo total desse plano é 28,27 (inferior a 69,33). Parece que a verificação de índice foi mais eficiente do que a verificação sequencial, pois o tempo de execução da consulta agora está abaixo de 2 ms.

Ajudando o planejador a estimar com mais precisão


Podemos ajudar o planejador a estimar com mais precisão de duas maneiras:
  1. Ajude-o a coletar estatísticas melhores
  2. Ajuste as constantes usadas para os cálculos

As estatísticas podem ser especialmente ruins após uma grande alteração nos dados de uma tabela. Assim, ao carregar muitos dados em uma tabela, você pode ajudar o Postgres executando um manual ANALYZE nele. As estatísticas também não persistem em uma atualização de versão principal, então esse é outro momento importante para fazer isso.

Naturalmente, as tabelas também mudam com o tempo, portanto, ajustar as configurações de autovacuum para garantir que ele seja executado com frequência suficiente para sua carga de trabalho pode ser muito útil.

Se você está tendo problemas com estimativas ruins para uma coluna com uma distribuição distorcida, você pode se beneficiar aumentando a quantidade de informações que o Postgres coleta usando o ALTER TABLE SET STATISTICS comando, ou mesmo o default_statistics_target para todo o banco de dados.

Outra causa comum de estimativas ruins é que, por padrão, o Postgres assumirá que duas colunas são independentes. Você pode corrigir isso solicitando que ele colete dados de correlação em duas colunas da mesma tabela por meio de estatísticas estendidas.

Na frente de ajuste constante, há muitos parâmetros que você pode ajustar para se adequar ao seu hardware. Supondo que você esteja executando em SSDs, você provavelmente desejará no mínimo ajustar sua configuração de random_page_cost . O padrão é 4, que é 4x mais caro que o seq_page_cost vimos anteriormente. Essa proporção fazia sentido em discos giratórios, mas em SSDs tende a penalizar demais a E/S aleatória. Como tal, uma configuração mais próxima de 1, ou entre 1 e 2, pode fazer mais sentido. No ScaleGrid, o padrão é 1.

Posso remover os custos dos planos de consulta?


Por muitos dos motivos mencionados acima, a maioria das pessoas deixa os custos ao executar EXPLAIN . No entanto, se desejar, você pode desativá-los usando o COSTS parâmetro.
EXPLAIN (COSTS OFF) SELECT * FROM users LIMIT 1;QUERY PLAN |-----------------------+Limit | -> Seq Scan em usuários|

Conclusão


Para recapitular, os custos nos planos de consulta são estimativas do Postgres de quanto tempo uma consulta SQL levará, em uma unidade arbitrária.

Ele escolhe o plano com o menor custo geral, com base em algumas constantes configuráveis ​​e em algumas estatísticas coletadas.

Ajudá-lo a estimar esses custos com mais precisão é muito importante para ajudá-lo a fazer boas escolhas e manter o desempenho de suas consultas.

Interessado em saber mais sobre ScaleGrid?

Para saber mais sobre como o ScaleGrid pode ajudá-lo a gerenciar seus bancos de dados, entre em contato conosco e mostraremos tudo o que nosso DBaaS tem a oferecer. Saiba mais sobre como o ScaleGrid pode permitir que você se concentre mais no desenvolvimento de seu produto e menos no gerenciamento de bancos de dados.