Para resumir os detalhes: Precisamos colocar aproximadamente 5 milhões de linhas em um banco de dados do fornecedor (Oracle). Tudo corre bem para lotes de 500 mil linhas usando OracleBulkCopy
(ODP.NET), mas quando tentamos escalar até 5 milhões, o desempenho começa a desacelerar depois que atinge a marca de 1 milhão, fica progressivamente mais lento à medida que mais linhas são carregadas e, eventualmente, expira após 3 horas ou mais.
Eu suspeito que esteja relacionado a uma chave primária na mesa, mas tenho procurado nos fóruns da Oracle e no Stack Overflow para obter informações e muito do que estou lendo contradiz isso (também, muitas postagens parecem contradizer uma à outra ) . Espero que alguém possa esclarecer algumas questões relacionadas ao processo:
A
OracleBulkCopy
classe usa carregamento convencional ou de caminho direto? Existe alguma maneira de confirmar isso, de uma maneira ou de outra?Supondo que ele use carregamento de caminho direto: é verdade que o Oracle define automaticamente todos os índices como inutilizáveis durante o carregamento e os coloca novamente online depois? Eu li várias declarações para esse efeito, mas, novamente, não posso confirmá-lo.
Se o número 2 for verdadeiro, deve fazer alguma diferença quais índices estão na tabela antes de iniciar uma operação de cópia em massa? Se sim, por quê?
Em relação ao item 3, existe alguma diferença prática, em geral, entre o carregamento em massa com um índice inutilizável e, na verdade, descartar o índice antes do carregamento e recriá-lo posteriormente?
Se o item 2 não estiver correto, ou se houver algumas ressalvas que não estou entendendo, faria alguma diferença explicitamente tornar o índice inutilizável antes do carregamento em massa e depois reconstruí-lo explicitamente depois?
Há mais alguma coisa, além da criação de índices, que poderia fazer com que uma operação de cópia em massa cresça progressivamente mais lenta à medida que mais e mais registros são adicionados? (Talvez algo a ver com o log, embora eu espere que operações em massa não sejam registradas?)
Se realmente não houver outra maneira de melhorar o desempenho, além de eliminar o PK / índice primeiro, que etapas posso seguir para garantir que o índice não desapareça completamente, ou seja, se a conexão com o banco de dados for perdida? no meio do processo?
Respostas:
Mais alguns dias de leitura e experimentação e eu pude (principalmente) responder a muitos deles:
Eu encontrei isso enterrado na documentação do ODP.NET (ironicamente, não nos
OracleBulkCopy
documentos):Assim, parece que ele não usar caminho direto.
Eu pude verificar fazendo uma operação de cópia em massa grande e obtendo as propriedades de índice do SQL Developer. O índice que aparecem como
UNUSABLE
enquanto a cópia em massa estava em andamento. No entanto , eu também descobri queOracleBulkCopy.WriteToServer
se recusará a executar se o índice iniciar em umUNUSABLE
estado, então claramente há mais coisas acontecendo aqui, porque se fosse tão simples quanto desabilitar e reconstruir o índice, ele não se importaria com o estado inicial.Faz diferença especificamente se o índice também é uma restrição . Encontrei essa pequena joia na documentação vinculada acima:
A documentação é um pouco obscura sobre o que acontece durante o carregamento, especialmente com chaves primárias, mas uma coisa é absolutamente certa - ela se comporta de maneira diferente com uma chave primária e sem uma. Como o
OracleBulkCopy
felizmente permitirá que você viole as restrições do índice (e coloque o índice noUNUSABLE
estado quando terminar), meu palpite é que ele está criando o índice PK durante a cópia em massa, mas simplesmente não a validando até depois.Não tenho certeza se a diferença observada está dentro do próprio Oracle ou apenas uma peculiaridade do
OracleBulkCopy
. O júri ainda está de fora.OracleBulkCopy
lançará uma exceção se um índice estiver inicialmente noUNUSABLE
estado, então é realmente um ponto discutível.Se houver são outros fatores, índices (e índices especialmente PK) ainda são os mais importantes, como eu descobri por:
Criando uma tabela temporária global com o mesmo esquema (usando
CREATE AS
), copiando em massa para a tabela temporária e, finalmente, executando um antigo simplesINSERT
da tabela temporária na tabela real. Como a tabela temporária não possui índice, a cópia em massa é muito rápida, e a finalINSERT
também é rápida porque os dados já estão em uma tabela (ainda não tentei a dica de anexar, pois uma cópia de tabela para tabela de 5 milhões de linhas já leva menos de 1 minuto).Ainda não tenho certeza das possíveis ramificações de (ab) usar o espaço de tabela temporário dessa maneira, mas até agora não me causou nenhum problema, e é muito mais seguro que a alternativa para impedir a corrupção das linhas ou índices.
O sucesso disso também demonstra claramente que o índice PK é o problema, pois essa é a única diferença prática entre a tabela temporária e a tabela permanente - ambas iniciadas com zero linhas durante os testes de desempenho.
Conclusão: não se preocupe em tentar copiar em massa mais de 100 mil linhas para uma tabela Oracle indexada usando o ODP.NET. Solte o índice (se você realmente não precisar dele) ou "pré-carregue" os dados em uma tabela diferente (não indexada).
fonte
Delete
não é possível porque o índice éUNUSABLE
. Isso é resultado da verificação de restrição que ocorre no final da cópia em massa.alter session set skip_unusable_indexes = true;
Aqui está um artigo da Oracle que explica quando será benéfico usar a inserção em massa ou quando não. Além disso, tem uma visão do que acontece no nível do banco de dados.
http://docs.oracle.com/cd/B28359_01/server.111/b28319/ldr_modes.htm
fonte