Meu entendimento é que uma atualização bloqueia uma tupla, marca-a como excluída e adiciona uma nova tupla.
Em outras palavras, update = delete + insert.
Ou então eu tinha acreditado. Mas parece que há algo fundamentalmente diferente sobre atualização de delete + insert no MVCC.
Configurar:
CREATE TABLE example (a int PRIMARY KEY, b int);
INSERT INTO example VALUES (1, 1);
Método 1: Atualizar
-- session A session B
BEGIN;
UPDATE example SET b = 2 WHERE a = 1;
DELETE FROM example WHERE a = 1;
COMMIT;
-- now there are 0 rows in table example (1 row was deleted by session B)
Método 2: Excluir e inserir
-- session A session B
BEGIN;
DELETE FROM example WHERE a = 1;
INSERT INTO example VALUES (1, 2);
DELETE FROM example WHERE a = 1;
COMMIT;
-- now there is 1 row in table example (0 rows deleted by session B)
Desta forma
UPDATE example SET b = 2 WHERE a = 1;
é diferente de
DELETE FROM example WHERE a = 1;
INSERT INTO example VALUES (1, 2);
Como devo entender a natureza da atualização do MVCC? A tupla tem algum tipo de "identidade" MVCC que é preservada durante a atualização? O que é isso?
Sim, há uma diferença entre
UPDATE
eDELETE
+INSERT
.Vamos usar a
pageinspect
extensão para ver as tuplas e os cabeçalhos das tuplas.Se você quiser repetir meu experimento, terá que descartar e recriar a tabela no meio. Além disso, pode haver sinalizadores adicionais (bits de dica) se você selecionou as linhas antes de examiná-las.
O significado de
infomask2
einfomask
pode ser encontrado emsrc/include/access/htup_details.h
, veja as citações no final da resposta.Após o
UPDATE
:A primeira tupla é a morta. Seu
t_ctid
foi alterado para apontar para a versão atualizada.Este é um dos pontos-chave, então deixe-me expandir isso: o de uma tupla
ctid
é a combinação do número do bloco e o “ponteiro de linha” (lp
no resultado da consulta.t_ctid
apontar para a nova versão de linha. Este é o link entre a tupla original e a versão atualizada.t_infomask2
é 2 (o número de colunas) maisHEAP_HOT_UPDATED
, então esta linha recebeu uma atualização HOT (havia espaço suficiente no bloco e não há índice).t_infomask
éHEAP_XMIN_COMMITTED
(um pouco de dica).A segunda tupla é a nova versão.
t_infomask2
é 2 plusHEAP_ONLY_TUPLE
, portanto, esta é uma “tupla somente de heap” que só pode ser acessada por meio da atualizaçãoctid
da versão antiga.t_infomask
éHEAP_XMAX_INVALID
(true, é 0) maisHEAP_UPDATED
(esta é a versão atualizada).Depois do
DELETE
+INSERT
:Novamente, a primeira tupla é a morta.
t_infomask2
é 2 maisHEAP_KEYS_UPDATED
(esta é uma tupla excluída ou atualizada) et_infomask
éHEAP_XMIN_COMMITTED
(a tupla era válida antes de ser excluída).A segunda tupla é a inserida:
t_infomask2
é 2 mais, et_infomask
éHEAP_XMAX_INVALID
(é 0), então esta é uma nova tupla.Explicação da diferença observada:
No
READ COMMITTED
nível de isolamento, uma transação sempre vê a última versão confirmada de uma linha. ODELETE
na sessão B tem que bloquear a linha e é bloqueado peloUPDATE
ouDELETE
na sessão A.A documentação explica o que acontece quando o bloqueio é liberado:
No caso de
UPDATE
há um link entre a versão de linha antiga e a nova, então o PostgreSQL bloqueia e exclui a nova versão de linha, enquanto no caso deDELETE
+INSERT
não há versão válida da linha após o bloqueio ter desaparecido e nada esta deletado.Portanto, embora em muitos aspectos
UPDATE
eDELETE
+INSERT
sejam bastante semelhantes no PostgreSQL, eles não são os mesmos: no segundo caso, não há conexão entre a linha excluída e a inserida.Apêndice: o significado de
infomask
einfomask2
t_infomask
:t_infomask2
: