INSERT OTIMIZADO COM ENTITY FRAMEWORK
Boa noite!
Surgiu a necessidade de eu importar 500 mil registros para uma tabela num banco de dados SQL Server.
Estou usando Entity Framework.
Eis meu código:
Como puderam notar, eu já desativei AutoDetectChangesEnabled. Isso teve um aumento de performance incrÃvel.
Outra coisa que percebi uma melhora significativa foi apenas realizar o commit com SaveChanges a cada 10000 registros. Tentei com 100000 anteriormente, mas aqui gerou um erro estranho de thread (não entendi também). E se eu desse um SaveChanges a cada iteração, ficaria bem mais lento do que a cada 10000.
Mas mesmo assim, estou aqui a quase 1 hora e não chegou na metade.
Como podem ver, a tupla do meu registro é pequena. (Apesar desse ser apenas um exemplo)
Tem algo a mais que eu possa fazer pra não demorar tanto? Na verdade, ele está demorando mesmo é no método SaveChanges. 10 mil registros tá levando cerca de 10 minutos pra persistir. Isso dá aproximadamente 33.3333 registros por segundo.
Quem souber alguma dica, fico muito agradecido!
Surgiu a necessidade de eu importar 500 mil registros para uma tabela num banco de dados SQL Server.
Estou usando Entity Framework.
Eis meu código:
Using ctx As ModeloEFContainer = New ModeloEFContainer
ctx.Configuration.AutoDetectChangesEnabled = False
For i As Integer = 1 To 500000
Dim expressao As New ExpressaoEntity
expressao.Resultado = enumResultado.Indefinido
expressao.ExpressaoString = [Ô]3+2[Ô]
ctx.Set(Of ExpressaoEntity).Add(expressao)
If i Mod 10000 = 0 Then
ctx.SaveChanges()
End If
Next
End Using
Como puderam notar, eu já desativei AutoDetectChangesEnabled. Isso teve um aumento de performance incrÃvel.
Outra coisa que percebi uma melhora significativa foi apenas realizar o commit com SaveChanges a cada 10000 registros. Tentei com 100000 anteriormente, mas aqui gerou um erro estranho de thread (não entendi também). E se eu desse um SaveChanges a cada iteração, ficaria bem mais lento do que a cada 10000.
Mas mesmo assim, estou aqui a quase 1 hora e não chegou na metade.
Como podem ver, a tupla do meu registro é pequena. (Apesar desse ser apenas um exemplo)
Tem algo a mais que eu possa fazer pra não demorar tanto? Na verdade, ele está demorando mesmo é no método SaveChanges. 10 mil registros tá levando cerca de 10 minutos pra persistir. Isso dá aproximadamente 33.3333 registros por segundo.
Quem souber alguma dica, fico muito agradecido!
Qual a necessidade de salvar dentro do comando FOR? Não vejo um sentido sensato para isso.
Using ctx As ModeloEFContainer = New ModeloEFContainer
ctx.Configuration.AutoDetectChangesEnabled = False
For i As Integer = 1 To 500000
Dim expressao As New ExpressaoEntity
expressao.Resultado = enumResultado.Indefinido
expressao.ExpressaoString = [Ô]3+2[Ô]
ctx.Set(Of ExpressaoEntity).Add(expressao)
Next
[txt-color=#e80000]ctx.SaveChanges()[/txt-color]
End Using
Pelo menos na minha máquina, ocorre um erro no SaveChages quando tento salvar um valor muito maior que 10 mil registros por vez.
Esse é o erro:
The CLR has been unable to transition from COM context 0x3322d98 to COM context 0x3322f08 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.
Esse é o erro:
The CLR has been unable to transition from COM context 0x3322d98 to COM context 0x3322f08 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.
DS2T,
Você já experimentou executar o aplicativo compilado?
Geralmente este erro ocorre quando tentamos executar o aplicativo em Debug pelo VS.
Você já experimentou executar o aplicativo compilado?
Geralmente este erro ocorre quando tentamos executar o aplicativo em Debug pelo VS.
Fala DS2T,
Veja só, usando for ou foreach sempre será mais lento, em vez de usar o add, use o addrange, o addrange espera uma lista, então a sua variável i deve ser uma lista com os 50 mil valores.
Para você ter uma idéa de como isso é rápido, eu importo para o meu PDV 25.000 produtos, em menos de um minuto, lembrando que o objeto produto possui varias propriedades, claro que não descobrir isso do dia pra noite, antigamente eu usava o foreach e realmente fica lento, demora muito mesmo.
Eu não lembro a partir de que versão esta disponÃvel o addrange, use o EntityFramework a partir da versão 6.0, eu não tenho certeza se esta disponÃvel em uma versão abaixo da 6.0.
Abraços
Veja só, usando for ou foreach sempre será mais lento, em vez de usar o add, use o addrange, o addrange espera uma lista, então a sua variável i deve ser uma lista com os 50 mil valores.
Para você ter uma idéa de como isso é rápido, eu importo para o meu PDV 25.000 produtos, em menos de um minuto, lembrando que o objeto produto possui varias propriedades, claro que não descobrir isso do dia pra noite, antigamente eu usava o foreach e realmente fica lento, demora muito mesmo.
Eu não lembro a partir de que versão esta disponÃvel o addrange, use o EntityFramework a partir da versão 6.0, eu não tenho certeza se esta disponÃvel em uma versão abaixo da 6.0.
Abraços
Tive um caso assim de quantidades absurdas de dados terem de ser inseridas no EF e passei por esse mesmo problema de performance. Era mais ou menos essa quantidade de registros, mas de hora em hora, então desempenho era crÃtico. Uma coisa que ajudou na performance, mas obviamente aumento o risco de inconsistências, foi desativar a validação além da auto-detecção de mudanças, no seu caso seria:
Mas o que fez mesmo diferença foi usar o add-in BulkInsert.
Experimente primeiro desabilitar a validação, se a performance ainda não for satisfatória, então use o BulkInsert
ctx.Configuration.ValidateOnSaveEnabled = false;
Mas o que fez mesmo diferença foi usar o add-in BulkInsert.
Experimente primeiro desabilitar a validação, se a performance ainda não for satisfatória, então use o BulkInsert
Desculpem a demora pessoal.
GUIMORAES123
Tentei rodar sem Debug. Realmente não apareceu nenhum erro, mas o processo do programa finalizou sozinho. Achei meio estranho.
Mas obrigado cara!
LAMPIAO
Fala aà Lampião, beleza cara?
Eu estava usando a versão 5.0 e não tinha o AddRange. Mas baixei pra testar e ver como era. Realmente, ficou mais rápido. Não entendi ainda o motivo disso. Até o SaveChanges ficou mais rápido. Isso que não entendi. Era pra ser mais rápido adicionar os itens na lista, mas não a persistência. Ou era?
Eu ainda vou fazer mais uns testes. To achando que pode ser a própria versão 6 que trouxe alguma melhoria no SaveChanges... não sei ainda. Obrigado!
LOCUTOR.. ops, KERPLUNK
Desabilitar a validação no Save melhorou bastante mesmo.O Bulkinsert eu ainda não cheguei a ver.
Dei uma pesquisada no MSDN mas não entendi ainda bem que tipo de validação fica desabilitada. Seria validação de chave estrangeira?
Obrigado a todos!
GUIMORAES123
Tentei rodar sem Debug. Realmente não apareceu nenhum erro, mas o processo do programa finalizou sozinho. Achei meio estranho.
Mas obrigado cara!
LAMPIAO
Fala aà Lampião, beleza cara?
Eu estava usando a versão 5.0 e não tinha o AddRange. Mas baixei pra testar e ver como era. Realmente, ficou mais rápido. Não entendi ainda o motivo disso. Até o SaveChanges ficou mais rápido. Isso que não entendi. Era pra ser mais rápido adicionar os itens na lista, mas não a persistência. Ou era?
Eu ainda vou fazer mais uns testes. To achando que pode ser a própria versão 6 que trouxe alguma melhoria no SaveChanges... não sei ainda. Obrigado!
LOCUTOR.. ops, KERPLUNK
Desabilitar a validação no Save melhorou bastante mesmo.O Bulkinsert eu ainda não cheguei a ver.
Dei uma pesquisada no MSDN mas não entendi ainda bem que tipo de validação fica desabilitada. Seria validação de chave estrangeira?
Obrigado a todos!
Tópico encerrado , respostas não são mais permitidas