TOAST Table Growth Out of Control - FULLVAC não faz nada


9

Recentemente, tive um servidor PostgreSQL 8.2.11 atualizado para 8.4, a fim de tirar proveito dos recursos de autovacuum e estar alinhado com outros 30 servidores PGSQL. Isso foi feito por um grupo de TI separado que administra o hardware, para que não tenhamos muitas opções em outras atualizações (não haverá mais de 9 anos por um tempo). O servidor existe em um ambiente muito fechado (rede isolada, privilégios de root limitados) e é executado no RHEL5.5 (i686). Após a atualização, o banco de dados aumentou constantemente para 5-6 GB por dia. Normalmente, o banco de dados, como um todo, tem ~ 20 GB; atualmente, é ~ 89GB. Temos outros servidores que executam bancos de dados equivalentes e, na verdade, sincronizam os registros entre si por meio de um aplicativo de terceiros (um que eu não tenho acesso ao funcionamento interno). Os outros bancos de dados têm aproximadamente 20 GB como deveriam.

Executando o SQL a seguir, é bastante óbvio que há um problema com uma tabela específica e, mais especificamente, sua tabela TOAST.

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;

Qual produz:

              relação | Tamanho  
------------------------------------ + ---------  
  pg_toast.pg_toast_16874 | 89 GB  
  fews00.warmstates | 1095 MB  
  ...  
(20 linhas)

Esta tabela TOAST é para uma tabela chamada "timeseries" que salva grandes registros de dados em blob. Um SUM(LENGTH(blob)/1024./1024.)de todos os registros nas séries temporais gera ~ 16 GB para essa coluna. Não deve haver razão para a tabela TOAST dessa tabela ser tão grande quanto é.

Eu executei um VACUUM FULL VERBOSE ANALYZE timeseriese o vácuo é executado sem erros.

INFO: limpando "pg_toast.pg_toast_16874"
INFO: "pg_toast_16874": encontrado 22483 removível, 10475318 versões de linhas não removíveis em 10448587 páginas
DETALHE: 0 versões de linhas inativas ainda não podem ser removidas.
As versões de linhas não removíveis variam de 37 a 2036 bytes.
Havia 20121422 ponteiros de itens não utilizados.
O espaço livre total (incluindo versões de linhas removíveis) é 0 bytes. As páginas 4944885 estão ou ficarão vazias, incluindo 0 no final da tabela. 4944885 páginas contendo 0 bytes livres são possíveis destinos de movimentação.
CPU 75.31s / 29.59u s decorridos 877.79 s.
INFO: o índice "pg_toast_16874_index" agora contém 10475318 versões de linha em 179931 páginas
DETALHE: 23884 versões de linha de índice foram removidas.
101623 páginas de índice foram excluídas, 101623 são atualmente reutilizáveis.
CPU 1.35s / 2.46u seg decorridos 21.07 seg.

REINDEXED a tabela que liberou algum espaço (~ 1 GB). Não consigo CLUSTER a tabela, pois não há espaço suficiente no disco para o processo, e estou esperando para reconstruir a tabela completamente, pois gostaria de descobrir por que ela é muito maior do que os bancos de dados equivalentes que temos.

Fiz uma consulta no wiki do PostgreSQL aqui - "Show Database Bloat" , e é isso que eu recebo:

banco de dados atual | schemaname | tablename | tbloat | wastedbytes | iname | ibloat wastedibytes  
----------------- + ------------ + ------------------- ------------- + -------- + ------------- + ------------- -------------------- + -------- + --------------  
ptrdb04 fews00 | séries temporais | 1.0 0 idx_timeseries_synchlevel | 0,0 | 0 0  
ptrdb04 fews00 | séries temporais | 1.0 0 idx_timeseries_localavail | 0,0 | 0 0  
ptrdb04 fews00 | séries temporais | 1.0 0 idx_timeseries_expirytime | 0,0 | 0 0  
ptrdb04 fews00 | séries temporais | 1.0 0 idx_timeseries_expiry_null | 0,0 | 0 0  
ptrdb04 fews00 | séries temporais | 1.0 0 uniq_localintid | 0,0 | 0 0  
ptrdb04 fews00 | séries temporais | 1.0 0 pk_timeseries | 0,1 0 0  
ptrdb04 fews00 | idx_timeseries_expiry_null | 0,6 | 0 ? | 0,0 | 0 0

Parece que o banco de dados não considera esse espaço como "vazio", mas não vejo de onde vem todo o espaço em disco!

Suspeito que este servidor de banco de dados esteja decidindo usar 4-5x tanto espaço em disco para salvar os mesmos registros extraídos dos outros servidores de dados. Minha pergunta é a seguinte: Existe uma maneira de verificar o tamanho do disco físico de uma linha? Eu gostaria de comparar o tamanho de uma linha nesse banco de dados com outro banco de dados "íntegro".

Obrigado por qualquer ajuda que você pode fornecer!

ATUALIZAÇÃO 1

