Eu gerencio um aplicativo que tem um back-end de banco de dados Oracle muito grande (quase 1 TB de dados com mais de 500 milhões de linhas em uma tabela). O banco de dados realmente não faz nada (sem SPocs, sem gatilhos ou qualquer coisa), é apenas um armazenamento de dados.
Todos os meses, somos obrigados a limpar os registros das duas tabelas principais. Os critérios para a eliminação variam e são uma combinação de idade da linha e alguns campos de status. Normalmente, acabamos limpando entre 10 e 50 milhões de linhas por mês (adicionamos cerca de 3 a 5 milhões de linhas por semana por meio de importações).
Atualmente, temos que fazer essa exclusão em lotes de cerca de 50.000 linhas (ou seja, excluir 50.000, confirmar, excluir 50.000, confirmar, repetir). A tentativa de excluir todo o lote de uma só vez faz com que o banco de dados não responda por cerca de uma hora (dependendo do número de linhas). Excluir as linhas em lotes como este é muito difícil para o sistema e normalmente temos que fazer isso "conforme o tempo permitir" ao longo de uma semana; permitir que o script seja executado continuamente pode resultar em uma degradação de desempenho que é inaceitável para o usuário.
Acredito que esse tipo de exclusão em lote também degrada o desempenho do índice e tem outros impactos que eventualmente causam a degradação do desempenho do banco de dados. Existem 34 índices em apenas uma tabela, e o tamanho dos dados do índice é realmente maior do que os próprios dados.
Aqui está o script que um de nossos funcionários de TI usa para fazer essa limpeza:
BEGIN
LOOP
delete FROM tbl_raw
where dist_event_date < to_date('[date]','mm/dd/yyyy') and rownum < 50000;
exit when SQL%rowcount < 49999;
commit;
END LOOP;
commit;
END;
Este banco de dados deve estar em 99,99999% e só temos uma janela de manutenção de 2 dias uma vez por ano.
Estou procurando um método melhor para remover esses registros, mas ainda não encontrei nenhum. Alguma sugestão?
A lógica com 'A' e 'B' pode estar "escondida" atrás de uma coluna virtual na qual você pode fazer o particionamento:
A solução clássica para isso é particionar suas tabelas, por exemplo, por mês ou por semana. Se você não os encontrou antes, uma tabela particionada é como várias tabelas estruturadas de forma idêntica com um implícito
UNION
ao selecionar, e o Oracle armazenará automaticamente uma linha na partição apropriada ao inseri-la com base nos critérios de particionamento. Você menciona índices - bem, cada partição também recebe seus próprios índices particionados. É uma operação muito barata no Oracle descartar uma partição (é análogo a umTRUNCATE
em termos de carga porque é isso que você está realmente fazendo - truncando ou descartando uma dessas subtabelas invisíveis). Será uma quantidade significativa de processamento para particionar "após o fato", mas não faz sentido chorar sobre o leite derramado - as vantagens de fazer até agora superam os custos. Todo mês você dividiria a partição superior para criar uma nova partição para os dados do próximo mês (você pode automatizar isso facilmente com umDBMS_JOB
).E com partições você também pode explorar consultas paralelas e eliminação de partições , o que deve deixar seus usuários muito felizes...
Um aspecto a ser considerado é quanto do desempenho de exclusão resulta dos índices e quanto da tabela bruta. Cada registro excluído da tabela requer a mesma exclusão da linha de cada índice btree. Se você tiver mais de 30 índices btree, suspeito que a maior parte do seu tempo seja gasto na manutenção do índice.
Isso tem um impacto na utilidade do particionamento. Digamos que você tenha um índice no nome. Um índice Btree padrão, tudo em um segmento, pode ter que fazer quatro saltos para ir do bloco raiz para o bloco folha e uma quinta leitura para obter a linha. Se esse índice for particionado em 50 segmentos e você não tiver a chave de partição como parte da consulta, cada um desses 50 segmentos precisará ser verificado. Cada segmento será menor, então você pode ter que fazer apenas 2 saltos, mas ainda pode acabar fazendo 100 leituras em vez das 5 anteriores.
Se forem índices de bitmap, as equações são diferentes. Você provavelmente não está usando índices para identificar linhas individuais, mas sim conjuntos delas. Portanto, em vez de uma consulta usando 5 E/S para retornar um único registro, ela estava usando 10.000 E/S. Como tal, a sobrecarga extra em partições extras para o índice não importa.
a exclusão de 50 milhões de registros por mês em lotes de 50.000 é de apenas 1.000 iterações. se você fizer 1 exclusão a cada 30 minutos, deve atender às suas necessidades. uma tarefa agendada para executar a consulta que você postou, mas remova o loop para que ele seja executado apenas uma vez, não deve causar uma degradação perceptível aos usuários. Fazemos praticamente o mesmo volume de registros em nossa fábrica que funciona praticamente 24 horas por dia, 7 dias por semana e atende às nossas necessidades. Na verdade, espalhamos um pouco mais de 10.000 registros a cada 10 minutos, que são executados em cerca de 1 ou 2 segundos em nossos servidores Oracle unix.
Se o espaço em disco não for um prêmio, você poderá criar uma cópia "de trabalho" da tabela, digamos
my_table_new
, usando CTAS (Criar tabela como seleção) com critérios que omitiriam os registros a serem descartados. Você pode fazer a instrução create em paralelo e com a dica de acréscimo para torná-la rápida e, em seguida, criar todos os seus índices. Então, uma vez terminado (e testado), renomeie a tabela existente paramy_table_old
e renomeie a tabela "trabalho" paramy_table
. Uma vez que você está confortável com tudodrop my_table_old purge
para se livrar da mesa velha. Se houver um monte de restrições de chave estrangeira, dê uma olhada nodbms_redefinition
pacote PL/SQL . Ele clonará seus índices, restrições, etc. ao usar as opções apropriadas. Este é um resumo de uma sugestão de Tom Kyte do AskTomfama. Após a primeira execução, você pode automatizar tudo, e a criação da tabela deve ser muito mais rápida, podendo ser feita enquanto o sistema estiver ativo, e o tempo de inatividade do aplicativo seria limitado a menos de um minuto para fazer a renomeação das tabelas. Usar CTAS será muito mais rápido do que fazer várias exclusões em lote. Essa abordagem pode ser particularmente útil se você não tiver o particionamento licenciado.CTAS de amostra, mantendo linhas com dados dos últimos 365 dias e
flag_inactive = 'N'
:ao descartar uma partição, você deixa índices globais inutilizáveis, que precisam reconstruir, a reconstrução de índices globais seria um grande problema, pois se você fizer isso online, será bastante lento, caso contrário, você precisará de tempo de inatividade. em ambos os casos, não pode atender ao requisito.
"Normalmente acabamos limpando entre 10 e 50 milhões de linhas por mês"
eu recomendaria usar a exclusão de lote PL/SQL, várias horas está ok, eu acho.