Vamos criar uma tabela temporária (eu escolho uma tabela temporária porque o autovacuum não funciona para esse tipo de tabela):
CREATE TEMP TABLE test (
id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
value int
);
INSERT INTO test (value) SELECT 0 FROM generate_series(0, 250);
SELECT ctid, * FROM test;
Veremos que a tabela consiste em duas páginas:
(0,1) 1 0
(0,2) 2 0
...
(0,226) 226 0
(1,1) 227 0
...
(1,25) 251 0
Agora, se atualizarmos uma linha
UPDATE test SET value = -1 WHERE id = 1;
SELECT ctid, * FROM test WHERE value <> 0;
veremos que a nova versão de linha foi inserida no final da tabela (na segunda página, que tem espaço livre suficiente para esta operação) e este é um comportamento padrão do PostgreSQL (a versão de linha antiga de (0,1) era marcado como morto)
(1,26) 1 -1
Vamos verificar o espaço livre das páginas:
CREATE EXTENSION pg_freespacemap;
SELECT * FROM pg_freespace('test');
Nós temos
0 0
1 0
ou seja, sem espaço livre; documentações dizem que o Free Space Map (FSM) é atualizado após a execução do vácuo. Agora, se atualizarmos outra linha da primeira página:
UPDATE test SET value = -1 WHERE id = 2;
SELECT ctid, * FROM test WHERE value <> 0;
vamos ver
(0,227) 2 -1
(1,26) 1 -1
A nova versão de linha não foi adicionada à 2ª página, mas foi adicionada à página atual, porque criamos espaço livre quando atualizamos o registro com id 1. Mas essa abordagem não corresponde ao meu entendimento de FSM. Aqui estão minhas perguntas:
- Como o PostgreSQL soube no segundo UPDATE que havia espaço livre na primeira página se
pg_freespace('test')
retornasse zero? Eu pensei que esta função lhe dá o FSM que por sua vez é usado no caso de UPDATEs para decidir qual página tem espaço livre suficiente para armazenar a nova versão da linha. - Geralmente eu estava pensando que, mesmo que a primeira linha estivesse marcada como morta, ela ocuparia algum espaço na 1ª página e esse espaço será liberado somente após o vácuo. Assim, eu esperava que o segundo UPDATE também adicionasse a nova versão de linha à 2ª página.
Isso conterá alguns componentes internos do PostgreSQL; desculpe, você pediu.
O Autovacuum não é executado em tabelas temporárias (porque elas são invisíveis fora da sua sessão) e, de qualquer forma, uma ou duas atualizações não acionarão o autovacuum. Agora é ele
VACUUM
que cria e mantém o mapa de espaço livre, então não há mapa de espaço livre no seu caso .pg_freespacemap
não dá erro, mas relata isso como 0.Na falta de um mapa de espaço livre, o PostgreSQL precisa examinar os blocos individuais para encontrar um bloco com espaço livre. Isso deixa o enigma por que a segunda atualização poderia encontrar espaço para criar uma tupla no bloco 0.
Para responder ao enigma, vamos discutir suas declarações e seus efeitos. Você pode verificar tudo isso usando a
pageinspect
extensão.o primeiro
UPDATE
:Isso marca a tupla (0,1) como atualizada (configura
xmax
) e cria a nova tupla (1,26), exatamente como você esperava.a seguinte consulta:
Essa consulta pode obter um bloqueio de página no bloco 0 e realizar a poda de heap , que é um “micro-vácuo” que libera o espaço ocupado por tuplas mortas. Observe que ele não pode remover (0,1), pois ainda é referenciado em um índice, mas o ponteiro de linha (0,1) torna-se um “stub” marcado
LP_DEAD
que não ocupa nenhum espaço. O espaço ocupado por essa tupla morta é liberado.Observe que, se você não executar isso
SELECT
, o seguinteUPDATE
executará a remoção de heap no bloco 0, portanto, o efeito será o mesmo.o segundo
UPDATE
:Na falta de um mapa de espaço livre, a instrução procura um bloco 0 e percebe que há espaço livre suficiente, então executa uma atualização HOT e coloca a nova tupla no bloco 0.