Acabei reconstruindo a tabela a partir de um esquema despejado devido ao seu tamanho (não foi possível deixá-la sozinha por mais um dia). Após a sincronização dos dados, através do processo de sincronização do software, a tabela TOAST foi de ~ 35 GB; no entanto, eu poderia contabilizar apenas ~ 9 GB dessa coluna de blob, que deve ser a mais longa em termos de valores. Não sei de onde vêm os outros 26 GB. CLUSTERed, VACUUM CHEIO e REINDEXed sem sucesso. Os arquivos postgresql.conf entre os servidores de dados local e remoto são exatamente os mesmos. Existe alguma razão para esse banco de dados estar tentando armazenar cada registro com um espaço maior no disco?

ATUALIZAÇÃO 2 - Fixa

Finalmente, decidi reconstruir completamente o banco de dados desde o início - até reinstalar os pacotes do PostgreSQL84 no sistema. O caminho do banco de dados foi reinicializado e os espaços de tabela limpos. O processo de sincronização de software de terceiros repovoou as tabelas e o tamanho final ficou em ~ 12 GB ! Infelizmente, isso não ajuda em nada a resolver qual era a fonte exata do problema. Vou assistir por um dia ou dois e ver se há grandes diferenças em como o banco de dados revitalizado está lidando com a tabela TOAST e postar esses resultados aqui.

Tamanho da relação


ptrdb04=> SELECT nspname || '.' || relname AS "relation",
ptrdb04->     pg_size_pretty(pg_relation_size(C.oid)) AS "size"
ptrdb04->   FROM pg_class C
ptrdb04->   LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
ptrdb04->   WHERE nspname NOT IN ('pg_catalog', 'information_schema')
ptrdb04->   ORDER BY pg_relation_size(C.oid) DESC
ptrdb04->   LIMIT 2;

        relação          |   tamanho   
 ------------------------- + --------- 
 pg_toast . pg_toast_17269 | 18 GB 
 somes00 . warmstates        | 1224 MB
 ( 2 linhas )  

VACUUM VERBOSE ANALYZE timeseries;

INFO: "timeseries": encontrado 12699 removíveis, 681961 versões de linhas não removíveis em 58130 das 68382 páginas
DETALHE: 0 versões de linhas mortas ainda não podem ser removidas.
Havia 105847 ponteiros de itens não utilizados.
0 páginas estão completamente vazias.
CPU 0,83s / 2,08u s decorridos 33,36 s.
INFO: limpando "pg_toast.pg_toast_17269"
INFO: índice verificado "pg_toast_17269_index" para remover as versões de linha 2055849
DETALHE: CPU 0.37s / 2.92u seg decorridos 13.29 seg.
INFO: "pg_toast_17269": removidas 2055849 versões de linha em 518543 páginas
DETALHE: CPU 8.60s / 3.21u seg decorridos 358.42 seg.
INFO: o índice "pg_toast_17269_index" agora contém 7346902 versões de linha em 36786 páginas
DETALHE: as versões de linha de índice 2055849 foram removidas.
10410 páginas de índice foram excluídas, 5124 são atualmente reutilizáveis.
CPU 0.00s / 0.00u seg. Decorrido 0.01 seg.
INFO: "pg_toast_17269": encontrada 1286128 removível, 2993389 versões de linha não removíveis em 1257871 de 2328079 páginas
DETALHE: 0 versões de linhas mortas ainda não podem ser removidas.
Havia 18847 ponteiros de itens não utilizados.
0 páginas estão completamente vazias.
CPU 26.56s / 13.04u seg decorrido 714.97 seg.
INFO: analisando "poucas séries00.tempos"
INFO: "timeseries": digitalizadas 30000 de 68382 páginas, contendo 360192 linhas ativas e 0 linhas mortas; 30000 linhas na amostra, 821022 total de linhas estimadas

A única diferença perceptível após a reconstrução (além do uso do disco) é

INFO: "pg_toast_17269": encontrado 1286128 removível, 2993389 versões de linha não removíveis
como @CraigRinger mencionado em um comentário. A contagem de linhas não removíveis é muito menor do que antes.

Nova pergunta: outras tabelas podem afetar o tamanho de outra tabela? (por meio de chaves estrangeiras e coisas do tipo) A reconstrução da tabela não fez nada, mas a reconstrução de todo o banco de dados provou corrigir o problema.


Por que você não atualizou diretamente para a 9.2? Tem ainda mais melhorias na área de vácuo de 8.4 (e 8.4 vai EOL no próximo ano de qualquer maneira)
a_horse_with_no_name

Eu atualizei a postagem. A atualização não foi feita por nossa loja e não necessariamente por nossa solicitação. Infelizmente, não temos essa opção para atualizar para o 9+.
BrM13

ESTÁ BEM. Eu só queria ter certeza de que você não estava com vista para o óbvio;)
a_horse_with_no_name

Respostas:


9

Este:

INFO: "pg_toast_16874": found 22483 removable, 10475318 nonremovable row versions in 10448587 pages 22483 removable, 10475318 nonremovable row versions in 10448587 pages

sugere que o problema subjacente é que algo ainda pode "ver" essas linhas para que não possam ser removidas.

