Capítulo 21. Rotinas de manutenção do banco de dados

Sumário
21.1. Rotina de Limpeza
21.1.1. Recuperação do espaço em disco
21.1.2. Atualização das estatísticas do planejador
21.1.3. Prevenção de falhas devido ao reinício do ID de transação
21.2. Rotina de reindexação
21.3. Manutenção do arquivo de registro

Existem algumas poucas tarefas de manutenção que precisam ser realizadas regularmente para manter o servidor PostgreSQL funcionando sem problemas. As tarefas mostradas neste capítulo são repetitivas por natureza, podendo ser facilmente automatizadas utilizando as ferramentas padrão do Unix como os scripts do cron. É responsabilidade do administrador do banco de dados instalar os scripts apropriados, e verificar se a execução está sendo bem-sucedida.

Uma tarefa de manutenção óbvia é a geração das cópias de segurança dos dados em períodos regulares. Sem uma cópia de segurança recente, não há como fazer a recuperação após um desastre (falha de disco, incêndio, remoção por engano de uma tabela crítica, etc.). Os mecanismos de cópia de segurança e restauração disponíveis no PostgreSQL estão descritos de forma abrangente no Capítulo 22.

Outra tarefa de manutenção importante é a limpeza periódica do banco de dados. Esta atividade está descrita na Seção 21.1.

Outro item que pode precisar de atenção periódica é o gerenciamento do arquivo de registro (log), conforme mostrado na Seção 21.3.

O PostgreSQL necessita de pouca manutenção se comparado a outros sistemas gerenciadores de bancos de dados. Apesar disso, a devida atenção a estas tarefas garante bem mais que uma experiência agradável e produtiva do sistema.

21.1. Rotina de Limpeza

O comando VACUUM do PostgreSQL deve ser executado regularmente por diversos motivos:

  1. Para recuperar o espaço em disco ocupado pelas linhas atualizadas e removidas.

  2. Para atualizar as estatísticas dos dados utilizadas pelo planejador de comandos do PostgreSQL.

  3. Para proteger contra perda de dados muito antigos devido ao recomeço do ID de transação.

A freqüência e a abrangência das operações de VACUUM, realizadas devido aos motivos acima, variam dependendo das necessidades da instalação. Portanto, os administradores de banco de dados devem compreender estas questões e desenvolver uma estratégia de manutenção apropriada. Esta seção se concentra em explicar as questões de alto nível; para obter detalhes sobre a sintaxe do comando e outras informações deve ser consultada a página de referência do comando VACUUM.

A partir do PostgreSQL 7.2 a forma padrão do comando VACUUM pode executar em paralelo com as operações normais do banco de dados (seleções, inserções, atualizações, exclusões, mas sem modificação de definição de tabela). Portanto, a rotina de limpeza não é mais tão impactante como era nas versões anteriores, não sendo mais tão crítico tentar agendá-la para as horas do dia com baixa utilização.

A partir do PostgreSQL 8.0 passaram a existir parâmetros de configuração que podem ser ajustados para reduzir ainda mais o impacto da limpeza em segundo plano. Consulte a Seção 16.4.3.4.

21.1.1. Recuperação do espaço em disco

Na operação normal do PostgreSQL, um comando UPDATE ou DELETE em uma linha não remove imediatamente a versão antiga da linha. Esta abordagem é necessária para obter os benefícios do controle de simultaneidade multi-versão (consulte o Capítulo 12): a versão da linha não pode ser removida enquanto houver possibilidade de ser acessada por outras transações. Mas no final, uma versão de linha desatualizada ou excluída não terá mais interesse para nenhuma transação. O espaço ocupado deve ser recuperado para ser reutilizado pelas novas linhas, evitando um crescimento sem fim da necessidade de espaço em disco. Isto é feito executando o comando VACUUM.

Obviamente, uma tabela que recebe atualizações ou exclusões freqüentes necessita ser limpa com mais freqüência que uma tabela que é atualizada raramente. Pode ser útil configurar tarefas periódicas no aplicativo cron para limpar apenas determinadas tabelas, omitindo as tabelas sabidamente pouco modificadas. Provavelmente, este procedimento é útil apenas quando há tanto tabelas muito atualizadas quanto tabelas raramente atualizadas — o custo adicional para limpar uma tabela pequena não é suficiente para valer a pena se preocupar com isto.

