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

Entendendo o desempenho de consultas do PostgreSQL


Descobrir por que uma consulta com bom desempenho no desenvolvimento e no teste aumenta a produção às vezes pode ser um desafio. Continue lendo para saber mais sobre alguns recursos que podem fornecer informações sobre como suas consultas se saem na produção.

Consultas em execução no momento


Quando um cliente conecta um servidor PostgreSQL, o processo principal do servidor Postgres (historicamente chamado de postmaster ) gera um novo processo (chamado debackend ) para atender as consultas do cliente. Cada back-end, portanto, está esperando que seu cliente envie uma consulta ou tentando executar uma.

A visualização do sistema pg_stat_activity mostra informações sobre cada backend que está em execução no momento. Em particular, ele mostra a consulta que o backend está executando no momento se estiver ativo, ou a última consulta executada se estiver aguardando o cliente enviar outra consulta.

Aqui estão dois back-ends atendendo clientes conectados ao banco de dados testdb , com ambos executando ativamente suas consultas:
testdb=# select usename,datname,state,query from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state   | active
query   | SELECT pg_sleep(10);
-[ RECORD 2 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state   | active
query   | select usename,datname,state,query from pg_stat_activity where datname='testdb';

Às vezes, a consulta pode estar esperando por um bloqueio, e isso também aparece inpg_stat_activity. Você pode ver um INSERT esperando por um bloqueio de relação aqui:
testdb=# select wait_event_type, wait_event, left(query, 60) from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]---+-------------------------------------------------------------
wait_event_type | Client
wait_event      | ClientRead
left            | lock table t in access exclusive mode;
-[ RECORD 2 ]---+-------------------------------------------------------------
wait_event_type |
wait_event      |
left            | select wait_event_type, wait_event, left(query, 60) from pg_
-[ RECORD 3 ]---+-------------------------------------------------------------
wait_event_type | Lock
wait_event      | relation
left            | insert into t values (1);

Para obter mais informações sobre pg_stat_activity, consulte a documentação.

Embora essa visualização seja útil para entender o que o Postgres está fazendo atualmente, ela não fornece informações sobre estatísticas de execução de consultas ou informações sobre consultas que terminaram a execução.

Todas as consultas executadas no passado


Para isso, a extensão pg_stat_statements é inestimável. Essa extensão está incluída na distribuição central do PostgreSQL e também está disponível em serviços gerenciados como AWS RDS e GCP SQL.

pg_stat_statements (PSS) é uma “extensão” nos termos do PostgreSQL e precisa ser instalado primeiro:
  • Consulte a documentação da sua distribuição Linux para ver se a extensão está pré-instalada ou se requer a instalação de outro pacote. Por exemplo, no Centos 7 você precisará sudo yum install postgresql-contrib .
  • Edite o arquivo de configuração principal postgresql.conf (normalmente em /etc , como/etc/postgresql/10/main/postgresql.conf no Debian) e altere o valor de shared_preload_libraries para “pg_stat_statements”. Esta é uma lista de valores separados por vírgulas, portanto, se já houver algo lá, acrescente uma vírgula e depois “pg_stat_statements”.
  • Para o AWS RDS, você precisará modificar seu grupo de parâmetros ativo e definir o valor.
  • Depois de editar “shared_preload_libraries”, você precisará reiniciar o daemon PostgreSQL. Infelizmente não há como contornar isso. No AWS RDS, você precisará reiniciar a instância do RDS.
  • Após uma reinicialização, o servidor PostgreSQL teria carregado a biblioteca compartilhada e podemos instalar a extensão executando CREATE EXTENSION pg_stat_statements . Você precisará ser um superusuário para executar este comando.
  • Você pode instalar a extensão em qualquer banco de dados e ainda ver as consultas em todos os bancos de dados.

Após a instalação da extensão, você pode consultar a visualização chamadapg_stat_statements para obter informações sobre cada consulta executada desde que a extensão foi instalada.

Os números, como o tempo gasto para executar a consulta, são acumulados como uma soma. Apenas para o tempo de execução da consulta, algumas estatísticas (média, min, max, desvio padrão) são apresentadas. Esses valores podem ser apagados usando a funçãopg_stat_statements_reset .

Veja como uma linha de pg_stat_statements parece:
testdb=# select * from pg_stat_statements where query like '%pg_sleep%' and dbid=42548;
-[ RECORD 1 ]-------+--------------------
userid              | 10
dbid                | 42548
queryid             | 2649515222348904837
query               | SELECT pg_sleep($1)
calls               | 1
total_time          | 10016.782625
min_time            | 10016.782625
max_time            | 10016.782625
mean_time           | 10016.782625
stddev_time         | 0
rows                | 1
shared_blks_hit     | 0
shared_blks_read    | 0
shared_blks_dirtied | 0
shared_blks_written | 0
local_blks_hit      | 0
local_blks_read     | 0
local_blks_dirtied  | 0
local_blks_written  | 0
temp_blks_read      | 0
temp_blks_written   | 0
blk_read_time       | 0
blk_write_time      | 0

Além dos parâmetros de identificação (usuário, banco de dados, consulta), você pode descobrir muitas coisas interessantes sobre sua consulta:
  • Quanto tempo leva para executar normalmente (mean_time )
  • Quantas linhas ele retorna em média (rows / calls )
  • A quantidade de dados lidos do cache de buffer compartilhado e a quantidade de dados lidos do disco (o shared_blks_read mostra a quantidade total de dados que a consulta leu, dos quais shared_blks_hit veio do cache)
  • A quantidade de dados que teve que ser gravada no disco de forma síncrona devido à pressão do cache (shared_blks_written )
  • A quantidade de dados gravados, como o número de blocos tocados(shared_blks_dirtied )
  • A quantidade de tempo gasto em leituras e gravações de disco (blk_{read,write}_time )
  • Arquivos temporários gravados e lidos de (temp_blks_{read,written} )
  • Tabelas temporárias gravadas e lidas de (local_* )

Os tempos de leitura e gravação do disco estão disponíveis somente se o parâmetro de configuraçãotrack_io_timing está ligado. Por padrão, não é. Na maioria dos sistemas Linux modernos, não há problema em ativar esse parâmetro. Consulte Mais informação.

Vale a pena fazer um instantâneo das pg_stat_statements dados continuamente em intervalos regulares para ver como esses parâmetros estão tendendo por consulta. A ferramenta de código aberto pgmetrics pode extrair e expor as pg_stat_statements dados como JSON para automação mais fácil.

Consultas executadas durante um intervalo de tempo


Uma vez que você tenha um sistema desse tipo, fica fácil rastrear as consultas executadas em um determinado período de tempo. Isso facilita a depuração de problemas, como por que um trabalho em lote noturno demorou mais do que o esperado.

Ao subtrair os contadores entre dois timestamps fornecidos, você pode descobrir a maioria dos números como antes, exceto o mínimo, o máximo e o desvio padrão. Isso é suficiente para identificar as consultas que foram executadas dentro do intervalo de tempo e os recursos que consumiram.

Registrando consultas lentas


Outra forma de identificar rapidamente as consultas que demoram mais do que o esperado é ativar o registro de instruções. Você pode especificar uma duração limite e, se a consulta demorar mais do que isso para ser concluída, ela será registrada. (No arquivo de log regular do PostgreSQL, não há um arquivo separado para consultas lentas.)

Para ativar esse recurso, edite a configuração conforme abaixo:
log_min_duration_statement = 1000 # in milliseconds

e recarregue o Postgres. Você também pode usar ALTER SYSTEM :
ALTER SYSTEM SET log_min_duration_statement = 1000; -- in milliseconds

Com isso, qualquer instrução (incluindo as não-DML) que demore mais de um segundo para terminar é registrada:
2019-12-02 16:57:05.727 UTC [8040] postgres@testdb LOG:  duration: 10017.862 ms  statement: SELECT pg_sleep(10);

O tempo real gasto pela consulta, bem como o texto SQL completo, é registrado.

Se você possui um sistema de monitoramento de logs e consegue acompanhar o número de consultas lentas por hora/dia, ele pode servir como um bom indicador de desempenho do aplicativo.

Planos de execução de consultas


Depois de localizar uma consulta que você acha que deve ser executada mais rapidamente, o próximo passo é dar uma olhada no plano de consulta. Normalmente, você precisa do plano de consulta real dos servidores de produção para trabalhar. Se você é capaz de executar EXPLAIN em servidores de produção tão bons, senão você precisa confiar em auto_explain .

auto_explain é outra extensão central do PostgreSQL, já instalada ou disponível como um pacote “contrib” para sua distro. Também está disponível no AWSRDS. auto_explain é um pouco mais simples de instalar do que pg_stat_statements :
  • Edite a configuração do postgres (ou o grupo de parâmetros RDS)shared_preload_libraries para incluir auto_explain .
  • Você não precisa reiniciar o Postgres, você pode apenas executar:LOAD 'auto_explain'; .
  • Você vai querer definir suas configurações, pelo menos esta:
    • auto_explain.log_min_duration = 1000 # seconds

Essencialmente, sempre que uma consulta demorar mais queauto_explain.log_min_duration número de segundos para concluir, auto_explainlogs a consulta e seu plano de execução da consulta no arquivo de log, assim:
2019-12-04 09:23:05.130 UTC [12823] postgres@testdb LOG:  duration: 11025.765 ms  plan:
        Query Text: select pg_sleep(11);
        Result  (cost=0.00..0.01 rows=1 width=4) (actual time=11025.716..11025.718 rows=1 loops=1)
          Output: pg_sleep('11'::double precision)

Ele também pode registrar o plano no formato JSON, se você tiver scripts que possam processá-lo:
2019-12-02 17:30:53.676 UTC [8040] postgres@testdb LOG:  duration: 10000.230 ms  plan:
        {
          "Query Text": "SELECT pg_sleep(10);",
          "Plan": {
            "Node Type": "Result",
            "Parallel Aware": false,
            "Startup Cost": 0.00,
            "Total Cost": 0.01,
            "Plan Rows": 1,
            "Plan Width": 4,
            "Actual Startup Time": 10000.205,
            "Actual Total Time": 10000.206,
            "Actual Rows": 1,
            "Actual Loops": 1,
            "Output": ["pg_sleep('10'::double precision)"],
            "Shared Hit Blocks": 0,
            "Shared Read Blocks": 0,
            "Shared Dirtied Blocks": 0,
            "Shared Written Blocks": 0,
            "Local Hit Blocks": 0,
            "Local Read Blocks": 0,
            "Local Dirtied Blocks": 0,
            "Local Written Blocks": 0,
            "Temp Read Blocks": 0,
            "Temp Written Blocks": 0,
            "I/O Read Time": 0.000,
            "I/O Write Time": 0.000
          },
          "Triggers": [
          ]
        }

No Postgres, não há outra maneira além de auto_explain de olhar para o plano de execução de uma consulta que já foi executada, o que torna o auto_explain uma ferramenta importante em sua caixa de ferramentas.