Os candidatos para isso são:

  • Transações preparadas perdidas. Cheque pg_catalog.pg_prepared_xacts; deve estar vazio. Também corra SHOW max_prepared_transactions; deve reportar zero.

  • Sessões de longa duração com uma transação aberta e ociosa. No PostgreSQL 8.4 e acima, isso deve ser apenas um problema para SERIALIZABLEtransações. Verifique pg_catalog.pg_stat_activitypara <IDLE> in transactionsessões.

Provavelmente, você tem um cliente que não está conseguindo confirmar ou reverter transações durante longos períodos inativos.

Se isso não acontecer, a próxima coisa que eu verificaria seria fazer uma soma octet_sizeda cada coluna da tabela de interesse. Compare isso com o pg_relation_sizeda tabela e sua TOASTmesa lateral. Se houver uma grande diferença, o espaço consumido provavelmente não terá mais linhas visíveis e você provavelmente terá problemas de inchaço na tabela. Se eles são bastante semelhantes, você pode começar a diminuir onde está o uso do espaço, resumindo os tamanhos dos octetos por coluna, obtendo os melhores valores de 'n' etc.


1) pg_prepared_xacts e max_prepared_transactions realmente voltaram vazios. 2) Definitivamente, existem algumas transações IDLE das SELECT * FROM pg_stat_activity WHERE current_query LIKE '<IDLE>%';quais traz de volta de 30 a 40 resultados; no entanto, isso parece bastante normal. Eu verifiquei alguns servidores "saudáveis" e eles eram os mesmos.
BrM13 04/07

3) Aqui está o que eu fiz. Em loop pelas colunas da série temporal, puxando octet_length (coluna). Multiplicou cada valor pela contagem de linhas e os somaram. Para séries temporais, obtive ~ 430 MB (próximo aos 493 MB de pg_relation_size) e 438 MB para a tabela TOAST (usando as colunas chunk_id, chunk_seq, chunk_data). As estimativas parecem corretas e a tabela TOAST está MUITO FORA do tamanho da relação em cerca de 2 ordens de grandeza (60 GB hoje). Parece que tenho inchaço, mas não do tipo tradicional (inchaço não utilizado). Caso contrário, um FULLVAC deve cuidar do problema.
BrM13 04/07

As sessões inativas do @Brad são boas, são apenas as sessões inativas com transações abertas que são um problema, ou seja <IDLE> in transaction, e somente se elas (a) estiverem ociosas por um tempo eb) estiverem usando SERIALIZABLEisolamento ou você estiver na versão 8.3 ou Mais velho.
Craig Ringer

@ Brad É interessante que apenas a TOASTtabela pareça estar inchada. BTW, se você estiver usando VACUUM FULLmuito em um servidor pré-9.0, você desejará, REINDEXpois VACUUM FULLnessas versões pode causar inchaço significativo no índice. Agora estou me perguntando se alguém colocou um absurdo FILLFACTORna mesa de brinde, embora isso não deva deixar você passar o consumo de espaço em 10x.
Craig Ringer

Obrigado pelo esclarecimento IDLE. Achei que era isso que você queria dizer, mas é bom saber com certeza. Quanto ao FILLFACTOR, a tabela está usando o padrão. FYI - De acordo com a documentação 8.4 CREATE TABLE, o padrão é 100 e você não pode definir um FILLFACTOR para a tabela TOAST.
BrM13

0

Eu não tenho nenhuma idéia de por que isso está inchado. Mas fiz algumas pesquisas e talvez este link tenha algumas dicas: http://postgresql.1045698.n5.nabble.com/A-154-GB-table-swelled-to-527-GB-on-the-Slony-slave -Como-compactar-td5543034.html ... Não é a sua situação exata, mas talvez ela esteja perto o suficiente para ajudá-lo a chegar ao fundo do inchaço fantasma.

No entanto, acho que a única maneira de compactar essa tabela nesse momento é CLUSTER. Como você está com pouco espaço em disco, isso é um problema.

Aqui está minha sugestão para isso: crie um espaço de tabela em uma unidade diferente com muito espaço extra e atribua sua tabela de problemas a esse espaço de tabela. O PostgreSQL copiará a tabela para o novo espaço de tabela (provavelmente bloqueando-a no processo, portanto, você precisará de uma janela de manutenção). Em seguida, VACFULL a tabela (limpa a maior parte do espaço antigo consumido pela tabela no espaço de tabela padrão). Depois, CLUSTER a mesa e ela deve se compactar. Em seguida, coloque-o novamente no espaço de tabela padrão e execute VACFULL novamente (para limpar o espaço não utilizado no novo espaço de tabela).


Acabei reconstruindo a tabela (despejando o esquema e reconstruindo a partir disso) e puxando os dados diretamente sobre um dos bancos de dados remotos. Depois que o processo foi concluído, o banco de dados ainda possuía 35 GB, sendo que apenas 9 GB foram contabilizados pela coluna "larga" de blob. CLUSTERed, VACUUM CHEIO, REINDEXed, e ainda estou sentado em uma tonelada de uso misterioso do disco.
BrM13 03/07/2013

O link está morto :(
hayd 8/08/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.