Você deve abandonar uma estrutura ORM quando precisar implementar uma operação em massa?

15

Aqui está uma situação comum:

  • Você precisa implementar uma operação em massa em um aplicativo que usa uma estrutura ORM.
  • Após a primeira passagem, você notou problemas significativos de desempenho.

Aqui está a minha pergunta:

  • Nessa situação, você deve favorecer uma solução que inclua SQL bruto?
  • Ou existem padrões de design conhecidos que podem ajudá-lo a atenuar os problemas geralmente associados a operações em massa com estruturas ORM?

EDITAR:

  • Não estou perguntando se você deve remover a estrutura ORM de todo o aplicativo.
  • Estou perguntando: você deve renunciar à estrutura ORM para esta pequena fatia do aplicativo?
Jim G.
fonte
Não sei se você deve fazer alguma coisa, mas você já tentou agrupar sua operação em massa?
precisa

Respostas:

13

Os ORMs não se destinam a assumir completamente o acesso ao seu banco de dados. Use-os para esses 80% de código que é CRUD, o que é entediante demais para escrever por conta própria. Use procedimentos armazenados, SQL dinâmico ou o que desejar para os 20% restantes que precisam ser cuidadosamente otimizados.

Robert Harvey
fonte
4
Isso funcionaria se a abstração do banco de dados não fosse um dos principais motivos pelos quais você decidiu usar um ORM.
@ Pierre303, estou tendo dificuldades para entender seu comentário. O que você quer dizer?
Mark Canlas
@ MarkCanlas: Eu acho que ele quer dizer "abstraindo o banco de dados", no sentido de que você pode mudar o banco de dados (por exemplo, passar do SQL Server para o MySQL), se quiser. Na prática, esse caso de uso quase nunca ocorre.
Robert Harvey
1
Você ainda pode criar abstrações. A maioria dos ORMs que realmente oferecem suporte a vários provedores / dialetos têm suporte para código específico do provedor / dialeto. Você pode implementar operações como inserção em massa / vinculação de matriz / TVP / qualquer outra coisa para bancos de dados específicos e voltar a desacelerar lentamente para provedores não suportados, como SQLite. Na pior das hipóteses, você pode dividir a funcionalidade que pode ser em massa em uma interface / classe e sub separados em uma implementação diferente com base nos parâmetros de compilação ou configuração.
Aaronaught 11/11/11
Sim, dialetos personalizados podem ajudar, assim como código específico para problemas específicos. No entanto, para que isso seja viável do ponto de vista financeiro, isso deve ser limitado ao mínimo estrito. Nossas funções personalizadas de personalização (dialetos) representam menos de 0,1% da base total de códigos de acesso a dados. Eu ficaria realmente preocupado se fosse mais do que isso.
7

Eu uso um ORM (nHibernate) em um aplicativo que requer alto desempenho e lida com bilhões de registros. Com o tempo, percebemos que os problemas de desempenho mais significativos estavam relacionados à nossa maneira de usar o ORM, e não apenas ao ORM.

O ORM não deve substituir seu conhecimento obrigatório do banco de dados. É uma ferramenta usada para obter mais produtividade e flexibilidade em seu código, mas você precisará conhecer os processos subjacentes para otimizar seu desempenho.

Você não especificou um ORM específico, portanto, eis o que fizemos para melhorar o desempenho:

  • Usamos um profiler ORM. (usamos nhprof)
  • Usamos um perfilador de banco de dados. (usamos o SQL Server Profiler)
  • Lemos o maior número possível de artigos sobre o assunto. (Muitos estavam disponíveis para o nHibernate, além de todo o capítulo sobre o assunto na documentação)
  • Compramos livros específicos sobre desempenho e escalabilidade.
  • Criamos um sistema de benchmarking para testar nossas próprias otimizações.
  • e, mais importante, fomos capazes de testar nosso código com clientes da vida real com enormes dados. Só essa última coisa nos ajudou a identificar a maioria dos problemas em nosso aplicativo.
Dan McGrath
fonte
1

Conseguimos fazer isso com o Entity Framework, mas nosso aplicativo executou muitas operações no estilo de lote (gravamos um grande número de registros em tabelas individuais), por isso foi um bom ajuste. Definitivamente, verificaria se seria possível manter a estrutura ORM, se possível, apenas para reduzir a quantidade de código de finalidade especial no seu aplicativo. É possível armazenar em buffer as gravações e executá-las como um grupo? Você perde a semântica das transações, mas, se você optar por operações em massa, presumo que você já tenha concordado com isso.

TMN
fonte
1

