我有一个巨大的表格,我需要按特定顺序阅读并计算一些汇总统计信息。该表已经有一个正确顺序的聚集索引,因此获取记录本身非常快。我正在尝试使用 LINQ to SQL 来简化我需要编写的代码。问题是我不想将所有对象加载到内存中,因为 DataContext 似乎将它们保留在周围——但尝试对它们进行分页会导致可怕的性能问题。
这是故障。最初的尝试是这样的:
var logs =
(from record in dataContext.someTable
where [index is appropriate]
select record);
foreach( linqEntity l in logs )
{
// Do stuff with data from l
}
这是相当快的,并且以良好的速率流式传输,但问题是应用程序的内存使用量不断增加,从未停止过。我的猜测是 LINQ to SQL 实体被保存在内存中并且没有被正确处理。因此,在创建大量对象 C# 时读取内存不足后,我尝试了以下方法。这似乎是许多人使用的常见Skip
/Take
范例,具有节省内存的附加功能。
请注意,这_conn
是预先创建的,并且会为每个查询创建一个临时数据上下文,从而导致相关实体被垃圾收集。
int skipAmount = 0;
bool finished = false;
while (!finished)
{
// Trick to allow for automatic garbage collection while iterating through the DB
using (var tempDataContext = new MyDataContext(_conn) {CommandTimeout = 600})
{
var query =
(from record in tempDataContext.someTable
where [index is appropriate]
select record);
List<workerLog> logs = query.Skip(skipAmount).Take(BatchSize).ToList();
if (logs.Count == 0)
{
finished = true;
continue;
}
foreach( linqEntity l in logs )
{
// Do stuff with data from l
}
skipAmount += logs.Count;
}
}
现在,当我通过数据流式传输时,我有一个理想的行为,即内存使用量根本不会增加。然而,我有一个更糟糕的问题:每个Skip
都导致数据加载越来越慢,因为底层查询似乎实际上导致服务器遍历所有先前页面的所有数据。在运行查询时,每个页面的加载时间越来越长,我可以看出这正在变成二次运算。此问题已出现在以下帖子中:
我似乎找不到使用 LINQ 执行此操作的方法,该方法允许我通过分页数据来限制内存使用,但仍然可以在恒定时间内加载每个页面。有没有办法正确地做到这一点?我的直觉是,可能有某种方法可以告诉 DataContext 明确忘记上面第一种方法中的对象,但我不知道该怎么做。