Alguém poderia me explicar esse comportamento? Eu executei a seguinte consulta no Postgres 9.3 executando nativamente no OS X. Eu estava tentando simular algum comportamento em que o tamanho do índice pudesse crescer muito maior que o tamanho da tabela e, em vez disso, achei algo ainda mais bizarro.
CREATE TABLE test(id int);
CREATE INDEX test_idx ON test(id);
CREATE FUNCTION test_index(batch_size integer, total_batches integer) RETURNS void AS $$
DECLARE
current_id integer := 1;
BEGIN
FOR i IN 1..total_batches LOOP
INSERT INTO test VALUES (current_id);
FOR j IN 1..batch_size LOOP
UPDATE test SET id = current_id + 1 WHERE id = current_id;
current_id := current_id + 1;
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT test_index(500, 10000);
Eu deixei isso funcionar por cerca de uma hora na minha máquina local, antes de começar a receber avisos de problemas de disco do OS X. Notei que o Postgres estava sugando cerca de 10 MB / s do meu disco local e que o banco de dados do Postgres estava consumindo um total geral. de 30 GB da minha máquina. Acabei cancelando a consulta. Independentemente disso, o Postgres não retornou o espaço em disco para mim e consultei o banco de dados para obter estatísticas de uso com o seguinte resultado:
test=# SELECT nspname || '.' || relname AS "relation",
pg_size_pretty(pg_relation_size(C.oid)) AS "size"
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_relation_size(C.oid) DESC
LIMIT 20;
relation | size
-------------------------------+------------
public.test | 17 GB
public.test_idx | 14 GB
No entanto, a seleção na tabela não produziu resultados.
test=# select * from test limit 1;
id
----
(0 rows)
A execução de 10000 lotes de 500 é de 5.000.000 de linhas, o que deve gerar um tamanho muito pequeno de tabela / índice (na escala de MB). Eu suspeito que o Postgres esteja criando uma nova versão da tabela / índice para cada INSERT / UPDATE que está acontecendo com a função, mas isso parece estranho. Toda a função é executada transacionalmente e a tabela estava vazia para iniciar.
Alguma idéia de por que estou vendo esse comportamento?
Especificamente, as duas perguntas que tenho são: por que esse espaço ainda não foi recuperado pelo banco de dados e a segunda é por que o banco de dados exigiu tanto espaço em primeiro lugar? 30 GB parece muito, mesmo quando se considera o MVCC
fonte
Os números reais após a análise da função são muito maiores porque todas as linhas da tabela obtêm o mesmo valor que é atualizado várias vezes em cada iteração.
Quando o executamos com parâmetros
n
em
:existem
m
inserções en * (m^2 + m) / 2
atualizações de linha . Portanto, paran = 500
em = 10000
, o Postgres precisará inserir apenas 10 mil linhas, mas executar ~ 25G (25 bilhões) de atualizações de tupla.Considerando que uma linha no Postgres possui uma sobrecarga de 24 bytes, uma tabela com apenas uma
int
coluna precisará de 28 bytes por linha mais a sobrecarga da página. Portanto, para a operação terminar, precisaríamos de cerca de 700 GB mais o espaço para o índice (que também seria algumas centenas de GB).Teste
Para testar a teoria, criamos outra tabela
test_test
com uma única linha.Em seguida, adicionamos um gatilho
test
para que cada atualização aumente o contador em 1. (Código omitido). Em seguida, executamos a função, com valores menores,n = 50
em = 100
.Nossa teoria prevê :
Teste 1 (
test
tabela original , com índice)Após a conclusão, verificamos o conteúdo da tabela:
E uso do disco (consulta em Estatísticas do tamanho / uso do Índice em Manutenção de Índice ):
A
test
tabela usou quase 9 MB para a tabela e 5 MB para o índice. Observe que atest_test
tabela usou outros 9 MB! Isso é esperado, pois também passou por 250 mil atualizações (nosso segundo gatilho atualizou a única linha detest_test
cada atualização de uma linhatest
).Observe também o número de varreduras na tabela
test
(10K) e as tuplas lêem (500K).Teste 2 (
test
tabela sem índice)Exatamente o mesmo que acima, exceto que a tabela não possui índice.
Temos o mesmo tamanho para uso de disco da tabela e, é claro, nenhum uso de disco para índices. O número de varreduras na tabela
test
é zero e as tuplas também são lidas.Teste 3 (com fator de preenchimento inferior)
Tentei com o fator de preenchimento 50 e o mais baixo possível, 10. Nenhuma melhoria. O uso do disco era quase idêntico aos testes anteriores (que usavam o fator de preenchimento padrão, 100%)
fonte