Eu tenho um banco de dados no PostgreSQL 9.2 que possui um esquema principal com cerca de 70 tabelas e um número variável de esquemas por cliente estruturados de forma idêntica, com 30 tabelas cada. Os esquemas do cliente têm chaves estrangeiras que referenciam o esquema principal e não o contrário.
Comecei a preencher o banco de dados com alguns dados reais extraídos da versão anterior. O banco de dados atingiu cerca de 1,5 GB (espera-se aumentar para vários 10s GB em semanas) quando eu tive que fazer uma exclusão em massa em uma tabela muito central no esquema principal. Todas as chaves estrangeiras envolvidas estão marcadas em EXCLUIR CASCADE.
Não foi surpresa que isso levasse muito tempo, mas após 12 horas ficou claro que era melhor começar de novo, abandonando o banco de dados e iniciando a migração novamente. Mas e se eu precisar repetir essa operação mais tarde, quando o banco de dados estiver ativo e muito maior? Existem métodos alternativos e mais rápidos?
Seria muito mais rápido se eu escrevesse um script que navegasse pelas tabelas dependentes, começando na tabela mais distante da tabela central, excluindo as linhas dependentes tabela por tabela?
Um detalhe importante é que existem gatilhos em algumas das tabelas.
Respostas:
Eu tive um problema parecido. Acontece que esses
ON DELETE CASCADE
gatilhos estavam atrasando bastante as coisas, porque essas exclusões em cascata eram terrivelmente lentas.Resolvi o problema criando índices nos campos de chave estrangeira nas tabelas de referência e passei de algumas horas para a exclusão a alguns segundos.
fonte
ON DELETE CASCADE
)EXPLAIN (ANALYZE, BUFFERS)
consulta em uma única exclusão de linha e deve mostrar quais restrições de chave estrangeira demoraram mais (pelo menos para mim).PRIMARY
índice é suficiente, mas oUNIQUE
índice definitivamente não é bom o suficiente para esse fim.Você tem poucas opções. A melhor opção é executar uma exclusão em lote para que os gatilhos não sejam acionados. Desative os gatilhos antes de excluir e, em seguida, reative-os. Isso economiza uma quantidade muito grande de tempo. Por exemplo:
Uma chave importante aqui é que você deseja minimizar a profundidade das subconsultas. Nesse caso, convém configurar tabelas temporárias para armazenar informações relevantes, para evitar subconsultas profundas ao excluir.
fonte
O método mais fácil de resolver o problema é a consulta tempo detalhada do PostgreSQL:
EXPLAIN
. Para isso, você precisa encontrar no mínimo uma única consulta concluída, mas que leva mais tempo do que o esperado. Digamos que essa linha se pareceriaEm vez de realmente executar esse comando, você pode executar
A reversão no final permite executar isso sem realmente modificar o banco de dados, mas você ainda obtém o tempo detalhado do que levou quanto. Depois de executar isso, você pode descobrir na saída que algum gatilho causa grandes atrasos:
Como
time
está em ms (milissegundo), a verificação dessa contraint levou cerca de 12,3 segundos. Você precisa adicionar um novoINDEX
sobre as colunas necessárias para que esse gatilho possa ser calculado com eficiência. Para referências de chave estrangeira, a coluna que faz referência a outra tabela deve ser indexada (ou seja, a coluna de origem, não a coluna de destino). O PostgreSQL não cria automaticamente esses índices para você eDELETE
é a única consulta comum em que você realmente precisa desse índice. Como resultado, você pode acumular anos de dados até chegar ao caso em queDELETE
é muito lento devido à falta de um índice.Depois de corrigir o desempenho dessa restrição (ou outra coisa que demorou muito tempo), repita o comando in
begin
/rollback
block para comparar o novo tempo de execução com o anterior. Continue até que você esteja satisfeito com o tempo de resposta de exclusão de linha única (eu tenho uma consulta para ir de 25,6 segundos a 15 ms simplesmente adicionando índices diferentes). Em seguida, você pode prosseguir para concluir sua exclusão completa sem hacks.(Observe que
EXPLAIN
precisa de uma consulta que possa ser concluída com êxito. Certa vez, tive um problema em que o PostgreSQL demorou muito para descobrir que uma exclusão violaria uma restrição de chave estrangeira e, nesse caso,EXPLAIN
não pode ser usada porque não emitirá tempo para falha. Não conheço nenhuma maneira fácil de depurar problemas de desempenho nesse caso.)fonte
Desativar gatilhos pode ser uma ameaça à integridade do banco de dados e não pode ser recomendado; no entanto, se você tiver certeza de que sua operação é à prova de restrições, poderá desativar os acionadores, com o seguinte:
SET session_replication_role = replica;
Execute o
DELETE
aqui.Para restaurar gatilhos, execute:
SET session_replication_role = DEFAULT;
Fonte aqui.
fonte
Se você tiver gatilhos ON DELETE CASCADE, esperamos que eles estejam lá por um motivo e, portanto, não devem ser desativados. Outro truque (ainda adicione seus índices) que funciona para mim é criar uma função de exclusão que exclui manualmente os dados começando com as tabelas no final da cascata e trabalha na direção da tabela principal. (É o mesmo que você faria se tivesse um gatilho ON DELETE RESTRICT)
Nesse caso, exclua os dados no tablec, em seguida, tableb e tablea
fonte