每次我使用LINQ to SQL编写下面表单的程序时,我最终会得到一个程序,它在运行时会抓取越来越多的内存,并且在可能只有25,000条记录之后消耗掉2GB的堆。我总是最终使用ADO.NET重写它。我做错了什么?
澄清:这个问题与加工速度无关;关于让它变得更快的答案是无关紧要的。
foreach (int i=0; i<some_big_number; i++)
{
using (myDC dc = new myDC()) // my DataContext
{
myRecord record = (from r in dc.myTable where r.Code == i select r).Single();
// do some LINQ queries using various tables from the data context
// and the fields from this 'record'. i carefully avoid referencing
// any other data context than 'dc' in here because I want any cached
// records to get disposed of when 'dc' gets disposed at the end of
// each iteration.
record.someField = newValueJustCalculatedAbove;
dc.SubmitChanges();
}
}
答案 0 :(得分:6)
您正在对数据上下文施加压力,以便每次都从头开始生成查询。
请尝试使用已编译的查询。
答案 1 :(得分:3)
每次循环迭代都要去数据库两次 - 一次检索行,然后再次更新行。这不是很有效。
您应该分批操作:
通过选择范围而不是单个值来获取一组行,即第一批为0-100,下一批为101-200,依此类推。如果您在“代码”列上定义了聚簇索引,这将是最快的。
在进入循环之前创建数据上下文
在循环内部,只需更新对象
调用SubmitChanges()循环结束后,这将在单个连接/事务中将所有更新发送到数据库
重复下一批
您应该使批量大小可配置,因为您无法确定哪个批量大小将产生最佳性能 - 请勿将其硬编码到应用程序中。
另外,我会使用SingleOrDefault()和null检查而不是Single(),除非你可以保证i的任何值都会有一行。
编辑:
就内存使用而言,这更难控制,但它并不是LINQ to SQL所特有的,任何批处理算法都必须处理这个问题。虽然我不建议在实践中使用GC.Collect(),但通常在处理大批量后作为解决方法就足够了。
您还可以考虑减少每行检索的数据量(取决于开始时的数量)。您可以创建一个新实体,该实体映射到同一个表中的一组更小的列,可能只有一个或两个,这样当您选择该实体时,您只需要检索要开始使用的列。这将改善速度和内存占用,因为更少的数据通过线路传输,并且对象要小得多。
答案 2 :(得分:1)
我无法复制问题。内存使用量持平。性能缓慢,但记忆力不断。
你确定你没有在其他地方泄漏吗?您能否生成一个可以重现问题的最小代码示例?
编辑:
我使用了几乎相同的示例代码:
for (int ii = 1; ii < 200000; ii++)
{
using (var dc = new PlayDataContext())
{
var record =
(from r in dc.T1s where r.Id == ii select r).SingleOrDefault();
if (record != null)
{
record.Name = "S";
dc.SubmitChanges();
}
}
}
没有问题。
所以要排除的事情:
您是否可以使用最新的FW重现问题,并使用一个小样本DataContext?
答案 3 :(得分:0)
您可以尝试在for循环之外创建datacontext,但不确定可以节省多少内存。
也许你可以在每次循环后调用GC.Collect(),看看你是否可以手动进行垃圾收集。