Existem duas variações do comando VACUUM. A primeira forma, conhecida como "limpeza preguiçosa" (lazy vacuum), ou simplesmente VACUUM, marca os dados expirados das tabelas para reutilização posterior; não tenta recuperar o espaço utilizado pelos dados expirados imediatamente. Portanto, o arquivo da tabela não é encurtado, e o espaço não utilizado no arquivo não é devolvido ao sistema operacional. Esta variante do VACUUM pode ser executada durante a operação normal do banco de dados.

A segunda forma é o comando VACUUM FULL. Esta forma utiliza um algoritmo mais agressivo para recuperar o espaço consumido pelas versões de linha expiradas. Todo espaço liberado pelo VACUUM FULL é imediatamente devolvido ao sistema operacional. Infelizmente, esta variante do comando VACUUM obtém um bloqueio exclusivo de cada tabela enquanto esta é processada pelo comando VACUUM FULL. Portanto, a utilização freqüente do comando VACUUM FULL pode ter um efeito extremamente negativo sobre o desempenho dos comandos simultâneos no banco de dados.

A forma padrão do comando VACUUM é melhor empregada com o objetivo de manter o nível de utilização de espaço em disco razoavelmente estável. Se for necessário devolver espaço em disco para o sistema operacional, pode ser utilizado o comando VACUUM FULL — mas qual é a vantagem de liberar espaço em disco que deverá ser alocado novamente em breve? Execuções do comando VACUUM padrão com freqüência moderada é uma abordagem melhor que a execução do comando VACUUM FULL com baixa freqüência, para manutenção de tabelas muito atualizadas.

A prática recomendada para a maioria das instalações é agendar o comando VACUUM para todo o banco de dados uma vez por dia em horário de pouca utilização, suplementado por limpezas mais freqüentes das tabelas muito atualizadas se for necessário (Algumas instalações com taxas muito alta de modificação dos dados executam o comando VACUUM em tabelas muito atualizadas uma vez a cada poucos minutos). Havendo vários bancos de dados em um agrupamento, não deve ser esquecido de limpar cada um deles; o programa vacuumdb pode ser útil.

Dica: O programa contrib/pg_autovacuum pode ser útil para automatizar operações de limpeza com alta freqüência.

O comando VACUUM FULL é recomendado para os casos onde se sabe que foi excluída a maior parte das linhas da tabela e, portanto, o tamanho estável da tabela pode ser reduzido substancialmente pela abordagem mais agressiva do comando VACUUM FULL. Deve ser utilizado o VACUUM simples, e não o VACUUM FULL, para recuperação rotineira de espaço.

Havendo uma tabela cujo conteúdo é excluído periodicamente, deve ser considerado executar o comando TRUNCATE em vez de utilizar DELETE seguido por VACUUM. O comando TRUNCATE remove todo o conteúdo da tabela imediatamente, não sendo necessário executar o comando VACUUM ou VACUUM FULL em seguida para recuperar o espaço que não está mais sendo utilizado.

21.1.2. Atualização das estatísticas do planejador

O planejador de comandos do PostgreSQL depende das informações estatísticas sobre o conteúdo das tabelas para poder gerar bons planos para os comandos. Estas estatísticas são coletadas pelo comando ANALYZE, que pode ser chamado por si próprio ou como um passo opcional do comando VACUUM. É importante que as estatísticas estejam razoavelmente precisas, senão o desempenho do banco de dados poderá ser degradado por planos mal escolhidos.

Assim como a execução do comando VACUUM para recuperar espaço, atualizações freqüentes das estatísticas são mais úteis para tabelas muito atualizadas que para tabelas raramente atualizadas. Porém, mesmo nas tabelas muito atualizadas, pode não ser necessário atualizar as estatísticas, se a distribuição dos dados não mudar muito. Uma regra empírica simples é pensar sobre quanto os valores mínimo e máximo das colunas da tabela mudam. Por exemplo, uma coluna timestamp contendo a data e hora da atualização da linha terá um valor máximo aumentando continuamente à medida que são atualizadas ou adicionadas linhas à tabela; este tipo de coluna, provavelmente, precisa de atualizações mais freqüentes das estatísticas do que, digamos, uma coluna contendo URLs de páginas acessadas em sítios da Web. A coluna URL pode ser modificada com a mesma freqüência, mas a distribuição estatística de seus valores provavelmente muda de forma relativamente lenta.

