Existem duas partes na minha pergunta.
- Existe uma maneira de especificar o tamanho inicial de um banco de dados no PostgreSQL?
- Se não houver, como você lida com a fragmentação quando o banco de dados cresce com o tempo?
Recentemente, migrei do MSSQL para o Postgres e uma das coisas que fizemos no mundo do MSSQL ao criar um banco de dados foi especificar o tamanho inicial do banco de dados e do log de transações. Isso reduziu a fragmentação e aumentou o desempenho, especialmente se o tamanho "normal" do banco de dados for conhecido previamente.
O desempenho do meu banco de dados diminui à medida que o tamanho aumenta. Por exemplo, a carga de trabalho pela qual estou passando normalmente leva 10 minutos. À medida que o banco de dados cresce, esse tempo aumenta. Fazer uma VÁCUO, VÁCUO CHEIO e VÁCUO CHEIO ANALISAR não parece resolver o problema. O que resolve o problema de desempenho é interromper o banco de dados, desagrupar a unidade e, em seguida, fazer uma ANÁLISE COMPLETA DE VÁCUO leva a execução do meu teste aos 10 minutos originais. Isso me leva a suspeitar que a fragmentação é o que está me causando dor.
Não consegui encontrar nenhuma referência para reservar espaço para tabelas / banco de dados no Postgres. Ou estou usando a terminologia errada e, portanto, não encontrando nada, ou existe uma maneira diferente de mitigar a fragmentação do sistema de arquivos no Postgres.
Alguma dica?
A solução
As respostas fornecidas ajudaram a confirmar o que eu comecei a suspeitar. O PostgreSQL armazena o banco de dados em vários arquivos e é isso que permite que o banco de dados cresça sem se preocupar com fragmentação. O comportamento padrão é compactar esses arquivos até a borda com dados da tabela, o que é bom para tabelas que raramente mudam, mas é ruim para tabelas atualizadas com frequência.
O PostgreSQL utiliza o MVCC para fornecer acesso simultâneo aos dados da tabela. Sob esse esquema, cada atualização cria uma nova versão da linha que foi atualizada (isso pode ser por carimbo de data ou hora, quem sabe?). Os dados antigos não são excluídos imediatamente, mas marcados para exclusão. A exclusão real ocorre quando uma operação VACUUM é executada.
Como isso se relaciona com o fator de preenchimento? O fator de preenchimento padrão da tabela de 100 compacta completamente as páginas da tabela, o que significa que não há espaço na página da tabela para conter linhas atualizadas, ou seja, linhas atualizadas serão colocadas em uma página de tabela diferente da linha original. Isso é ruim para o desempenho, como mostra minha experiência. Como minhas tabelas de resumo são atualizadas com muita frequência (até 1500 linhas / s), optei por definir um fator de preenchimento de 20, ou seja, 20% da tabela será para dados de linha inseridos e 80% para dados de atualização. Embora isso possa parecer excessivo, a grande quantidade de espaço reservado para as linhas atualizadas significa que as linhas atualizadas permanecem na mesma página que a original e existe uma página da tabela que não está cheia quando o daemon de autovacuum é executado para remover linhas obsoletas.
Para "consertar" meu banco de dados, fiz o seguinte.
- Defina o fator de preenchimento das minhas tabelas de resumo como 20. Você pode fazer isso no momento da criação passando um parâmetro para CREATE TABLE ou após o fato via ALTER TABLE. Emiti o seguinte comando plpgsql:
ALTER TABLE "my_summary_table" SET (fillfactor = 20);
- Emitiu um VACUUM FULL, pois isso grava uma versão completamente nova do arquivo de tabela e, por implicação, grava um novo arquivo de tabela com o novo fator de preenchimento .
Ao executar novamente meus testes, não vejo degradação no desempenho, mesmo quando o banco de dados é tão grande quanto eu preciso e com muitos milhões de linhas.
TL; DR - A fragmentação do arquivo não foi a causa, foi a fragmentação do espaço de tabela. Isso é atenuado, ajustando o fator de preenchimento da tabela para se adequar ao seu caso de uso específico.