Como substituir atomicamente os dados da tabela no PostgreSQL


14

Desejo substituir todo o conteúdo de uma tabela, sem afetar nenhuma SELECTdeclaração recebida durante o processo.

O caso de uso é ter uma tabela que armazena informações da caixa de correio extraídas regularmente e precisa ser armazenada em uma tabela do PostgreSQL. Existem muitos clientes usando um aplicativo que está constantemente consultando a mesma tabela.

Normalmente, eu faria algo como (pseudocódigo de entrada) ...

BEGIN TRANSACTION
TRUNCATE TABLE
INSERT INTO
COMMIT

Infelizmente, porém, a tabela não pode ser lida durante esse processo; devido ao tempo necessário INSERT INTOpara concluir. A mesa está trancada.

No MySQL, eu teria usado seu RENAME TABLEcomando atômico para evitar esses problemas ...

CREATE TABLE table_new LIKE table; 
INSERT INTO table_new;
RENAME TABLE table TO table_old, table_new TO table; *atomic operation*
DROP TABLE table_old;

Como eu consegui isso no PostgreSQL?

Para os fins desta pergunta, você pode assumir que não estou usando chaves estrangeiras.


Por que você acha que a tabela não pode ser lida ao inserir linhas nela? A tabela truncada terá efeito imediato em todas as sessões; no entanto, as inserções (se feitas dentro de uma transação que agrupe todas elas, como sugere seu pseudo-código) não serão visíveis para outras sessões até que você confirme. Outras sessões poderão selecionar na tabela e verão uma tabela vazia até você confirmar.
Zgguy

1
@zgguy o TRUNCATEcomando adquirirá um bloqueio AccessExclusive na tabela, para que ninguém mais possa ler a tabela até que a transação seja confirmada ou revertida.
Josh Kupershmidt

2
Se você usar em deletevez truncatedele, será mais lento, mas sem bloquear os leitores. Quantas linhas você precisa excluir?
A_horse_with_no_name

@a_horse_with_no_name Geralmente entre 200 e 300 mil linhas com muitas colunas varchar. O tempo de espera DELETEe INSERTseria muito longo.
Clarkey

Respostas:


20

Certo, o TRUNCATE TABLE comando que você está executando "... adquire um bloqueio ACCESS EXCLUSIVE em cada tabela em que opera "; portanto, no primeiro bloco SQL que você postou, quaisquer outros clientes que tentarem acessar a tabela após esse período serão bloqueados até que você INSERTtermine. e você COMMIT.

Você pode usar a mesma solução alternativa que no seu código específico do MySQL; O Postgres suporta aproximadamente a mesma sintaxe e terá um comportamento de bloqueio semelhante. A saber:

BEGIN;
-- You probably want to make sure that no one else is
-- INSERT / UPDATE / DELETE'ing from the original table, otherwise
-- those changes may be lost during this switchover process. One way
-- to do that would be via:
-- LOCK TABLE "table" IN ROW EXCLUSIVE mode;
CREATE TABLE "table_new" (LIKE "table");
INSERT INTO "table_new" ...;

-- The ALTER TABLE ... RENAME TO command takes an Access Exclusive lock on "table",
-- but these final few statements should be fast.
ALTER TABLE "table" RENAME TO "table_old";
ALTER TABLE "table_new" RENAME TO "table";
DROP TABLE "table_old";

COMMIT;

Bônus extra: O Postgres realmente suporta DDL transacional, ao contrário do MySQL, portanto, caso você precise ROLLBACK a transação acima, você pode fazê-lo com segurança.


Vou fazer alguns testes sobre isso, obrigado pela sua resposta. Se eu usasse o LOCK TABLEmétodo que você sugeriu, precisaria desbloqueá-lo novamente antes do COMMIT, ou será desbloqueado?
Clarkey

1
EDIT: Encontrou a seguinte declaração nesta documentação : "Não há comando UNLOCK TABLE; os bloqueios são sempre liberados no final da transação."
Clarkey

1
Uma coisa que falta aqui são todas as restrições anexadas que ainda pertencem a_old
Intellix 07/09/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.