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.