Tenho problemas para adicionar uma nova coluna em uma tabela.
Tentei executá-lo algumas vezes, mas depois de mais de 10 minutos em execução, decidi cancelar a consulta devido ao tempo de bloqueio.
ALTER TABLE mytable ADD mycolumn VARCHAR(50);
Informação útil:
- Versão do PostgreSQL: 9.1
- Número de linhas: ~ 250K
- Número de colunas: 38
- Número de colunas anuláveis: 32
- Número de restrições: 5 (1 PK, 3 FK, 1 ÚNICO)
- Número de índices: 1
- Tipo de SO: Debian Squeeze 64
Encontrei informações interessantes sobre a maneira como o PostgreSQL gerencia colunas anuláveis (via HeapTupleHeader).
Meu primeiro palpite é que, como essa tabela já possui 32 colunas anuláveis com 8 bits MAXALIGN
, o HeapTupleHeader tem 4 bytes de comprimento (não verificado e não sei como fazê-lo).
Portanto, a adição de uma nova coluna anulável pode precisar de uma atualização do HeapTupleHeader em cada linha para adicionar um novo 8 bits MAXALIGN
, o que pode causar problemas de desempenho.
Então, tentei alterar uma das colunas anuláveis (que não é realmente anulável na realidade) para diminuir para 31 o número de colunas anuláveis, para verificar se meu palpite poderia ser verdadeiro.
ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;
Infelizmente, essa alteração também leva muito tempo, mais de 5 minutos, então eu também a abortei.
Você tem uma idéia do que poderia causar esse custo de desempenho?
fonte
SET NOT NULL
não altera o tipo, apenas adiciona uma restrição - mas a restrição deve ser verificada na tabela e isso exige uma verificação completa da tabela. A versão 9.4 melhora alguns desses casos com bloqueios mais fracos, mas ainda é bastante pesado.Respostas:
Existem alguns mal-entendidos aqui:
O mapa de bits nulo é não parte do cabeçalho montão tuplo. Por documentação:
Suas 32 colunas anuláveis não são suspeitas por dois motivos:
O bitmap nulo é adicionado por linha e somente se houver pelo menos um
NULL
valor real na linha. As colunas anuláveis não têm impacto direto, apenas osNULL
valores reais . Se o bitmap nulo estiver alocado, ele sempre será alocado completamente (tudo ou nada). O tamanho real do bitmap nulo é de 1 bit por coluna, arredondado para o próximo byte . Por código de fonte atual:O bitmap nulo é alocado após o cabeçalho da tupla de heap e seguido por um OID opcional e dados da linha. O início de um OID ou dados de linha é indicado por
t_hoff
no cabeçalho. Por código fonte do comentário :Há um byte livre após o cabeçalho da tupla de heap, que ocupa 23 bytes. Portanto, o bitmap nulo para linhas de até 8 colunas efetivamente não tem custo adicional. Com a 9ª coluna da tabela,
t_hoff
são avançados outrosMAXALIGN
bytes (normalmente 8) para fornecer outras 64 colunas. Portanto, a próxima borda teria 72 colunas.Para exibir informações de controle de um cluster de banco de dados PostgreSQL (incl.
MAXALIGN
), Exemplo para uma instalação típica do Postgres 9.3 em uma máquina Debian:Atualizei as instruções na resposta relacionada que você citou .
Tudo isso à parte, mesmo que sua
ALTER TABLE
instrução desencadeie uma reescrita de uma tabela inteira (o que provavelmente ocorre, alterando um tipo de dados), 250K não são muito e seria uma questão de segundos em qualquer máquina decente (a menos que as linhas sejam extraordinariamente grandes) . 10 minutos ou mais indicam um problema completamente diferente. O seu extrato está aguardando um bloqueio na mesa, provavelmente.O crescente número de entradas
pg_stat_activity
significa mais transações abertas - indica acesso simultâneo à tabela (provavelmente) que precisa aguardar a conclusão da operação.Alguns tiros no escuro
Verifique se há um inchaço na tabela, tente um método
VACUUM mytable
mais suave ou mais agressivoVACUUM FULL mytable
- que pode encontrar os mesmos problemas de simultaneidade, pois esse formulário também adquire um bloqueio exclusivo. Você pode tentar o pg_repack ...Começaria por inspecionar possíveis problemas com índices, gatilhos, chave estrangeira ou outras restrições, especialmente as que envolvem a coluna. Especialmente um índice corrompido pode estar envolvido? Tente
REINDEX TABLE mytable;
ouDROP
todos eles e adicione-os novamenteALTER TABLE
na mesma transação .Tente executar o comando à noite ou sempre que não houver muita carga.
Um método de força bruta seria interromper o acesso ao servidor e tente novamente:
Sem poder identificá-lo, a atualização para a versão atual ou a 9.4 futura em particular pode ajudar. Houve várias melhorias para tabelas grandes e para detalhes de bloqueio. Mas se houver algo quebrado no seu banco de dados, você provavelmente deve descobrir isso primeiro.
fonte