ORMs não fazem nada mágico. Eles convertem métodos de acesso a objetos em SQL. As instruções SQL que eles executam não são necessariamente mais lentas que as SQL que você escreveria manualmente. Dito isto, há alguns problemas que você pode encontrar:

  1. Transações: uma operação em massa grande é quase sempre mais rápida do que muitas transações pequenas que juntas realizam a mesma coisa. Portanto, se suas chamadas de método ORM usarem transações refinadas (os métodos ativos de estilo de registro nas entidades do Spring Roo, por exemplo, são anotados como @Transactional por padrão), as operações em massa serão lentas. Se esse for o caso no seu aplicativo, você deve examinar sua lógica de transação.
  2. Armazenamento em cache: no Hibernate, um cache de primeiro nível permite ao gerente da entidade evitar viagens de ida e volta desnecessárias ao banco de dados. Coisa boa em geral, mas ruim para inserções em massa, onde leva ao entupimento desnecessário de cache, resultando em degradação no desempenho do aplicativo. Se esse for o seu problema, você deve observar o padrão de Lote sugerido acima por ChrisAnnODell. Nós o usamos em nossos importadores e acelera muito as inserções a granel.

Não há nada errado em usar o SQL nativo para melhorar o desempenho. Mas primeiro certifique-se de entender o que está deixando você mais lento.

wallenborn
fonte
Para evitar o cache, use uma StatelessSession. Além disso, evite IDs de incremento automático. HiLo ou Guid deve ser usado.
1

Ignore o ORM. Não apenas isso, mas também ignore o sql "regular". Use um utilitário em massa do seu banco de dados para inserir conjuntos de dados extremamente grandes em uma tabela intermediária. Em seguida, use o sql para executar suas atividades de preparação.

Seu ORM de "sabor do blog" pode não funcionar em todas as situações.

Lord Tydus
fonte
Certo, esse tipo de ferramenta de back-end é um aborrecimento para aprender, mas, depois de três ou quatro vezes, você será um especialista e poderá fazer as coisas mais rapidamente e, às vezes, coisas que não podem ser feitas de outras maneiras. É como a diferença entre uma pá e um trator. Escrevi ferramentas controladas por script para várias plataformas para ler arquivos de entrada de texto e atualizar dados com operações de baixo nível. Escrever essa ferramenta também pode facilitar sua vida (ou pelo menos mais interessante). Coisas como essa podem ser usadas para ajustar os dados de personalização nas instalações do cliente durante as atualizações de software.
0

Estive nessa situação. Às vezes você precisa.

Alguns ORM permitem que o desenvolvedor pule o modelo de objeto e vá diretamente para a camada do banco de dados.

Também existem ORM, que usam operações em massa, encapsuladas, como Orientadas a Objetos.

umlcat
fonte
0

Conforme mencionado por umlcat , existem alguns ORMs que permitem usar operações em massa.

Ainda melhor, muitos ORMs são extensíveis; portanto, você pode escrever seu próprio método para executar operações em massa, se ainda não tiver suporte. Se a operação em massa no seu aplicativo é algo que você pode levar em consideração, eu o adicionaria como uma camada no ORM (para fazer isso, você provavelmente precisará escrever SQL bruto), mas, no aplicativo, use o ORM método que você implementou.

Isso também facilita o teste de unidade e a depuração. Depois de ter uma boa cobertura de teste para seus métodos ORM, você poderá usá-lo em seus aplicativos. Caso contrário, a depuração de SQL bruto (especialmente os grandes com transações e muitos JOINs) pode ser um problema.

Uma vez, levei quase um dia para localizar um bug em uma chamada SQL bruta que era quase 100 LOC, e o bug era apenas um caractere! Desde então, tento evitar o SQL bruto no aplicativo e todos os procedimentos do SQL são testados separadamente.

Attila O.
fonte
0

Bem, não há nenhum padrão de design que eu saiba. Meu palpite é que você tomou a decisão pelo ORM por um motivo, portanto, abandonar o ORM provavelmente não é o que você deseja. No entanto, nesses casos, acho que há espaço para misturar as duas soluções. Não há nada de errado nisso, desde que você faça isso de maneira consciente e documente por que você se desvia do uso padrão do ORM em seu software. Além disso, algumas estruturas do ORM possuem alguns recursos para realizar operações em massa. Eu sei que o nHibernate (ORM para o .NET framework) chamou StatelessSessions, que têm muito menos custos indiretos, mas isso ainda pode não lhe dar o impulso de desempenho que você está procurando. Nesse caso, basta usar SQL bruto.

Pieter
fonte