实体框架将数千个对象传输到弹性搜索
本文关键字:传输 对象 搜索 千个 框架 实体 | 更新日期: 2023-09-27 18:37:05
我正在创建一个网络应用程序,该应用程序将拥有世界上每个城镇,地区和国家/地区的数据库,以允许将其他对象映射到某个位置。作为应用程序的一部分,我希望用户能够搜索一个地方,为此,我正在使用弹性搜索来索引所有内容。为了与弹性搜索交互,我正在使用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 条记录
我不知道ElasticSearch和NEST,但根据它们的源代码,IndexManyAsync
每次调用时都会创建一个新任务。
因此,如果任务的执行速度比实体框架具体化实体的速度慢得多,您将有大量任务正在执行(或等待执行),每个任务都有BATCH_SIZE
个实体加载到内存中,因为这些实体作为参数传递:所以基本上所有实体都将同时加载到内存中。
我不确定如何解决这个问题,因为我不知道 NEST 或 ElasticSearch 关于批处理索引的最佳实践,但这是对您的问题的解释。
对象只有在没有实时引用时才有资格进行垃圾回收。
显然,您已经处理了上下文,但您可能仍然引用了不那么明显的实体。 在 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
你不会错过dbContext.Countries
和innerContext.Regions
的AsNoTracking()
吗?当您使用更改跟踪器时,它将在内存中保留引用,直到查询的实体引用消失。
在您的情况下,它可能会增加您的内存,因为由于更改跟踪器,多个上下文(主要是区域的 innerContext)实际上没有被释放。
只需尝试对所有查询使用 AsNoTracking,然后再次检查内存占用。
仍然在您的情况下,如果不使用 AsNoTracking,所有跟踪的实体都将传递到您的 SearchClient 方法,那么您无法知道这些引用何时实际发布。
如果问题与 EF 本身无关,请尝试使用 ANTS 内存性能分析器等工具查找内存泄漏,它非常有用。