33

我正在处理一个非常大的数据集,大约有 200 万条记录。我有下面的代码,但是在处理了大约三批(大约 600,000 条记录)后出现内存不足异常。我知道,当它遍历每个批处理实体框架延迟加载时,它会尝试将完整的 200 万条记录构建到内存中。有什么方法可以卸载我处理过的批次吗?

ModelContext dbContext = new ModelContext();
IEnumerable<IEnumerable<Town>> towns = dbContext.Towns.OrderBy(t => t.TownID).Batch(200000);
foreach (var batch in towns)
{
    SearchClient.Instance.IndexMany(batch, SearchClient.Instance.Settings.DefaultIndex, "Town", new SimpleBulkParameters() { Refresh = false });
}

注意:批处理方法来自这个项目:https ://code.google.com/p/morelinq/

搜索客户端是这样的:https ://github.com/Mpdreamz/NEST

4

2 回答 2

80

问题是,当您从 EF 获取数据时,实际上创建了两个数据副本,一个返回给用户,另一个 EF 保留并用于更改检测(以便它可以将更改持久保存到数据库) . EF 在上下文的整个生命周期中都保留了第二组,而它的这组会使您的内存不足。

你有 2 个选项来处理这个问题

  1. 每批更新您的上下文
  2. 在查询中使用 .AsNoTracking() 例如:

    IEnumerable<IEnumerable<Town>> towns = dbContext.Towns.AsNoTracking().OrderBy(t => t.TownID).Batch(200000);
    

这告诉 EF 不要保留副本以进行更改检测。您可以在我的博客上阅读更多关于 AsNoTracking 的作用及其对性能的影响:http: //blog.staticvoid.co.nz/2012/4/2/entity_framework_and_asnotracking

于 2013-08-11T07:51:53.440 回答
-1

我编写了一个迁移例程,该例程从一个 DB 读取并将(布局中的微小变化)写入另一个 DB(不同类型),在这种情况下,更新每个批次的连接并使用 AsNoTracking() 并没有为我解决.

请注意,使用 '97 版本的 JET 会出现此问题。它可以与其他数据库完美配合。

但是,以下算法确实解决了内存不足问题:

  • 使用一个连接读取和一个写入/更新
  • 使用 AsNoTracking() 读取
  • 每 50 行左右写入/更新,检查内存使用情况,根据需要恢复内存 + 重置输出数据库上下文(和连接的表):

    var before = System.Diagnostics.Process.GetCurrentProcess().VirtualMemorySize64;
    if (before > 800000000)
    {
        dbcontextOut.SaveChanges();
        dbcontextOut.Dispose();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        dbcontextOut = dbcontextOutFunc();
        tableOut = Dynamic.InvokeGet(dbcontextOut, outputTableName);
    }
    
于 2019-08-26T10:07:05.190 回答