O Postgres contém um horizonte de eventos móvel, que está em vigor cerca de 2 bilhões de transações à frente ou atrás do ID de transação atual. As transações até 2 bilhões à frente ou mais de 2 bilhões atrás do ID da transação atual são consideradas no futuro e, portanto, serão invisíveis para as transações atuais.
O Postgres evita essa perda catastrófica de dados marcando especialmente as linhas antigas para que, independentemente de onde estejam em relação ao ID da transação atual, elas fiquem visíveis.
O congelamento é esse processo de marcação de tuplas ativas antigas (ou seja, linhas do banco de dados) para que elas não sejam atropeladas pelo horizonte de eventos em movimento que, de outra forma, as faria parecer no futuro. Isso contrasta com a aspiração, que é a liberação de espaço consumido por antigas tuplas mortas que não são mais visíveis para nenhuma transação.
Ambos os processos são gerenciados por vácuo.
Há várias configurações que controlam como o congelamento é feito.
Primeiro,
vacuum_freeze_min_age
controla se uma tupla será congelada ou não enquanto o vácuo já estiver olhando para uma página para ver se há tuplas mortas que podem ser limpas. Tuplas anteriores a vacuum_freeze_min_age
será congelado neste caso. Definir esse valor baixo significa que haverá menos trabalho a ser feito mais tarde, mas com o possível custo de esforço extra tanto na atividade de CPU quanto de E/S ou WAL. Geralmente, você provavelmente deseja que isso seja definido para pelo menos algumas horas de transações. Digamos que você espera fazer até 2.000 transações por segundo como uma taxa sustentada. 2000 TPS são 7,2 milhões de transações por hora. Assim, uma configuração bastante agressiva para este caso pode ser, digamos, 20m. A configuração padrão é 50m. Da mesma forma para vacuum_multixact_freeze_min_age
. Observe que os contadores transaction_id e multixid são independentes – você precisa acompanhar os dois. Segundo, existem
vacuum_freeze_table_age
e vacuum_multixact_freeze_table_age
. Essas configurações controlam quando o autovacuum não apenas examinará as páginas que podem ter linhas mortas, mas qualquer página que possa ter linhas descongeladas. Os padrões para essas configurações são 150m. Se você reduziu vacuum_freeze_min_age
suficiente, em muitos casos esse vácuo mais agressivo terá pouco ou nenhum trabalho a fazer. De qualquer forma, esse processo não é tão ocupado quanto costumava ser, pois as versões modernas do Postgres (9.6 e superiores) mantêm um mapa de páginas onde todas as tuplas estão congeladas e visitam apenas as páginas que não estão todas congeladas. Isso significa que esta não é mais uma varredura de tabela completa. O último é
autovacuum_freeze_max_age
. Se a última vez que a tabela foi verificada completamente em busca de linhas descongeladas foi mais do que esse número de transações atrás, o autovacuum iniciará um vácuo anti-wraparound na tabela. O padrão é 200m. Da mesma forma para autovacuum_multixact_freeze_max_age
para o qual o padrão é 400m. Isso é algo que você realmente quer evitar. Há duas coisas que podem ser feitas. Primeiro, é muito comum aumentar essas configurações para algo como 1 bilhão, para ter mais espaço, especialmente em sistemas que são grandes consumidores de transações. Você pode aumentar mais, mas deseja ter bastante espaço de transação entre sua tupla mais antiga e o horizonte de eventos. Em segundo lugar, é importante monitorar seus sistemas e tomar medidas corretivas antes que qualquer banco de dados se depare com isso. Essa ação corretiva geralmente inclui a aspiração manual. Um problema que pode ocorrer é onde você tem DDL que faz com que o autovacuum normal (ou seja, não anti-wraparound) se cancele. Se você fizer isso o suficiente, eventualmente, você terá um vácuo anti-wraparound forçado, e qualquer DDL ficará na fila atrás do processo de vácuo, e isso, por sua vez, bloqueará qualquer DML adicional. Nesta fase, sua tabela é efetivamente ilegível até que o vácuo termine. Isso depende do padrão de uso do seu banco de dados, mas não é apenas uma possibilidade teórica e as implantações do Postgres e DBAs precisam levar isso em consideração.
Monitorar seu cluster de banco de dados é fundamental para gerenciar isso. Em particular, você precisa monitorar o
datfrozenxid
e datminmxid
de cada banco de dados no cluster e, se eles ficarem muito antigos, tome medidas corretivas antes que um vácuo anti-wraparound seja necessário. Muitas vezes o problema é com uma ou algumas tabelas no banco de dados. Quais são o problema pode ser descoberto examinando o relfrozenxid
e relminmxid
das tabelas do banco de dados. A age()
e mxid_age()
funções são úteis para descobrir a idade dos contadores de ID de transação e multixid, respectivamente. O congelamento não é algo que você pode evitar, é uma atividade de manutenção essencial no Postgres que precisa ser gerenciada ativamente.