A maneira mais eficiente de adicionar uma coluna serial a uma tabela enorme

10

Qual é a maneira mais rápida de adicionar uma coluna BIGSERIAL a uma tabela enorme (~ 3 bilhões de linhas, ~ 174 GB)?

EDITAR:

  • Eu quero que a coluna seja incrementada em valores para as linhas existentes ( NOT NULL).
  • Não defini um fator de preenchimento (que parece uma péssima decisão em retrospecto).
  • Não tenho problemas com espaço em disco, só quero que seja o mais rápido possível.
Thi Duong Nguyen
fonte

Respostas:

12

O que há de errado com:

ALTER TABLE foo ADD column bar bigserial;

Será preenchido com valores exclusivos automaticamente (começando com 1).

Se você deseja um número para cada linha existente, todas as linhas da tabela precisam ser atualizadas . Ou não?

A tabela ficará com o dobro do tamanho se não puder reutilizar tuplas mortas ou espaço livre nas páginas de dados. O desempenho da operação pode se beneficiar muito com FILLFACTORmenos de 100 ou apenas tuplas mortas aleatórias espalhadas sobre a mesa. Caso contrário, você poderá executar VACUUM FULL ANALYZEposteriormente para recuperar o espaço em disco. Isso não será rápido, no entanto.

pgstattuple
Você pode estar interessado nesta extensão. Ajuda a reunir estatísticas em suas tabelas. Para descobrir sobre tuplas mortas e espaço livre:

Instale a extensão uma vez por banco de dados:

CREATE EXTENSION pgstattuple;

Ligar:

SELECT * FROM pgstattuple('tbl');

Alternativa

Se você puder se dar ao luxo de criar uma nova tabela, que quebraria dependendo de visualizações, chaves estrangeiras, ...

Crie uma cópia vazia da tabela antiga:

CREATE new_tbl AS
SELECT *
FROM   old_tbl
LIMIT  0;

Adicione a coluna bigserial:

ALTER new_tbl ADD column bar bigserial;

INSERIR dados da tabela antiga, preenchendo automaticamente o bigserial:

INSERT INTO new_tbl
SELECT *    --  new column will be filled with default
FROM   old_tbl
ORDER  BY something; -- or don't order if you don't care: faster

A nova coluna bigserial está ausente no SELECT do INSERT e será preenchida com seu valor padrão automaticamente . Você pode especificar todas as colunas e adicionar nextval()à SELECTlista com o mesmo efeito.

Certifique-se de ter todos os seus dados na nova tabela.
Adicione índices, restrições, gatilhos que você tinha na tabela antiga agora .

DROP TABLE old_tbl;
ALTER TABLE new_tbl RENAME TO old_tbl;

No geral, pode ser um pouco mais rápido. Isso deixa você com uma tabela de baunilha (e índices) sem inchaço.

Você precisa de espaço livre em disco - em torno do tamanho da tabela antiga, dependendo do estado da tabela - como sala de manobra. Mas você pode precisar tanto do primeiro método simples por causa do inchaço da tabela. Novamente, os detalhes dependem do estado da sua tabela.

Erwin Brandstetter
fonte
3
Embrulhe a alternativa em uma única transação, que será muito mais rápida. Ele vai evitar adicionais fsyncs
Frank Heikens