É possível executar o comando ANALYZE em tabelas específicas, e mesmo em colunas específicas da tabela. Portanto, existe flexibilidade para atualizar algumas estatísticas com mais freqüência que outras se for requerido pelo aplicativo. Entretanto, na prática a utilidade desta funcionalidade é duvidosa. A partir do PostgreSQL 7.2 o comando ANALYZE se tornou uma operação bem rápida, mesmo em tabelas grandes, porque utiliza uma amostra aleatória das linhas da tabela, em vez de ler todas as linhas da tabela. Portanto, provavelmente é mais simples executá-lo para todo o banco de dados na freqüência desejada.

Dica: Embora possa não ser muito produtivo aumentar a freqüência de execução do comando ANALYZE por coluna, pode valer a pena fazer ajustes por coluna do nível de detalhe das estatísticas coletadas pelo comando ANALYZE. Colunas que são muito utilizadas em cláusula WHERE, e que contém uma distribuição de dados muito irregular, podem requerer um histograma dos dados com granulação mais fina que as demais colunas. Consulte o comando ALTER TABLE SET STATISTICS.

A prática recomendada, para a maioria das instalações, é agendar a execução do comando ANALYZE para todo o banco de dados uma vez por dia, em horário de pouca utilização; é útil sua combinação com a execução do comando VACUUM todas as noites. Entretanto, nas instalações onde as estatísticas das tabelas mudam de forma relativamente lenta, pode-se considerar que esta freqüência seja demasiada, e que a execução do comando ANALYZE com uma freqüência mais baixa seja suficiente.

21.1.3. Prevenção de falhas devido ao reinício do ID de transação

A semântica de transação do MVCC do PostgreSQL depende de poder comparar números identificadores 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 ser enxergada pela transação corrente. Como os IDs de transação possuem tamanho limitado (32 bits quando esta documentação foi escrita), um agrupamento em funcionamento por um longo período de tempo (mais de 4 bilhões de transações) sofre um reinício do ID de transação: o contador do XID volta a zero e, de repente, a 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 catastrófica (Na verdade os dados ainda estão lá, mas isto não serve de consolo se não é possível acessá-los).

Antes do PostgreSQL 7.2 a única defesa contra o reinício do XID era executar novamente o initdb pelo menos a cada 4 bilhões de transações. É claro que não era muito satisfatório para instalações com alto tráfego e, por isso, foi concebida uma solução melhor. A nova abordagem permite o servidor permanecer ativo indefinidamente, sem executar o initdb ou qualquer forma de reinício. O preço é a necessidade desta manutenção: todas as tabelas do banco de dados devem ser VACUUM-nizadas pelo menos uma vez a cada um bilhão de transações.

Na prática este não é um requisito oneroso, mas uma vez que a conseqüência de não respeitá-lo pode ser a perda total dos dados (e não apenas desperdício de espaço em disco ou degradação do desempenho), foram introduzidos alguns dispositivos especiais para ajudar os administradores de banco de dados acompanharem o tempo decorrido desde que o comando VACUUM foi executado pela última vez. O restante desta seção fornece os detalhes.

A nova abordagem para comparação de XID faz distinção de dois XIDs especiais, os de número 1 e 2 (BootstrapXID e FrozenXID). Estes dois XIDs são sempre considerados mais antigos que qualquer XID normal. Os XIDs normais (àqueles maiores que 2) são comparados utilizando a aritmética de módulo-231. Isto significa que para todo XID normal existem dois bilhões de XIDs que são "mais antigos" e dois bilhões que são "mais novos"; outra maneira disto ser dito é que o espaço do XID normal é circular, sem ponto de término. Portanto, ao ser criada uma versão de linha com um determinado XID normal, esta versão de linha vai parecer estar "no passado" para as próximas dois bilhões de transações, não importando de qual XID normal está se falando. Se a versão da linha ainda existir após mais de dois bilhões transações, de repente vai parecer estar no futuro. Para evitar perda de dados, deve ser atribuído o XID FrozenXID para as versões antigas das linhas algum tempo antes de atingirem a marca "antiga-dois-bilhões-de-transações". Uma vez que tenha sido atribuído este XID especial, vai parecer estar "no passado" para todas as transações normais, a despeito dos problemas de reinício, e esta versão de linha será válida até ser excluída, não importando quanto demore. Esta reatribuição de XID é tratada pelo comando VACUUM.

