Os bancos de dados do PostgreSQL requerem
uma manutenção de limpeza periódica, conhecida como
VACUUM.
Para muitas instalações, é suficiente deixar a limpeza ser feita pelo
daemon autovacuum, descrito em
Processo de limpeza automática.
Pode ser necessário ajustar os parâmetros de limpeza automática
descritos nesta seção para obter os melhores resultados.
Alguns administradores de banco de dados desejarão complementar
ou substituir as atividades do autovacuum
por comandos VACUUM gerenciados manualmente,
que são normalmente executados segundo uma programação de comandos
especificada no cron, ou no
Gerenciador de tarefas do
Windows.
Para configurar adequadamente a limpeza gerenciada manualmente,
é essencial entender os problemas discutidos nas próximas subseções.
Os administradores que dependem do
daemon autovacuum podem ler este
material para ajudá-los a entender e ajustar o
autovacuum.
O comando VACUUM do PostgreSQL precisa processar cada tabela regularmente por vários motivos:
Cada um desses motivos determina a execução de operações de
VACUUM de frequência e escopo variados,
conforme explicado nas subseções a seguir.
Existem duas variantes do comando VACUUM:
VACUUM padrão e VACUUM FULL.
O comando VACUUM FULL pode recuperar mais espaço
em disco, mas a execução é muito mais lenta.
Além disso, a forma padrão do comando VACUUM
pode ser executada em paralelo com as operações do banco de dados
em produção.
(Comandos como SELECT, INSERT,
UPDATE e DELETE continuarão
a funcionar normalmente, embora não seja possível modificar a
definição de uma tabela com comandos como
ALTER TABLE enquanto esta estiver sendo limpa.)
O comando VACUUM FULL requer o bloqueio
ACCESS EXCLUSIVE na tabela em que está trabalhando,
portanto não pode ser usado em paralelo com outro uso da tabela.
Geralmente, portanto, os administradores devem se esforçar para usar
o comando VACUUM padrão, e evitar o uso do
comando VACUUM FULL.
O comando VACUUM cria uma quantidade substancial
de tráfego de E/S, o que pode causar baixo desempenho nas outras
sessões ativas.
Existem parâmetros de configuração que podem ser ajustados para
reduzir o impacto no desempenho devido à limpeza em segundo plano
— veja Atraso do VACUUM baseado em custos.
No PostgreSQL, o UPDATE
ou DELETE de uma linha não remove imediatamente
a versão antiga da linha.
Esta abordagem é necessária para obter os benefícios do controle de
concorrência multiversão (MVCC, veja
Controle de concorrência): a versão da linha não deve ser removida
enquanto ainda estiver potencialmente visível para outras transações.
Mas, por fim, uma versão de linha desatualizada ou excluída não
é mais interessante para nenhuma transação.
O espaço que ocupa deve então ser recuperado para reutilização por
novas linhas, para evitar o crescimento ilimitado dos requisitos
de espaço em disco.
Isto é feito executando o comando VACUUM.
A forma padrão do comando VACUUM remove versões
de linhas mortas em tabelas e índices, e marca o espaço disponível
para reutilização futura.
Entretanto, não devolverá o espaço ao sistema operacional, exceto
no caso especial em que uma ou mais páginas no final da tabela
fiquem inteiramente livres, e o bloqueio exclusivo da tabela pode
ser facilmente obtido.
Em contraste, o comando VACUUM FULL compacta as
tabelas ativamente, escrevendo uma nova versão completa do arquivo
da tabela sem espaço morto.
Isto minimiza o tamanho da tabela, mas pode levar muito tempo.
Também requer espaço em disco extra para a nova cópia da tabela,
até que a operação seja concluída.
O objetivo usual da limpeza de rotina é executar o comando
VACUUM padrão com frequência suficiente
para evitar a necessidade do VACUUM FULL.
O daemon autovacuum tenta trabalhar dessa
forma e, na verdade, nunca executará o VACUUM FULL.
Nesta abordagem, a ideia não é manter as tabelas em seu tamanho
mínimo, mas manter o uso do espaço em disco estável:
cada tabela ocupa um espaço equivalente ao seu tamanho mínimo,
mais quanto espaço for usado entre execuções de limpeza.
Embora o comando VACUUM FULL possa ser usado para
reduzir uma tabela de volta ao seu tamanho mínimo e devolver o espaço
em disco ao sistema operacional, não haverá muito sentido nisso se a
tabela simplesmente crescer novamente no futuro.
Portanto, para manter as tabelas atualizadas,
execuções moderadamente frequentes do comando
VACUUM padrão é uma abordagem melhor
do que execuções pouco frequentes do comando
VACUUM FULL.
Alguns administradores preferem agendar a limpeza por conta própria
como, por exemplo, fazendo todo o trabalho à noite, quando a carga
estiver baixa.
A dificuldade em fazer a limpeza segundo um cronograma fixo
é que, se a tabela tiver um pico inesperado na atividade de
atualização, ela poderá crescer a ponto de ser realmente necessário o
uso do comando VACUUM FULL para recuperar o espaço.
O uso do daemon autovacuum alivia este
problema, porque o daemon autovacuum agenda
a limpeza dinamicamente em resposta à atividade de atualização.
Não é aconselhável desativar o
daemon autovacuum inteiramente, a menos
que se tenha uma carga de trabalho extremamente previsível.
Um compromisso possível é definir os parâmetros do processo para
reagir apenas a atividades de atualização inusitadamente pesadas,
evitando assim que a situação saia de controle, enquanto comandos
VACUUM programados devem fazer a maioria do
trabalho quando a carga for típica.
Para aqueles que não usam o autovacuum,
uma abordagem típica é agendar o comando VACUUM
para cada banco de dados uma vez por dia durante um período
de baixo uso, complementado por uma limpeza mais frequente de tabelas
fortemente atualizadas conforme necessário.
(Algumas instalações com taxas de atualização extremamente altas
limpam suas tabelas mais ativas tão frequentemente quanto uma vez
a cada poucos minutos.)
Havendo vários bancos de dados em uma instância, não se deve esquecer
de executar o comando VACUUM em cada um deles;
o utilitário vacuumdb pode ser útil.
O comando VACUUM simples pode não ser satisfatório
quando a tabela contém muitas versões de linhas mortas, como
resultado de atividade de atualização ou de exclusão acima
do normal.
Se existir uma tabela assim, e for preciso recuperar o excesso de
espaço em disco que ela ocupa, será necessário usar o comando
VACUUM FULL, ou, como alternativa, o comando
CLUSTER, ou uma das variantes de reescrita
de tabela do comando ALTER TABLE.
Estes comandos reescrevem uma nova cópia inteira da tabela,
e criam novos índices para ela.
Todas estas opções requerem o bloqueio
ACCESS EXCLUSIVE.
Note que estes comandos também usam temporariamente um espaço extra
em disco aproximadamente igual ao tamanho da tabela, porque as
cópias antigas da tabela e dos índices não podem ser liberadas até
que as novas sejam concluídas.
Se houver uma tabela cujo conteúdo inteiro é excluído periodicamente,
deve-se considerar fazê-lo usando o comando
TRUNCATE, em vez de usar o comando
DELETE seguido pelo comando
VACUUM.
O comando TRUNCATE remove todo o conteúdo da
tabela imediatamente, sem exigir um comando VACUUM
ou VACUUM FULL subsequente para
recuperar o espaço em disco não mais utilizado.
A desvantagem é que é violada a semântica estrita do
MVCC.
O planejador de consultas do PostgreSQL
conta com informações estatísticas sobre o conteúdo das tabelas
para gerar bons planos de consultas.
Estas estatísticas são reunidas pelo comando
ANALYZE, que pode ser chamado sozinho,
ou como uma etapa opcional do comando VACUUM.
É importante ter estatísticas razoavelmente precisas, caso contrário
escolhas ruins de planos podem degradar o desempenho do servidor de
banco de dados.
O daemon autovacuum, se ativo, executa
automaticamente comandos ANALYZE sempre que o
conteúdo da tabela for alterado o suficiente.
Entretanto, os administradores podem preferir confiar em operações de
ANALYZE agendadas manualmente, especialmente se
for conhecido que a atividade de atualização da tabela não
afetará as estatísticas de colunas que “interessam”.
O daemon agenda o comando
ANALYZE estritamente em função do número de
linhas inseridas ou atualizadas; não tem conhecimento se isto
levará a mudanças estatísticas significativas.
As tuplas alteradas em partições e tabelas filhas por herança não
acionam a análise na tabela mãe.
Se a tabela mãe estiver vazia, ou for raramente alterada, ela nunca
poderá ser processada pelo autovacuum, e as
estatísticas da árvore de herança em seu todo não serão coletadas.
É necessário executar o comando ANALYZE na tabela
mãe manualmente para manter as estatísticas atualizadas.
Assim como na limpeza para recuperação de espaço, as atualizações
frequentes de estatísticas são mais úteis para as tabelas muito
atualizadas do que para as tabelas raramente atualizadas.
Mas mesmo para uma tabela bastante atualizada, poderá não haver
necessidade de atualizações de estatísticas, se a distribuição
estatística dos dados não estiver mudando muito.
Uma regra simples é pensar em quanto os valores mínimo e máximo
das colunas na tabela mudam.
Por exemplo, uma coluna do tipo de dados timestamp
contendo a hora da atualização da linha, terá um valor máximo
que aumenta constantemente à medida que as linhas são adicionadas
e atualizadas; esta coluna precisará provavelmente de atualizações
de estatísticas mais frequentes do que, digamos, uma coluna
contendo URLs para páginas acessadas em um site.
A coluna de URL poderá receber alterações com
a mesma frequência, mas a distribuição estatística de seus valores
mudará provavelmente de forma relativamente lenta.
É possível executar o comando ANALYZE em tabelas
específicas, e até mesmo em colunas específicas de uma tabela,
portanto existe a flexibilidade de atualizar algumas estatísticas
com mais frequência do que outras, se a aplicação exigir.
Na prática, porém, é geralmente melhor apenas analisar todo o
banco de dados, porque é uma operação rápida.
O comando ANALYZE usa uma amostragem
estatisticamente aleatória das linhas da tabela,
em vez de ler cada linha.
Embora o ajuste da frequência por coluna para o comando
ANALYZE possa não ser muito produtivo,
pode-se achar que vale a pena fazer o ajuste por coluna no nível
de detalhe das estatísticas coletadas pelo comando
ANALYZE.
As colunas muito usadas em cláusulas WHERE,
e com distribuições de dados muito irregulares, podem exigir um
histograma de dados mais refinado do que outras colunas.
Veja o comando ALTER TABLE SET STATISTICS,
ou altere o valor padrão para todo o banco de dados usando o
parâmetro de configuração
default_statistics_target.
Além disso, por padrão, há informações limitadas disponíveis sobre a seletividade das funções. Entretanto, se for criado um objeto de estatísticas ou um índice de expressão que usa uma chamada de função, serão coletadas estatísticas úteis sobre a função, o que poderá melhorar muito os planos de consulta que usam o índice de expressão.
O daemon autovacuum não executa comandos
ANALYZE para tabelas estrangeiras, porque não
tem meios de determinar com que frequência isto pode ser útil.
Se as consultas exigirem estatísticas em tabelas estrangeiras para
um planejamento adequado, é uma boa ideia executar comandos
ANALYZE gerenciados manualmente nessas tabelas
em um cronograma adequado.
O daemon autovacuum não executa comandos
ANALYZE para tabelas particionadas.
As mães da herança só serão analisadas se a própria mãe for alterada
– as alterações nas tabelas filhas não acionam a análise
automática na tabela mãe.
Se as consultas exigirem estatísticas nas tabelas mãe para um
planejamento adequado, será necessário executar periodicamente o
comando ANALYZE manualmente nessas tabelas para
manter as estatísticas atualizadas.
O comando VACUUM mantém um
mapa de visibilidade para cada
tabela, para manter o controle de quais páginas contêm apenas tuplas
conhecidas por serem visíveis para todas as transações ativas
(e todas as transações futuras, até que a página seja novamente
modificada).
Isto tem duas finalidades.
Em primeiro lugar, o próprio VACUUM poderá saltar
estas páginas na próxima execução, porque não há nada para limpar.
Em segundo lugar, permite que o PostgreSQL responda algumas consultas usando apenas o índice, sem referência à tabela subjacente. Como os índices do PostgreSQL não contêm informações de visibilidade da linha, uma varredura de índice normal busca a linha da tabela para cada entrada de índice correspondente, para verificar se ela está visível pela transação corrente. Uma varredura de índice-apenas, por outro lado, verifica primeiro o mapa de visibilidade. Se for conhecido que todas as linhas na página estão visíveis, a busca da linha poderá ser evitada. Isto é mais útil em grandes conjuntos de dados em que o mapa de visibilidade pode evitar acessos ao disco. O mapa de visibilidade é muito menor do que a tabela, portanto pode ser facilmente armazenado em cache, mesmo quando a tabela for muito grande.
A semântica de transações do controle de concorrência multiversão (MVCC) do PostgreSQL depende da capacidade de comparar números de ID de transação (XID): uma versão de linha com XID de inserção maior que o XID da transação corrente está “no futuro”, não devendo estar visível para a transação corrente. Mas como os IDs de transação têm tamanho limitado (32 bits), uma instância ativa há muito tempo (mais de 4 bilhões de transações) poderá sofrer um reinício do identificador de transação: o contador XID volta a zero e, de repente, as transações que estavam no passado parecem estar no futuro — significando que suas saídas se tornam invisíveis. Em resumo, uma perda de dados terrível. (Na verdade, os dados continuam lá, mas isto será de pouca serventia se não for possível obtê-los.) Para evitar esta situação, é necessário limpar todas as tabelas em todos os bancos de dados pelo menos uma vez a cada dois bilhões de transações.
A razão pela qual a limpeza periódica resolve este problema é que
o comando VACUUM irá marcar as linhas como
congeladas (frozen),
indicando que foram inseridas por uma transação suficientemente
antiga para que os efeitos dessa transação ainda possam ser visíveis
pelas transações correntes e futuras.
Os XIDs normais são comparados usando a aritmética
de módulo-232.
Isto significa que para cada XID normal, existem
dois bilhões de XIDs que são
“mais antigos”, e dois bilhões que são
“mais novos”;
outra maneira de dizer isto é que o espaço de XID
normal é circular sem extremidade.
Portanto, uma vez que uma versão de linha foi criada com um
XID normal específico, a versão da linha parecerá
estar “no passado” para os próximos dois bilhões de
transações, independentemente de qual XID
normal está se falando.
Se a versão da linha ainda existir após mais de dois bilhões de
transações, de repente parecerá estar no futuro.
Para evitar esta situação, o PostgreSQL
reserva um XID especial, o
FrozenTransactionId, que não segue as regras
normais de comparação de XID, sendo sempre
considerado mais antigo que todo XID normal.
As versões de linhas congeladas são tratadas como se o
XID de inserção fosse
FrozenTransactionId, para parecerem estar
“no passado” para todas as transações normais,
independentemente de problemas de reinício e, portanto, estas
versões de linha serão válidas até serem excluídas, não importando
quanto tempo isto demore.
Nas versões do PostgreSQL anteriores a
9.4, o congelamento era implementado substituindo o
XID de inserção da linha por
FrozenTransactionId, que era visível na coluna
do sistema xmin da linha.
As versões mais recentes apenas definem um bit sinalizador,
preservando o xmin original da linha
para possível uso forense.
Entretanto, linhas com xmin igual a
FrozenTransactionId (2) ainda podem ser
encontradas em bancos de dados com
pg_upgrade de versões anteriores à 9.4.
Além disso, os catálogos do sistema podem conter linhas com
xmin igual a
BootstrapTransactionId (1), indicando que foram
inseridas durante a primeira fase do initdb.
Assim como FrozenTransactionId, este
XID especial é tratado como mais antigo que
qualquer XID normal.
O parâmetro vacuum_freeze_min_age controla quão antigo um valor XID deve ser antes que as linhas com este XID sejam congeladas. Aumentar esta configuração poderá evitar trabalho desnecessário, se as linhas que de outra forma seriam congeladas forem modificadas novamente em breve, mas diminuir esta configuração aumenta o número de transações que podem ocorrer antes que a tabela seja limpa novamente.
O comando VACUUM usa o mapa de visibilidade para
determinar quais páginas de uma tabela devem ser verificadas.
Normalmente, o sistema ignora páginas que não possuem versões de
linhas desatualizadas, mesmo que estas páginas ainda possam ter
versões de linhas com valores de XID antigos.
Portanto, comandos VACUUM normais nem sempre
congelam todas as versões antigas das linhas na tabela.
Quando isto acontece, o comando VACUUM
eventualmente precisará executar um
VACUUM agressivo, que
congelará todos os valores de XID e MXID elegíveis que ainda não
foram congelados, incluindo aqueles de todas as páginas visíveis,
mas não inteiramente congeladas.
Se uma tabela estiver acumulando um número excessivo de páginas visíveis, mas não congeladas, uma operação de limpeza normal poderá optar por examinar páginas que podem ser ignoradas, na tentativa de congelá-las. Fazendo isto, diminui-se o número de páginas que a próxima limpeza agressiva terá que varrer. Estas são referidas como páginas varridas com avidez. A varredura com avidez pode ser ajustada para tentar congelar mais páginas inteiramente visíveis, aumentando vacuum_max_eager_freeze_failure_rate. Mesmo que a varredura com avidez tenha mantido o número de páginas visíveis mas não congeladas no mínimo, a maioria das tabelas ainda requer uma limpeza agressiva periódica. Entretanto, qualquer página congelada com sucesso por congelamento rápido pode ser ignorada durante um processo de limpeza agressiva, portanto o congelamento rápido pode minimizar a sobrecarga dos processos de limpeza agressiva.
vacuum_freeze_table_age controla quando uma
tabela é limpa de forma agressiva.
Todas as páginas visíveis, mas não inteiramente congeladas, serão
varridas se o número de transações que ocorreram desde a última
varredura for maior que vacuum_freeze_table_age
menos vacuum_freeze_min_age.
Definir vacuum_freeze_table_age como 0 força o
VACUUM a sempre usar sua estratégia agressiva.
O tempo máximo que uma tabela pode ficar sem limpeza é de dois bilhões
de transações menos o valor vacuum_freeze_min_age
no momento da última limpeza agressiva.
Se não for limpa por mais tempo do que isto, poderá ocorrer perda
de dados.
Para garantir que isto não aconteça, o
daemon autovacuum é chamado em qualquer tabela
que possa conter linhas não congeladas com XIDs
anteriores à idade especificada pelo parâmetro de configuração
autovacuum_freeze_max_age.
(Isto acontecerá, mesmo se o autovacuum
estiver inativo.)
Isto implica que, se uma tabela não for limpa de outra forma, o
autovacuum será chamado para ela
aproximadamente uma vez a cada transação
autovacuum_freeze_max_age menos
vacuum_freeze_min_age.
Para as tabelas limpas regularmente para fins de recuperação
de espaço, isto é de pouca importância.
Entretanto, para as tabelas estáticas (incluindo tabelas que recebem
inserções, mas não atualizações ou exclusões), não há necessidade de
limpar para recuperação de espaço, portanto pode ser útil tentar
maximizar o intervalo entre os autovacuum
forçados em tabelas estáticas muito grandes.
Obviamente, pode-se fazer isto aumentando
autovacuum_freeze_max_age, ou diminuindo
vacuum_freeze_min_age.
O valor máximo efetivo para vacuum_freeze_table_age
é de 0,95 * autovacuum_freeze_max_age;
uma configuração maior do que esta será limitada ao máximo.
Um valor maior que autovacuum_freeze_max_age
não faria sentido, porque o autovacuum
anti-reinício seria acionado naquele ponto de qualquer maneira,
e o multiplicador de 0,95 deixa algum espaço para respirar para
executar o comando VACUUM manualmente antes que
isto aconteça.
Como regra geral, vacuum_freeze_table_age deve
ser definido como um valor um pouco abaixo de
autovacuum_freeze_max_age, deixando espaço
suficiente para que o comando VACUUM agendado
regularmente, ou o autovacuum disparado
pela atividade normal de exclusão e atualização, seja executado
neste intervalo.
Defini-lo muito próximo pode levar a limpezas automáticas
anti-reinício, mesmo que a tabela tenha sido limpa recentemente
para recuperar espaço, enquanto valores mais baixos levam a uma
limpeza agressiva mais frequente.
A única desvantagem de aumentar
autovacuum_freeze_max_age
(e vacuum_freeze_table_age ao mesmo tempo)
é que os subdiretórios pg_xact e
pg_commit_ts da instância ocuparão mais espaço,
porque devem armazenar o status de efetivação, e
(se track_commit_timestamp estiver ativo)
o carimbo de data/hora de todas as transações, desde
autovacuum_freeze_max_age.
O status de efetivação usa dois bits por transação, portanto se
autovacuum_freeze_max_age for definido com o
seu valor máximo permitido de dois bilhões, então
pg_xact poderá crescer para cerca de meio
gigabyte, e pg_commit_ts para cerca de 20 GB.
Se estes valores forem triviais em comparação com o tamanho total
do banco de dados, é recomendável definir
autovacuum_freeze_max_age com seu valor máximo
permitido.
Caso contrário, dever ser definido dependendo do que se deseja
permitir para o armazenamento de pg_xact e de
pg_commit_ts.
(O valor padrão, 200 milhões de transações, se traduz em cerca de
50 MB de armazenamento para pg_xact, e cerca
de 2 GB de armazenamento para pg_commit_ts.)
Uma desvantagem de diminuir vacuum_freeze_min_age
é que poderá fazer com que o comando VACUUM faça
um trabalho inútil: congelar uma versão de linha será perda de tempo
se a linha for modificada logo em seguida (fazendo com que ela
obtenha um novo XID).
Portanto, a configuração deve ser grande o suficiente para que as
linhas não sejam congeladas até que seja improvável que mudem mais.
Para rastrear a idade dos XIDs descongelados mais antigos em um
banco de dados, o comando VACUUM armazena
estatísticas de XID nas tabelas do sistema
pg_class e
pg_database.
Em particular, a coluna relfrozenxid da
linha pg_class de uma tabela contém o XID
final do congelamento que foi usado pelo último comando
VACUUM agressivo para esta tabela.
Da mesma forma, a coluna datfrozenxid
da linha pg_database de um banco de dados
é um limite inferior para os XIDs não congelados que aparecem neste
banco de dados — é simplesmente o valor mínimo entre os
valores de relfrozenxid por tabela no
banco de dados.
Uma maneira prática de examinar estas informações é executar
consultas como:
SELECT c.oid::regclass as nome_da_tabela,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as idade
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');
SELECT datname, age(datfrozenxid) FROM pg_database;
A coluna age mede o número de transações desde
o XID de corte até o XID
da transação corrente.
Quando é especificado o parâmetro VERBOSE do
comando VACUUM, o comando VACUUM
mostra várias estatísticas sobre a tabela.
Isto inclui informações sobre como
relfrozenxid e
relminmxid avançaram,
e o número de páginas recém-congeladas.
Os mesmos detalhes aparecem no registro do servidor quando o
registro do autovacuum
(controlado por log_autovacuum_min_duration)
relata uma operação de VACUUM executada pelo
autovacuum.
Embora o comando VACUUM examine principalmente
páginas que foram modificadas desde a última limpeza, também poderá
varrer agressivamente algumas páginas inteiramente visíveis, mas
não inteiramente congeladas, numa tentativa de congelá-las,
mas o campo relfrozenxid só será avançado
quando todas as páginas da tabela que possam conter XIDs descongelados
forem verificadas.
Isto ocorre quando o campo relfrozenxid é
mais antigo que vacuum_freeze_table_age transações,
quando é usada a opção FREEZE do comando
VACUUM, ou quando todas as páginas que ainda
não estão inteiramente congeladas precisam ser limpas para remover
versões com linhas mortas.
Quando o comando VACUUM examina todas as páginas
da tabela que ainda não estão inteiramente congeladas, deve definir
age(relfrozenxid) para um valor ligeiramente
superior à configuração vacuum_freeze_min_age
que foi utilizada (mais pelo número de transações iniciadas desde
que o comando VACUUM foi iniciado).
O comando VACUUM irá definir
relfrozenxid para o XID mais antigo que
permanecer na tabela, portanto é possível que o valor final seja
muito mais recente do que o estritamente necessário.
Se nenhum comando VACUUM com avanço de
relfrozenxid for emitido na tabela até
que autovacuum_freeze_max_age seja atingido,
será forçado em breve para a tabela um
autovacuum.
Se por algum motivo o autovacuum falhar ao limpar os XIDs antigos de uma tabela, o sistema começará a emitir mensagens de aviso como a seguinte quando os XIDs mais antigos do banco de dados atingirem quarenta milhões de transações antes do ponto de reinício:
WARNING: database "mydb" must be vacuumed within 39985967 transactions HINT: To avoid XID assignment failures, execute a database-wide VACUUM in that database.
(Um comando VACUUM manual deve resolver o
problema, conforme sugerido pela dica; mas note-se que o comando
VACUUM deve ser executado por um superusuário,
senão não conseguirá processar os catálogos do sistema, o que o
impede de avançar o datfrozenxid do
banco de dados.)
Se estes avisos forem ignorados, o sistema se recusará a atribuir
novos XIDs quando restarem menos de três milhões de transações até
o reinício:
ERROR: database is not accepting commands that assign new XIDs to avoid wraparound data loss in database "mydb" HINT: Execute a database-wide VACUUM in that database.
Nesta condição quaisquer transações já em andamento poderão
continuar, mas somente transações de leitura poderão ser iniciadas.
Operações que modificam registros do banco de dados ou truncam
relações irão falhar.
O comando VACUUM ainda poderá ser executado
normalmente.
Note-se que, ao contrário do que às vezes era recomendado em versões
anteriores, não é necessário nem recomendável parar o
postmaster, ou entrar no
modo de usuário único, para restaurar a operação normal.
Em vez disso, deve-se seguir os seguintes passos:
age(transactionid) seja grande.
Estas transações devem ser efetivadas ou desfeitas.age(backend_xid) ou
age(backend_xmin) seja grande.
Estas transações devem ser efetivadas ou desfeitas, ou a sessão
pode ser encerrada usando pg_terminate_backend.
age(xmin) ou
age(catalog_xmin) seja grande.
Em muitos casos estes encaixes foram criados para replicação em
servidores que não existem mais, ou que estão inativos há muito tempo.
Se for removido um encaixe de um servidor que ainda existe e que
ainda poderá tentar se conectar a este encaixe, esta réplica poderá
precisar ser reconstruída.VACUUM no banco de dados de
destino. Um VACUUM para todo o banco de dados
é o mais simples; para reduzir o tempo necessário, também é possível
executar comandos manuais VACUUM nas tabelas
onde relminxid é o mais antigo.
Não deve ser usado VACUUM FULL neste cenário,
porque requer um XID, e portanto irá falhar exceto no modo
superusuário, onde, em vez disso, consumirá um XID e, portanto,
aumentará o risco de estouro do ID da transação.
Não se deve usar VACUUM FREEZE também, porque
fará mais do que o mínimo necessário para restabelecer a operação
normal.
Em versões anteriores às vezes era necessário parar o
postmaster e executar o
comando VACUUM no banco de dados em modo de
usuário único.
Em cenários típicos, isto não é mais necessário e deve ser evitado
sempre que possível, porque envolve a desativação do sistema.
Também é mais arriscado, porque desativa as proteções contra o
reinício do ID da transação, que são projetadas para evitar a
perda de dados.
O único motivo para usar o modo de usuário único neste cenário é
se for desejado usar o comando TRUNCATE ou
DROP para excluir tabelas desnecessárias e
evitar ter que usar o comando VACUUM para
limpá-las.
A margem de segurança de três milhões de transações existe para
permitir que o administrador faça isto.
Veja a página de referência postgres para
obter detalhes sobre como usar o modo de usuário único.
Os identificadores multixact
(multiple transaction) são usados
para oferecer suporte ao bloqueio de linha das transações múltiplas.
Como existe apenas um espaço limitado no cabeçalho da tupla para
armazenar informações de bloqueio, esta informação é codificada
como um “identificador de transação múltipla”, ou
ID multixact para abreviar, sempre que houver
mais de uma transação bloqueando a mesma linha simultaneamente.
As informações sobre quais identificadores de transação estão
incluídos em qualquer ID multixact específico
são armazenadas separadamente no subdiretório
pg_multixact, e apenas o
ID multixact aparece no campo
xmax no cabeçalho da tupla.
Assim como os identificadores de transação, os
IDs multixact são implementados como um contador
de 32 bits e o armazenamento correspondente, o que requer um
cuidadoso gerenciamento de envelhecimento, limpeza de armazenamento,
e tratamento de reinício.
Existe uma área de armazenamento separada que contém a lista de
membros em cada identificador multixact, que
também usa um contador de 32 bits, e que também deve ser gerenciado.
A função do sistema pg_get_multixact_members()
descrita em Tabela 9.84 pode ser usada
para examinar os IDs de transação associados a um ID de multixact.
Sempre que o comando VACUUM varre qualquer parte
de uma tabela, ele irá substituir qualquer
ID multixact que encontrar que seja mais antigo
que vacuum_multixact_freeze_min_age por um
valor diferente, que poderá ser o valor zero, um único identificador
de transação, ou um ID multixact mais novo.
Para cada tabela,
pg_class.relminmxid
armazena o ID multixact mais antigo possível ainda
aparecendo em qualquer tupla dessa tabela.
Se este valor for mais antigo que
vacuum_multixact_freeze_table_age,
é forçada uma limpeza agressiva.
Conforme discutido na seção anterior, uma limpeza agressiva
significa que apenas as páginas que estão inteiramente congeladas
serão ignoradas.
Pode ser usada a função mxid_age() em
pg_class.relminmxid
para encontrar sua idade.
Varreduras agressivas do comando VACUUM,
independentemente do que as cause, garantem
conseguir avançar o relminmxid da tabela.
Por fim, à medida que todas as tabelas em todos os bancos de dados
são varridas e seus valores de multixact mais
antigos são avançados, o armazenamento em disco dos
multixacts mais antigos pode ser removido.
Como mecanismo de segurança, uma varredura de limpeza agressiva
será realizada em qualquer tabela cuja idade do
multixact seja superior a
autovacuum_multixact_freeze_max_age.
Além disso, se o armazenamento ocupado por membros de
multixacts exceder cerca de 10 GB, varreduras
agressivas de limpeza ocorrerão com mais frequência para todas as
tabelas, começando por aquelas que possuem a idade de
multixact mais antiga.
Ambos os tipos de varreduras agressivas ocorrerão mesmo que o
autovacuum esteja
nominalmente desativado.
A área de armazenamento dos membros pode crescer até cerca de 20 GB
antes de alcançar o reinício (wraparound).
Assim como no caso do XID, se o autovacuum não conseguir limpar os MXIDs antigos de uma tabela, o sistema começará a emitir mensagens de aviso quando os MXIDs mais antigos do banco de dados atingirem quarenta milhões de transações antes do ponto de reinício. E, assim como no caso do XID, se estes avisos forem ignorados o sistema se recusará a gerar novos MXIDs quando restarem menos de três milhões até o reinício.
A operação normal, quando os MXIDs se esgotam, pode ser restaurada de maneira muito semelhante àquela utilizada quando os XIDs se esgotam. Devem ser seguidas as mesmas etapas da seção anterior, mas com as seguintes diferenças:
multixact.pg_stat_activity;
entretanto, procurar por XIDs antigos ainda é uma boa maneira de
determinar quais transações estão causando problemas de reinício
de MXID.
O PostgreSQL possui um recurso opcional,
mas altamente recomendado, chamado
autovacuum,
cuja finalidade é automatizar a execução dos comandos
VACUUM e ANALYZE.
Quando ativado, o autovacuum verifica as
tabelas que tiveram muitas de tuplas inseridas, atualizadas,
ou excluídas.
Estas verificações usam o recurso de coleta de estatísticas;
portanto, o autovacuum não poderá ser usado
a menos que track_counts esteja definido como
on.
Na configuração padrão, a limpeza automática é ativada, e os
parâmetros de configuração relacionados são definidos adequadamente.
O “daemon autovacuum”,
na verdade, consiste em vários processos.
Existe um processo daemon persistente,
chamado de autovacuum launcher, que se
encarrega de iniciar o processo
autovacuum worker
para todos os bancos de dados.
O processo lançador distribui o trabalho ao longo do tempo,
tentando iniciar um processo trabalhador em cada banco de dados
a cada autovacuum_naptime segundos.
(Portanto, se a instância tiver N
bancos de dados, será iniciado um novo processo trabalhador a cada
autovacuum_naptime/N
segundos.)
No máximo autovacuum_max_workers processos
trabalhadores podem ser executados ao mesmo tempo.
Havendo mais de autovacuum_max_workers
bancos de dados a serem processados, o próximo banco de dados
será processado assim que o primeiro processo trabalhador terminar.
Cada processo trabalhador irá verificar cada tabela em seu banco
de dados, e executar o comando VACUUM e/ou
ANALYZE conforme necessário.
O parâmetro log_autovacuum_min_duration
pode ser configurado para monitorar a atividade dos processos
trabalhadores do autovacuum.
Se várias tabelas grandes se tornarem elegíveis para limpeza num curto espaço de tempo, todos os processos trabalhadores do autovacuum poderão ficar ocupados com a limpeza dessas tabelas por um longo período. Isto resultaria em outras tabelas e bancos de dados não sendo limpos até que um processo trabalhador se torne disponível. Não há limite de quantos processos trabalhadores podem estar em um único banco de dados, mas os processos trabalhadores tentam evitar a repetição do trabalho que já foi feito por outros processos trabalhadores. Note que o número de processos trabalhadores em execução não conta para os limites de max_connections ou superuser_reserved_connections.
As tabelas cujo valor de relfrozenxid
é maior que autovacuum_freeze_max_age
transações antigas são sempre limpas (isto também se aplica àquelas
tabelas cuja idade máxima de congelamento foi modificada por meio
de parâmetros de armazenamento; veja abaixo).
Caso contrário, se o número de tuplas obsoletadas desde o último
comando VACUUM exceder o
“limite de limpeza”, a tabela será limpa.
O limite de limpeza é definido como:
limite da limpeza = Mínimo(
limite máximo da limpeza,
limite base da limpeza +
fator de escala da limpeza * número de tuplas)
-- original
vacuum threshold = Minimum(
vacuum max threshold,
vacuum base threshold +
vacuum scale factor * number of tuples)
onde limite máximo da limpeza
(vacuum max threshold) é
autovacuum_vacuum_max_threshold,
limite base da limpeza
(vacuum base threshold) é
autovacuum_vacuum_threshold,
fator de escala da limpeza
(vacuum scale factor) é
autovacuum_vacuum_scale_factor,
e o número de tuplas é
pg_class.reltuples.
A tabela também passará pelo processo de limpeza se o número de tuplas inseridas desde a última limpeza tiver excedido o limite de inserção definido, o qual é definido como:
limite de inserção da limpeza =
limite de inserção base da limpeza +
fator de escala de inserção da limpeza * número de tuplas
-- original
vacuum insert threshold =
vacuum base insert threshold +
vacuum insert scale factor * number of tuples
onde o limite de inserção base da limpeza
(vacuum insert base threshold) é
autovacuum_vacuum_insert_threshold,
o fator de escala de inserção da limpeza
(vacuum insert scale factor) é
autovacuum_vacuum_insert_scale_factor,
e o número de tuplas é
pg_class.reltuples,
e a porcentagem da tabela que não está congelada é
1 - pg_class.relallfrozen / pg_class.relpages.
Tais operações de limpeza podem permitir que partes da tabela sejam
marcadas como inteiramente visíveis e
também permitir que tuplas sejam congeladas, o que pode reduzir o
trabalho necessário em operações de limpeza subsequentes.
Para tabelas que recebem operações INSERT, mas
nenhuma ou quase nenhuma operação de UPDATE ou
DELETE, poderá ser benéfico reduzir o
autovacuum_freeze_min_age da tabela,
porque isto pode permitir que as tuplas sejam congeladas por
operações de limpeza realizadas mais cedo.
O número de tuplas obsoletas e o número de tuplas inseridas são
obtidos a partir do sistema de estatísticas cumulativas;
é uma contagem com consistência eventual, atualizada por cada
operação de UPDATE, DELETE e
INSERT.
Se o valor de relfrozenxid da tabela
tiver mais transações do que o especificado em
vacuum_freeze_table_age, será realizada uma
limpeza agressiva para congelar tuplas antigas e avançar o
relfrozenxid.
Para a análise, é usada uma condição semelhante: o limite, definido como
limite da análise =
limite base da análise +
fator de escala da análise * número de tuplas
-- original
analyze threshold =
analyze base threshold +
analyze scale factor * number of tuples
é comparado com o número total de tuplas inseridas, atualizadas ou
excluídas desde o último ANALYZE.
As tabelas particionadas não armazenam tuplas diretamente e,
consequentemente, não são processadas pelo
autovacuum.
(O autovacuum processa
partições de tabela da mesma forma que outras tabelas.)
Infelizmente, isto significa que o
autovacuum não executa o
comando ANALYZE em tabelas particionadas,
e isto pode resultar em planos abaixo do ideal para consultas que
fazem referência a estatísticas de tabelas particionadas.
Pode-se contornar este problema executando manualmente o comando
ANALYZE em tabelas particionadas quando elas
são populadas pela primeira vez e, novamente, sempre que a
distribuição de dados em suas partições mudar significativamente.
As tabelas temporárias não podem ser acessadas pelo autovacuum. Portanto, as operações apropriadas de limpeza e análise devem ser executadas por meio de comandos SQL na sessão.
Os limites padrão e fatores de escala são obtidos do arquivo
postgresql.conf, mas é possível sobrepô-los
(e muitos outros parâmetros de controle de limpeza automático)
por tabela; veja Parâmetros de armazenamento
para obter mais informações.
Se a configuração foi alterada por meio dos parâmetros de
armazenamento da tabela, este valor será usado ao processar
esta tabela; caso contrário, serão usadas as configurações globais.
Veja Limpeza automática para obter mais
informações sobre as configurações globais.
Quando vários processos trabalhadores estão em execução,
os parâmetros de atraso por custo do autovacuum
(veja Atraso do VACUUM baseado em custos) são
“balanceados” entre todos os processos trabalhadores
em execução, de modo que o impacto total de E/S no sistema seja o
mesmo, independentemente do número de processos trabalhadores
realmente em execução.
Entretanto, quaisquer processos trabalhadores processando tabelas,
cujos parâmetros de armazenamento
autovacuum_vacuum_cost_delay ou
autovacuum_vacuum_cost_limit foram definidos
por tabela, não serão considerados no algoritmo de balanceamento.
Os processos trabalhadores de autovacuum
geralmente não bloqueiam outros comandos.
Se um processo tentar adquirir um bloqueio que entre em conflito
com o bloqueio SHARE UPDATE EXCLUSIVE mantido
pelo autovacuum, a aquisição do bloqueio
interromperá o autovacuum.
Para os modos de bloqueio conflitantes, veja a
Tabela 13.2.
Entretanto, se o autovacuum estiver em
execução para evitar o reinício do identificador da transação
(ou seja, o nome da consulta autovacuum
na visão pg_stat_activity terminar com
(para evitar reinício automático)),
o autovacuum não será interrompido
automaticamente.
A execução regular de comandos que adquirem bloqueios conflitantes
com o bloqueio SHARE UPDATE EXCLUSIVE
(por exemplo, o comando ANALYZE), pode
impedir que os autovacuum sejam concluídos.