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

Velocidade de truncamento do Postgresql


Isso surgiu algumas vezes recentemente, tanto no SO quanto nas listas de discussão do PostgreSQL.

O TL;DR para seus dois últimos pontos:

(a) Os buffers compartilhados maiores podem ser o motivo pelo qual TRUNCATE é mais lento no servidor CI. A configuração diferente do fsync ou o uso de mídia rotacional em vez de SSDs também podem estar com defeito.

(b) TRUNCATE table tem um custo fixo, mas não necessariamente mais lento que DELETE , além de dar mais trabalho. Veja a explicação detalhada a seguir.

ATUALIZAÇÃO: Uma discussão significativa sobre o desempenho do pgsql surgiu a partir deste post. Veja este tópico.

ATUALIZAÇÃO 2: Melhorias foram adicionadas ao 9.2beta3 que devem ajudar com isso, veja este post.

Explicação detalhada de TRUNCATE table vs DELETE FROM :

Embora não seja um especialista no assunto, meu entendimento é que TRUNCATE table tem um custo quase fixo por tabela, enquanto DELETE é pelo menos O(n) para n linhas; pior se houver alguma chave estrangeira referenciando a tabela que está sendo excluída.

Eu sempre presumi que o custo fixo de um TRUNCATE table foi menor que o custo de um DELETE em uma mesa quase vazia, mas isso não é verdade.

TRUNCATE table; faz mais do que DELETE FROM table;

O estado do banco de dados após uma tabela TRUNCATE table é o mesmo que se você executasse:
  • DELETE FROM table;
  • VACCUUM (FULL, ANALYZE) table; (somente 9.0+, veja nota de rodapé)

... embora é claro TRUNCATE table não atinge seus efeitos com um DELETE e um VACUUM .

O ponto é que DELETE e TRUNCATE table fazer coisas diferentes, então você não está apenas comparando dois comandos com resultados idênticos.

Uma tabela DELETE FROM table; permite que linhas mortas e bloat permaneçam, permite que os índices carreguem entradas mortas, não atualiza as estatísticas da tabela usadas pelo planejador de consulta, etc.

Um TRUNCATE table fornece uma tabela e índices completamente novos como se fossem apenas CREATE ed. É como se você excluísse todos os registros, reindexasse a tabela e fizesse um VACUUM FULL .

Se você não se importa se há sujeira na tabela porque você está prestes a preenchê-la novamente, talvez seja melhor usar DELETE FROM table; .

Porque você não está executando VACUUM você descobrirá que linhas mortas e entradas de índice se acumulam como inchaço que deve ser verificado e depois ignorado; isso atrasa todas as suas consultas. Se seus testes não criam e excluem tantos dados, você pode não notar ou se importar, e você sempre pode fazer um VACUUM ou dois no meio do seu teste, se você fizer isso. Melhor, deixe que as configurações agressivas de autovacuum garantam que o autovacuum faça isso por você em segundo plano.

Você ainda pode TRUNCATE table todas as suas tabelas depois do todo o conjunto de testes é executado para garantir que nenhum efeito se acumule em muitas execuções. Na versão 9.0 e mais recente, VACUUM (FULL, ANALYZE) table; globalmente na mesa é pelo menos tão bom se não melhor, e é muito mais fácil.

O IIRC Pg tem algumas otimizações que significam que ele pode perceber quando sua transação é a única que pode ver a tabela e marcar imediatamente os blocos como livres de qualquer maneira. Nos testes, quando quis criar bloat, tive que ter mais de uma conexão simultânea para fazer isso. Eu não confiaria nisso, no entanto.

DELETE FROM table; é muito barato para mesas pequenas sem referências f/k

Para DELETE todos os registros de uma tabela sem referências de chave estrangeira a ela, todo Pg tem que fazer uma varredura de tabela sequencial e definir o xmax das tuplas encontradas. Esta é uma operação muito barata - basicamente uma leitura linear e uma gravação semi-linear. AFAIK não precisa mexer nos índices; eles continuam a apontar para as tuplas mortas até que sejam limpas por um VACUUM posterior que também marca blocos na tabela contendo apenas tuplas mortas como livres.

DELETE só fica caro se houver muitos de registros, se houver muitas referências de chave estrangeira que devem ser verificadas, ou se você contar a tabela VACUUM (FULL, ANALYZE) table; necessário para corresponder a TRUNCATE table efeitos de dentro do custo de seu DELETE .

Em meus testes aqui, uma tabela DELETE FROM table; normalmente era 4x mais rápido que TRUNCATE table a 0,5 ms vs 2 ms. Esse é um banco de dados de teste em um SSD, rodando com fsync=off porque eu não me importo se eu perder todos esses dados. Claro, DELETE FROM table; não está fazendo o mesmo trabalho, e se eu acompanhar com uma tabela VACUUM (FULL, ANALYZE) table; é um 21ms muito mais caro, então o DELETE só é uma vitória se eu realmente não precisar da mesa impecável.

TRUNCATE table; faz muito mais trabalho de custo fixo e limpeza do que DELETE

Por outro lado, um TRUNCATE table tem que dar muito trabalho. Ele deve alocar novos arquivos para a tabela, sua tabela TOAST, se houver, e todos os índices que a tabela possui. Os cabeçalhos devem ser gravados nesses arquivos e os catálogos do sistema também podem precisar de atualização (não tenho certeza nesse ponto, não verifiquei). Em seguida, ele precisa substituir os arquivos antigos pelos novos ou remover os antigos e garantir que o sistema de arquivos acompanhe as alterações com uma operação de sincronização - fsync() ou similar - que geralmente libera todos os buffers no disco . Não tenho certeza se a sincronização foi ignorada se você estiver executando com a opção (comer dados) fsync=off .

Aprendi recentemente que TRUNCATE table também deve liberar todos os buffers do PostgreSQL relacionados à tabela antiga. Isso pode levar um tempo não trivial com enormes shared_buffers . Eu suspeito que é por isso que é mais lento no seu servidor CI.

O equilíbrio

De qualquer forma, você pode ver que um TRUNCATE table de uma tabela que tem uma tabela TOAST associada (a maioria tem) e vários índices pode demorar alguns instantes. Não muito, mas mais do que um DELETE de uma mesa quase vazia.

Conseqüentemente, talvez seja melhor fazer um DELETE FROM table; .

--

Nota:em bancos de dados anteriores a 9.0, CLUSTER table_id_seq ON table; ANALYZE table; ou tabela VACUUM FULL ANALYZE table; REINDEX table; seria um equivalente mais próximo de TRUNCATE table . O VACUUM FULL impl mudou para um muito melhor em 9.0.