Como você deve ter notado, como um banco de dados totalmente gerenciado como um produto de serviço, o AWS Relational Data base Service (RDS) restringe a execução de comandos userland.
Eu estava preparando um banco de dados de produção e minha pg_largeobject
tabela inchou para 40% de toda a capacidade do dispositivo virtual de persistência.
Como posso executar vacuumlo
(lindamente bem explicado em outra pergunta do DBA.SE aqui ) em uma instância do RDS executando o banco de dados PostgreSQL?
Encontre aqui (no espelho GitHub do repositório oficial do PostgreSQL) as fontes da
vacuumlo
versão do banco de dados PostgreSQL que eu estava usando naquele exato momento.Você pode imaginar o resto: eu apenas imito o que o programa está realizando, também simplesmente descrito aqui .
1. Construção de mesa temporária.
Prepare a tabela temporária de referências de objeto ou OIDs.
1.1. Tabela de lob temporária como cópia da tabela de lob completa.
1.2. Limite a tabela de lob temporária.
Execute a consulta que retorna todas as colunas do seu banco de dados digitadas OID:
Em seguida, você deve executar esta consulta para todos os resultados obtidos pela consulta anterior (observe
${VARIABLE}
que você deve substituir de acordo com sua milhagem) para remover da tabela temporária todos os OIDs de objetos realmente em uso:No meu caso, foram apenas duas tabelas totalizando cinco colunas e, na verdade, ambas as tabelas vazias, então, naturalmente, as cinco
DELETE
consultas não fizeram nada. Se você tiver um subesquema habilitado para OID maior , talvez seja necessário automatizar isso de alguma forma.2. Desvinculação de objetos grandes.
Finalmente o programa declara um cursor que itera as
lo
células restantes da tabela temporária, limpando-as com umalo_unlink
chamada de função.2.A. Não faça desta forma.
Eu deveria ter automatizado isso com um procedimento armazenado PLPGSQL, mas como sou péssimo nesse tipo de tarefa, acabei de emitir este forro:
Então este outro que itera selecionando o primeiro OID órfão na tabela temporária de OIDs órfãos, desvinculando-o e removendo-o da tabela:
Eu sabia que estava subotimizado pra caramba, mas deixei que removesse lentamente esses registros órfãos. Quando não é DBA, esses one-liners podem ser mais fáceis de forjar do que trabalhar algum PLPGSQL idiomático significativo.
Mas isso era muito lento para deixá-lo assim.
2.B. Faça isso melhor do que 2.A (embora ainda não seja uma bala de prata).
Você poderá acelerar a desvinculação de objetos grandes com algo simples, como:
OBSERVAÇÃO não é necessário desvincular objetos grandes
100
por vez, nem mesmo um número específicox
deles por vez, mas desvincular100
por vez é a base mais segura aplicável à configuração de memória padrão de todos os tamanhos de instância da AWS. Se você usar um número excessivamente grande para isso, corre o risco de falha de função devido à memória atribuída insuficientemente; como você pode fazer dependerá da quantidade de objetos a serem desvinculados e seu tamanho, do tipo e tamanho da instância e do grau de configuração manual aplicada.O que é um pouco fácil de forjar para uma pessoa que não é DBA, chamando-o com algo como
Onde
${WHATEVER}
é uma constante construída dependendo do tamanho da tabela dos objetos grandes temporários e do número de bloqueios por transação que sua configuração está permitindo (estou usando os padrões do RDS, mas iterando debash
acho que nem preciso saber qual é o maior número delo_unlink
s que o RDBMS está permitindo com currentmax_locks_per_transaction
.3.
VACUUM
grandes tabelas de objetos.Sugerido por este tópico na lista de discussão postgres , entendi que deveria
VACUUM
pg_largeobject
depois de desvincular os objetos.Não tenho certeza de qual deles é o conjunto mínimo necessário, ou o tempo adequado para sua execução, mas alguns deles podem ajudar, e nenhum deve causar nenhum dano: executei
VACUUM ANALYZE VERBOSE pg_largeobject_metadata; VACUUM ANALYZE VERBOSE pg_largeobject; VACUUM FULL ANALYZE VERBOSE pg_largeobject_metadata; VACUUM FULL ANALYZE VERBOSE pg_largeobject;
várias vezes enquanto a micro instância estava desvinculando os objetos ( levou muito tempo, infelizmente), primeiro quando cerca de 1/4 dos objetos já foram desvinculados e algum pouco armazenamento foi devolvido ao sistema operacional, em segundo lugar quando cerca de 1/3 dos objetos já foram desvinculados e outro pequeno armazenamento foi devolvido ao SO, em terceiro lugar, quando cerca de 3/5 objetos já foram desvinculados, a instância sofreu um retorno de armazenamento maciço ao SO:Esse retorno maciço de armazenamento era o que eu estava procurando. Depois de executar a consulta para as maiores tabelas encontradas na página inicial oficial , com menos de 3/4 do total de objetos desvinculados, a tabela de objetos foi reduzida para menos de 3GiB, longe dos 20GiB iniciais e ainda mais inchados.
OBSERVAÇÃO automatizar a iteração rápida
VACUUM ANALYZE
em tabelas tem os mesmos efeitos de longo prazo (de limpar a tabela do excesso de armazenamento em disco) que executar um únicoVACUUM FULL
, mas sem adquirir um bloqueio exclusivo.Todos os créditos para https://dba.stackexchange.com/a/174664/83878 mas consegui simplificar a solução no meu caso. Há apenas um esquema no meu caso. Além disso, a primeira instrução
search_path = pg_catalog
não funciona no meu caso porque não tenho permissões para modificar o catálogo do sistema. Eu também poderia simplificar drasticamente o procedimento de desvinculação com apenas uma única instrução.vacuum_lo_removeme
:CRIAR TABELA SE NÃO EXISTE vacuo_lo_removeme AS SELECT oid AS lo FROM pg_catalog.pg_largeobject_metadata
Nota: Isso é criado no esquema padrão agora.
Exemplo: estou usando Java aqui (
rs
é o ResultSet da declaração anterior):