O que especificamente o OracleBulkCopy faz e como posso otimizar seu desempenho?

14

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:

  1. A OracleBulkCopyclasse usa carregamento convencional ou de caminho direto? Existe alguma maneira de confirmar isso, de uma maneira ou de outra?

  2. 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.

  3. 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ê?

  4. 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?

  5. 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?

  6. 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?)

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

Aaronaught
fonte
Nota lateral: Os dados que estão sendo copiados já estão na ordem de classificação de acordo com a PK, que é o único índice na tabela.
Aaronaught 26/10/11
Você está usando um DataReader para ler os dados da fonte?
2741111
@bernd_k: Não, carregando totalmente da memória. Definitivamente não é a fonte do problema.
Aaronaught 30/10/11

Respostas:

13

Mais alguns dias de leitura e experimentação e eu pude (principalmente) responder a muitos deles:

  1. Eu encontrei isso enterrado na documentação do ODP.NET (ironicamente, não nos OracleBulkCopydocumentos):

    O recurso Cópia em massa do ODP.NET usa uma abordagem de carregamento de caminho direto, que é semelhante, mas não é o mesmo que o Oracle SQL * Loader. Usar o carregamento direto do caminho é mais rápido que o carregamento convencional (usando INSERTinstruções SQL convencionais ).

    Assim, parece que ele não usar caminho direto.

  2. 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 UNUSABLEenquanto a cópia em massa estava em andamento. No entanto , eu também descobri que OracleBulkCopy.WriteToServerse recusará a executar se o índice iniciar em um UNUSABLEestado, 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.

  3. Faz diferença especificamente se o índice também é uma restrição . Encontrei essa pequena joia na documentação vinculada acima:

    Restrições ativadas
    Durante uma cópia em massa do Oracle, as seguintes restrições são ativadas automaticamente por padrão:

    • NOT NULL
    • UNIQUE
    • PRIMARY KEY (restrições exclusivas em colunas não nulas)

    NOT NULLrestrições são verificadas no momento da criação da matriz de colunas. Qualquer linha que viole a NOT NULLrestrição é rejeitada.

    UNIQUEas restrições são verificadas quando os índices são reconstruídos no final da carga. O índice é deixado no estado Índice Inutilizável se violar uma UNIQUErestrição.

    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 OracleBulkCopyfelizmente permitirá que você viole as restrições do índice (e coloque o índice no UNUSABLEestado 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.

  4. 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.

  5. OracleBulkCopylançará uma exceção se um índice estiver inicialmente no UNUSABLEestado, então é realmente um ponto discutível.

  6. Se houver são outros fatores, índices (e índices especialmente PK) ainda são os mais importantes, como eu descobri por:

  7. 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 simples INSERTda 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 final INSERTtambé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).

Aaronaught
fonte
Não tenho certeza sobre a verificação de restrições de chave primária. Por acaso, inseri os mesmos dados em massa em uma tabela Oracle duas vezes e o Select * mostra 2 as linhas duplicadas. Nesse estado, a exclusão não é possível, mas a tabela truncada ajuda a retornar a um estado limpo.
bernd_k
@bernd_k: Deletenão é possível porque o índice é UNUSABLE. Isso é resultado da verificação de restrição que ocorre no final da cópia em massa.
Aaronaught 30/10/11
Eu tenho um skript do PowerShell em execução, chamando bulkcopy em um banco de dados Oracle a partir de um leitor de dados do SQL Server, todas as tabelas de destino com Chaves primárias e não tenho problemas com tabelas com até 205278 linhas. Mas sou muito cuidadoso em preencher primeiro as tabelas mestras antes de preencher as tabelas de detalhes. Não removi nenhum outro índice da tabela e não tenho problemas quando as tabelas estão inicialmente vazias.
bernd_k
@bernd_k: Sim, também não tive muitos problemas nesse volume (veja meu último parágrafo). É quando você chega aos milhões que fica horrível. Também pode haver uma diferença se você esvaziar a tabela algum tempo após cada cópia em massa (essa não será esvaziada, será anexada e você saberá como os índices ficam mais lentos à medida que aumentam).
Aaronaught 30/10/11
Talvez ele ajuda quando você faz umaalter session set skip_unusable_indexes = true;
Wernfried Domscheit
1

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

SmartCoder
fonte
3
Os links tendem a apodrecer; convém incluir informações relevantes do link em sua resposta.
Mustaccio