首页 文章

实体框架将数千个对象传输到Elastic Search

提问于
浏览
4

我正在创建一个Web应用程序,它将在世界上的每个城镇,地区和国家/地区拥有一个数据库,以允许将其他对象映射到某个位置 . 作为应用程序的一部分,我希望用户能够搜索一个地方,为此我使用弹性搜索来索引所有内容 . 要与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);
    }
}

正如您所看到的,我将非常大的表分成批处理,以避免加载到内存中 . 问题是这不起作用,我不断出现内存异常 . 我认为为每个批处理使用一个新的上下文可以避免这个问题,因为当处理上下文时它将处理它已经加载的所有实体,这似乎不是这种情况 . 有任何想法吗?

正如数据量的指示:国家表有193条记录地区表有80,523条记录城镇表有2,743,469条记录

3 回答

  • 0

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

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

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

  • 0

    如果没有对象的实时引用,则对象仅适用于垃圾回收 .
    很明显,您已经处理了上下文,但您可能仍然引用了那些不那么明显的实体 . 在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
    
  • 0

    你不会错过 AsNoTracking()dbContext.CountriesinnerContext.Regions 吗?当您使用ChangeTracker时,它将在内存中保留引用,直到查询的实体引用消失 .

    在你的情况下,它可能会增加你的记忆,因为由于ChangeTracker,多个上下文(主要是Regions的innerContext)实际上没有被处理掉 .

    只是尝试使用AsNoTracking进行所有查询并再次检查内存占用 .

    仍然在你的情况下,通过不使用AsNoTracking,所有被跟踪的实体都被传递给你的SearchClient方法,然后你不知道这些引用何时会被实际发布 .

    如果问题与EF本身无关,请尝试使用ANTS Memory Profiler等工具查找内存泄漏,这非常有用 .

相关问题