2011-05-12 53 views
5

我有> 67000條記錄從另一個來源進入我的系統。在將業務規則應用於這些記錄之後,我必須將它們存儲到數據庫中。實體框架(EF)OutOfMemoryException

Error in executing code|Exception of type 'System.OutOfMemoryException' was thrown.* at System.Data.Mapping.Update.Internal.KeyManager.<WalkGraph>d__5.MoveNext() 
    at System.Data.Mapping.Update.Internal.KeyManager.GetPrincipalValue(PropagatorResult result) 
    at System.Data.Mapping.Update.Internal.UpdateCompiler.GenerateValueExpression(EdmProperty property, PropagatorResult value) 
    at System.Data.Mapping.Update.Internal.UpdateCompiler.BuildSetClauses(DbExpressionBinding target, PropagatorResult row, PropagatorResult originalRow, TableChangeProcessor processor, Boolean insertMode, Dictionary`2& outputIdentifiers, DbExpression& returning, Boolean& rowMustBeTouched) 
    at System.Data.Mapping.Update.Internal.UpdateCompiler.BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor) 
    at System.Data.Mapping.Update.Internal.TableChangeProcessor.CompileCommands(ChangeNode changeNode, UpdateCompiler compiler) 
    at System.Data.Mapping.Update.Internal.UpdateTranslator.<ProduceDynamicCommands>d__0.MoveNext() 
    at System.Linq.Enumerable.<ConcatIterator>d__71`1.MoveNext() 
    at System.Data.Mapping.Update.Internal.UpdateCommandOrderer..ctor(IEnumerable`1 commands, UpdateTranslator translator) 
    at System.Data.Mapping.Update.Internal.UpdateTranslator.ProduceCommands() 
    at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) 
    at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) 
    at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) 

我使用EF 4.0

 using (var context = new MyEntities()) 
     { 
      var importDataInfo = context.ImportDataInfoes.First(x => x.ID == importId); 

      importedRecords.ForEach(importDataInfo.ValuationEventFulls.Add); 

      context.SaveChanges(); 
     } 

執行代碼我收到以下錯誤(OutOfMemoryException異常)後:我用下面的代碼做這件事。

我的問題是有限制的記錄數量保存?保存大量記錄的最佳做法是什麼(將它們保存在塊中?關於事務呢?)。

謝謝大家。

回答

4

您可能希望一次將這些數據分批發送到1024個記錄中。

您可以將批處理記錄的循環包裝到事務中,以便在需要時可以回滾整個序列。請注意,此事務很可能升級爲分佈式事務。

分佈式事務只能應用於運行Microsoft分佈式事務處理協調器(MS-DTC)服務的服務器。在進行分佈式交易時,會有明顯的性能損失。

+0

感謝您的回覆。 - 是的,我更改了我的代碼以測試它是否與大塊記錄一起工作,並且確實有效,但是我的問題是我沒有閱讀任何有關EF中SaveChanges()限制記錄數的任何內容。你有沒有聽說過MS的這個限制? – 2011-05-12 16:26:20

+0

@Vlad Bezden該限制不是專門針對EF,它只是使用.NET技術的一部分。當你ETL 67K記錄時,你可能在內存中有200k +對象。你有67k原始記錄,你有67k EF對象,並且每個對象最有可能在上下文中爲每個對象實例化一些狀態跟蹤實體,所以那裏已經有67k + 67k + 67k,我不會感到驚訝的是,其他輔助對象也會一起創建。 EF在延遲查詢模式下工作,直到您調用SaveChanges這就是OOM發生的原因時,纔會執行實際工作。 – 2011-05-12 16:36:33

+0

我有類似的問題。不清楚應該如何改變片段。很高興看到原始問題的更新。我確實有現有的代碼。在循環之後立即添加foreach循環和SaveChanges。不清楚是否在循環中包含SaveChanges,是否始終清楚通過添加的內容。添加 – MicMit 2012-06-12 23:33:54

4

通常,.NET僅限於在單個集合或其他對象中尋址2GB的內存。這是因爲即使在64位環境中,索引器也使用32位整數(最大值爲20億,並且發生更改)。即使對於像整數這樣的簡單數據類型,單個int的大小也意味着只有5億個整數可以存儲在單個數組中。對於像結構這樣的更大值類型,集合的最大元素數量非常小。

如果您使用的是Windows XP這樣的32位環境,則會有更低的限制;整個程序的最大內存空間不能大於2GB。這對像你的ETL有一些相當高的限制,我不會感到驚訝的是,你的程序正在耗盡內存,試圖一次處理67k內存記錄。

如果可以,解決方案是以較小的批次處理記錄。嘗試根據ID構建一個聲明,其中返回ID(有希望自動生成)大於您已檢索的最大ID的前100個記錄。一旦記錄被處理,處置它(或者只是孤立它,讓GC完成它的工作)。

+0

感謝您的回覆。當我調用SaveChanges方法時,我得到了OOME。我的應用程序對.NET代碼沒有任何問題,我測試了大於1,000,000的處理導入記錄,並且系統沒問題,但是當我在EF – 2011-05-12 16:23:07

+0

上調用SaveChange時,這隻會失敗可能是因爲像EF這樣的ORM必須確定更改了什麼所以他們可以創建一個高效的UPDATE語句。這需要再次提取記錄,並將更改存儲在將用於生成更新的某個集合中。此外,形成更新命令的字符串是字符的集合,並且每個UPDATE將67,000乘以高達幾千個字符,並且您可以輕鬆達到字符串的長度限制。再次嘗試設置傳入和傳出記錄的「批量大小」,這會限制傳入數據量和傳出命令的大小。 – KeithS 2011-05-12 16:48:51

+0

對不起,遲入:它是導致OOME的SaveChanges()調用 - 所以如果我更頻繁地調用SaveChanges,那麼我可以最小化這種風險?我有一個foreach循環,但在保存之前我正在處理所有項目,而沒有什麼可以阻止我單獨保存每個項目。 – BlueChippy 2011-10-18 10:10:52