AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 174663
Accepted
48347
48347
Asked: 2017-05-27 03:56:44 +0800 CST2017-05-27 03:56:44 +0800 CST 2017-05-27 03:56:44 +0800 CST

Como `vacuumlo` um banco de dados RDS PostgreSQL?

  • 772

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_largeobjecttabela 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?

postgresql amazon-rds
  • 2 2 respostas
  • 4721 Views

2 respostas

  • Voted
  1. Best Answer
    48347
    2017-05-27T03:56:44+08:002017-05-27T03:56:44+08:00

    Encontre aqui (no espelho GitHub do repositório oficial do PostgreSQL) as fontes da vacuumloversã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.

    => SET search_path = pg_catalog;
    [...]
    => CREATE TABLE vacuum_lo_removeme AS \
            SELECT oid AS lo FROM pg_largeobject_metadata;
    [...]
    => ANALYZE vacuum_lo_removeme;
    [...]
    => _
    

    1.2. Limite a tabela de lob temporária.

    Execute a consulta que retorna todas as colunas do seu banco de dados digitadas OID:

    => SELECT
         s.nspname, c.relname, a.attname FROM pg_class c, pg_attribute a
         , pg_namespace s, pg_type t
       WHERE
         a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid
         AND a.atttypid = t.oid AND c.relnamespace = s.oid 
         AND t.typname in ('oid', 'lo') AND c.relkind in ('r','m')
         AND s.nspname !~ '^pg_';
    

    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:

    => DELETE FROM vacuum_lo_removeme WHERE \
            lo IN (SELECT ${column} FROM ${SCHEMA}.${TABLE});
    

    No meu caso, foram apenas duas tabelas totalizando cinco colunas e, na verdade, ambas as tabelas vazias, então, naturalmente, as cinco DELETEconsultas 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 locélulas restantes da tabela temporária, limpando-as com uma lo_unlinkchamada 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:

    $ echo 'SELECT COUNT(*) FROM vacuum_lo_removeme;' | \
            $MY_AUTOMATABLE_PSQL_MILEAGE 
      count  
    ---------
     1117233
    (1 row)
    

    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:

    $ for i in {1..1117000}; do \
            export oid=$(echo 'SELECT * FROM vacuum_lo_removeme LIMIT 1' | \
            $MY_AUTOMATABLE_PSQL_MILEAGE | grep -v 'lo\|\-\-\-\-\|row\|^$' | \
            sed s/\ //g) && \
            echo "SELECT lo_unlink($oid); \
                  DELETE FROM vacuum_lo_removeme WHERE lo = $oid" | \
                  $MY_AUTOMATABLE_PSQL_MILEAGE; \
            done
     lo_unlink 
    -----------
             1
    (1 row)
    
    DELETE 1
     lo_unlink 
    -----------
             1
    (1 row)
    
    DELETE 1
     lo_unlink 
    -----------
             1
    (1 row)
    
    DELETE 1
     lo_unlink 
    -----------
             1
    (1 row)
    
    DELETE 1
    ERROR:  must be owner of large object 18448
    DELETE 1
    ERROR:  must be owner of large object 18449
    DELETE 1
    ERROR:  must be owner of large object 18450
    DELETE 1
    ERROR:  must be owner of large object 18451
    [...]
     lo_unlink 
    -----------
             1
    (1 row)
    
    DELETE 1
    [...]
    

    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:

    => CREATE OR REPLACE FUNCTION unlink_orphan_los() RETURNS VOID AS $$
    DECLARE
      iterator integer := 0;
      largeoid OID;
      myportal CURSOR FOR SELECT lo FROM vacuum_lo_removeme;
    BEGIN
      OPEN myportal;
      LOOP
        FETCH myportal INTO largeoid;
        EXIT WHEN NOT FOUND;
        PERFORM lo_unlink(largeoid);
        DELETE FROM vacuum_lo_removeme WHERE lo = largeoid;
        iterator := iterator + 1;
        RAISE NOTICE '(%) removed lo %?', iterator, largeoid;
        IF iterator = 100 THEN EXIT; END IF;
      END LOOP;
    END;$$LANGUAGE plpgsql;
    

    OBSERVAÇÃO não é necessário desvincular objetos grandes 100por vez, nem mesmo um número específico xdeles por vez, mas desvincular 100por 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

    $ for i in {0..$WHATEVER}; do echo 'SELECT unlink_orphan_los()' | \
            $YOUR_AUTOMATABLE_PSQL_MILEAGE
    

    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 de bashacho que nem preciso saber qual é o maior número de lo_unlinks que o RDBMS está permitindo com current max_locks_per_transaction.

    3. VACUUMgrandes tabelas de objetos.

    Sugerido por este tópico na lista de discussão postgres , entendi que deveria VACUUM pg_largeobjectdepois 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:

    insira a descrição da imagem aqui

    insira a descrição da imagem aqui

    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 único VACUUM FULL, mas sem adquirir um bloqueio exclusivo.

    • 5
  2. k_o_
    2019-01-14T07:23:31+08:002019-01-14T07:23:31+08:00

    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_catalognã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.

    1. Criar tabela tmp 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.

    1. Encontre todos os nomes de tabelas e colunas onde um OID é usado:
    SELECT c.relname as tablename, a.attname as columnname FROM pg_class c, pg_attribute a, pg_namespace s, pg_type t WHERE a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid AND a.atttypid = t.oid AND c.relnamespace = s.oid AND t.typname in ('oid', 'lo') AND c.relkind in ('r','m') AND s.nspname !~ '^pg_' AND relname != 'vacuum_lo_removeme'
    
    1. Itere sobre todas as tabelas e remova os objetos grandes ainda usados ​​da tabela tmp:
    DELETE FROM vacuum_lo_removeme WHERE lo IN (SELECT ${columnname} FROM ${tablename}
    

    Exemplo: estou usando Java aqui ( rsé o ResultSet da declaração anterior):

                while (rs.next()) {
                    String tablename = rs.getString("tablename");
                    String columnname = rs.getString("columnname");
                    // remove OID which must be kept
                    String removeUsedOids = String.format("DELETE FROM vacuum_lo_removeme WHERE lo IN (SELECT \"%s\" FROM \"%s\")",
                            columnname, tablename);
                    stmt = connection.createStatement();
                    stmt.executeUpdate(removeUsedOids);
                }
    
    1. Remover OAs órfãos
    SELECT count(lo_unlink(lo)) from vacuum_lo_removeme where lo in (select loid from pg_catalog.pg_largeobject)
    
    1. Soltar banco de dados temporário
    DROP table vacuum_lo_removeme
    
    • 1

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve