使用LINQ to SQL读取巨大的表:耗尽内存与慢速分页

我有一个庞大的表,我需要阅读某个订单并计算一些汇总统计数据。 该表已经有正确顺序的聚集索引,因此获取记录本身非常快。 我正在尝试使用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范例,具有节省内存的附加function。

请注意,事先创建了_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 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导致数据加载越来越慢,因为底层查询似乎实际上导致服务器遍历所有先前页面的所有数据。 在运行查询时,每个页面加载的时间越来越长,我可以说这是一个二次运算。 此问题出现在以下post中:

  • LINQ Skip()问题
  • LINQ2SQL选择命令并跳过/取

我似乎无法通过LINQ找到一种方法来实现这一点,它允许我通过分页数据来限制内存使用,但仍然可以在每个页面加载恒定时间。 有没有办法正确地做到这一点? 我的预感是,可能有某种方式告诉DataContext明确忘记上面第一种方法中的对象,但我无法找到如何做到这一点。

在疯狂地抓住一些吸管之后,我发现DataContextObjectTrackingEnabled = false可能正是医生所要求的。 毫不奇怪,这是专为这样的只读案例而设计的。

 using (var readOnlyDataContext = new MyDataContext(_conn) {CommandTimeout = really_long, ObjectTrackingEnabled = false}) { var logs = (from record in readOnlyDataContext.someTable where [index is appropriate] select record); foreach( linqEntity l in logs ) { // Do stuff with data from l } } 

当通过对象流式传输时,上述方法不使用任何内存。 在编写数据时,我可以使用启用了对象跟踪的不同DataContext ,这似乎工作正常。 但是,这种方法确实存在SQL查询的问题,可能需要一个小时或更长时间才能流式传输和完成,因此,如果有一种方法可以在不影响性能的情况下进行上述分页,我可以选择其他方法。

关于关闭对象跟踪的警告 :我发现当您尝试使用相同的DataContext执行多个并发读取时,您不会收到错误There is already an open DataReader associated with this Command which must be closed first. 应用程序进入无限循环,CPU使用率为100%。 我不确定这是一个C#错误还是一个function。