Sei que adicionar uma restrição de chave estrangeira requer uma varredura de tabela e um SHARE ROW EXCLUSIVE
bloqueio em ambas as tabelas.
Para evitar a possível varredura de tabela longa, a restrição pode ser adicionada com a NOT VALID
abordagem. Mas estou pensando, ao adicionar uma nova coluna, você também deve usar NOT VALID
ou o Postgres é inteligente o suficiente para reconhecer que é uma nova coluna e, portanto, a tabela inteira não precisa ser varrida?
Estou usando Django, e o SQL gerado para adicionar uma coluna de chave estrangeira se parece com isto:
ALTER TABLE
"example"
ADD
COLUMN "new_column_id" integer NULL CONSTRAINT "example_new_column_id_fk" REFERENCES "another_table"("id") DEFERRABLE INITIALLY DEFERRED;
SET
CONSTRAINTS "example_new_column_id_b781b6be_fk" IMMEDIATE;
NOT VALID
só pode ser adicionado aALTER TABLE ... ADD CONSTRAINT
, nãoALTER TABLE ... ADD COLUMN
diretamente. O script SQL válido seria (possível em uma única transação):Testei isso no Postgres 16 bloqueando a tabela de destino no
ROW EXCLUSIVE
modo - o bloqueio mais leve que entra em conflito comSHARE ROW EXCLUSIVE
aquele que cria uma restrição FK assume na tabela de destino - em uma transação:Então executei o script acima em uma segunda transação simultânea.
O resultado: A segunda transação teve que esperar até que a primeira transação liberasse o
ROW EXCLUSIVE
bloqueio - com ou semNOT VALID
. Então, adicionar o FK leva a umSHARE ROW EXCLUSIVE
bloqueio na tabela de destino em qualquer caso.Pelo que Laurenz comentou, a verificação a seguir é muito rápida ou não é realizada devido a valores ausentes na coluna de referência.
Então:
NOT VALID
ele é desnecessário neste caso específico.NOT VALID
infelizmente não há como evitá-la.