A maneira normal de agir do comando VACUUM é atribuir o FrozenXID para toda versão de linha que possua um XID normal antigo que esteja mais de um bilhão de transações no passado. Esta política preserva o XID original de inserção até que, provavelmente, não seja mais de interesse (Na verdade, a maioria das versões de linha provavelmente vivem e morrem sem que jamais tenham sido "congeladas"). Com esta política, o intervalo máximo seguro entre execuções do comando VACUUM em qualquer tabela é de exatamente um bilhão de transações: se for esperado mais tempo, é possível que uma versão de linha que da ultima vez não era antiga o suficiente para ser congelada, agora esteja antiga mais de dois bilhões de transações, e tenha passado para o futuro — ou seja, foi perdida (É claro que vai reaparecer após outros dois bilhões de transações, mas isto não ajuda).

Uma vez que a execução periódica do comando VACUUM é necessária devido aos motivos descritos anteriormente, é pouco provável que alguma tabela não tenha sido limpa pelo tempo de um bilhão de transações. Para ajudar os administradores a garantir que esta restrição é obedecida, o comando VACUUM armazena estatísticas sobre ID de transação na tabela do sistema pg_database. Em particular, ao término da operação de limpeza de todo o banco de dados (ou seja, o comando VACUUM sem especificação de um nome de tabela), é atualizada a coluna datfrozenxid da linha do banco de dados na tabela pg_database. O valor armazenado neste campo é o XID do ponto de corte de congelamento utilizado pelo comando VACUUM. Há garantia que, neste banco de dados todos, os XIDs mais antigos que este XID de ponto de corte foram substituídos pelo FrozenXID. Uma maneira conveniente de examinar esta informação é executar a consulta:

SELECT datname, age(datfrozenxid) FROM pg_database;

A coluna age (idade) mede o número de transações desde o XID de corte de congelamento até o XID da transação corrente.

Com a política de congelamento padrão, a coluna age começa em um bilhão para um banco de dados onde o comando VACUUM acabou de ser executado. Quando age se aproxima de dois bilhões, deve ser executado novamente o comando VACUUM no banco de dados para evitar o risco da falha devido ao reinício. A prática recomendada é executar o comando VACUUM em todos os bancos de dados pelo menos uma vez a cada meio bilhão (500 milhões) de transações, para que se tenha uma ampla margem de segurança. Para ajudar a obedecer esta regra, cada execução do comando VACUUM de todo o banco de dados emite, automaticamente, uma advertência caso haja alguma entrada em pg_database mostrando uma idade de mais 1,5 bilhões de transações. Por exemplo:

play=# VACUUM;
WARNING:  some databases have not been vacuumed in 1613770184 transactions
HINT:  Better vacuum them within 533713463 transactions, or you may have a wraparound failure.
VACUUM

O comando VACUUM com a opção FREEZE utiliza uma política de congelamento mais agressiva: as versões das linhas são congeladas se forem antigas o suficiente para serem consideradas boas por todas as transações em aberto. Em particular, se o comando VACUUM FREEZE for executado em um banco de dados não utilizado de outra forma, é garantido que todas as versões de linha neste banco de dados serão congeladas. Portanto, enquanto o banco de dados não for modificado de forma alguma, não será necessário executar o comando VACUUM para evitar o problema de reinício do ID de transação. Esta técnica é utilizada pelo initdb para preparar o banco de dados template0. Também deve ser utilizada para preparar todos os bancos de dados criados pelo usuário a serem marcados com datallowconn = false em pg_database, uma vez que não há nenhuma maneira conveniente de executar o comando VACUUM em um banco de dados em que não se pode conectar. Deve ser observado que a mensagem de advertência automática do comando VACUUM sobre bancos de dados não limpos ignoram as entradas de pg_database com datallowconn = false, para evitar emitir falsas advertências sobre estes bancos de dados; portanto, é responsabilidade de quem o faz garantir que estes bancos de dados sejam congelados corretamente.

Atenção

Para garantir a segurança contra reinício de transação é necessário limpar todas as tabelas, inclusive os catálogos do sistema, em todos os bancos de dados, pelo menos uma vez a cada bilhão de transações. Já foi visto perda de dados causadas por pessoas que decidiram que bastava limpar suas próprias tabelas de usuário ativas. Isto vai parecer que funciona bem, mas só por algum tempo.

SourceForge.net Logo CSS válido!