【问题标题】:Entity framework transfer thousands of objects to Elastic Search实体框架将数千个对象传输到 Elastic Search
【发布时间】:2013-11-08 05:29:44
【问题描述】:

我正在创建一个网络应用程序,该应用程序将拥有世界上每个城镇、地区和国家/地区的数据库,以允许将其他对象映射到某个位置。作为应用程序的一部分,我希望用户能够搜索一个地方,为此我使用 Elastic Search 来索引所有内容。为了与 Elastic Search 交互,我正在使用 NEST。

我有以下代码:

public void RefreshLocationIndex()
{
    int count;
    using (var dbContext = new ModelContext())
    {
        IndexMany(dbContext.Countries, "Country");
    }

    using (var dbContext = new ModelContext())
    {
        count = dbContext.Regions.AsNoTracking().Count();
    }
    for (var i = 0; i <= count; i += BATCH_SIZE)
    {
        using (var innerContext = new ModelContext())
        {
            IndexMany(innerContext.Regions.OrderBy(t => t.RegionID).Skip(i).Take(BATCH_SIZE),
                    "Region");
        }
    }


    using (var dbContext = new ModelContext())
    {
        count = dbContext.Towns.AsNoTracking().Count();
    }
    for (var i = 0; i <= count; i += BATCH_SIZE)
    {
        using (var innerContext = new ModelContext())
        {
            IndexMany(innerContext.Towns.AsNoTracking().OrderBy(t => t.TownID).Skip(i).Take(BATCH_SIZE), "Town");
        }
    }
}

public void IndexMany(IQueryable<Entity> objects, string type)
{
    var itemCount = objects.Count();
    if (itemCount > 0)
    {
        SearchClient.Instance.IndexManyAsync(objects, SearchClient.Instance.Settings.DefaultIndex, type);
    }
}

如您所见,我将非常大的表分成批次,以尽量避免加载到内存中。问题是这不起作用,我不断出现内存不足的异常。我认为为每个批次使用一个新的上下文可以避免这个问题,因为当上下文被释放时,它会释放它加载的所有实体,但似乎并非如此。有什么想法吗?

仅作为数据量的指示: Country 表有 193 条记录 Region 表有 80,523 条记录 Town 表有 2,743,469 条记录

【问题讨论】:

  • 只为区域创建和处理大 dbcontext 80,523 次,为城镇创建和处理 2,743,469 次是否明智?
  • 您需要等待 IndexManyAsync 完成,或者在等待完成时将所有内容放入内存中。

标签: c# entity-framework nest


【解决方案1】:

我不知道 ElasticSearch 和 NEST,但根据 their source codeIndexManyAsync 每次调用都会创建一个新任务。

因此,如果任务的执行速度比 Entity Framework 实体化您的实体慢得多,那么您将有大量正在执行(或等待执行)的任务,每个任务都有 BATCH_SIZE 实体加载到内存中,因为这些实体是作为参数传递:所以基本上你的所有实体都会同时加载到内存中。

我不知道如何解决这个问题,因为我不知道 NEST 或 ElasticSearch 关于批处理索引的最佳实践,但这里是您问题的解释。

【讨论】:

    【解决方案2】:

    只有在没有对它的实时引用时,对象才有资格进行垃圾回收。
    显然,您已经处理了上下文,但您可能仍然引用了不那么明显的实体。在 SearchClient.Instance.IndexManyAsync(objects, ...) 的某个地方,您必须仍然持有对对象的引用。

    在本例中,dbContext 被释放时不会回收内存。

    List<Region> regions;
    using (var dbContext = new ModelContext())
    {
        regions = dbContext.Regions.ToList();  
    }  //dbContext is disposed, but memory is not reclaimed.
    regions = null; //memory is reclaimed
    

    【讨论】:

      【解决方案3】:

      您不会因为dbContext.CountriesinnerContext.Regions 而缺少AsNoTracking() 吗?当您使用 ChangeTracker 时,它会将引用保留在内存中,直到查询的实体引用消失为止。

      在您的情况下,这可能会增加您的内存,因为由于 ChangeTracker,实际上并未处理多个上下文(主要是区域的 innerContext)。

      只需尝试对所有查询使用 AsNoTracking 并再次检查内存占用情况。

      在您的情况下,由于不使用 AsNoTracking,所有被跟踪的实体都被传递给您的 SearchClient 方法,那么您无法知道这些引用何时会真正被释放。

      如果问题与EF本身无关,尝试使用ANTS Memory Profiler之类的工具查找内存泄漏,非常有用。

      【讨论】:

        猜你喜欢
        • 2015-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-24
        • 2011-06-23
        • 2011-08-13
        • 1970-01-01
        相关资源
        最近更新 更多