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

And*_*Mao 5 c# sql linq database skip-take

我有一个庞大的表,我需要阅读某个订单并计算一些汇总统计数据.该表已经有一个正确顺序的聚集索引,因此获取记录本身非常快.我正在尝试使用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
}
Run Code Online (Sandbox Code Playgroud)

这是非常快的,并且流速很好,但问题是应用程序的内存使用量不断上升.我的猜测是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我有了所需的行为,因为当我通过数据流时,内存使用量根本没有增加.然而,我有一个更糟糕的问题:每个Skip都导致数据加载越来越慢,因为底层查询似乎实际上导致服务器遍历所有先前页面的所有数据.在运行查询时,每个页面加载的时间越来越长,我可以说这正在转变为二次运算.此问题出现在以下帖子中:

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

And*_*Mao 16

在一些稻草疯狂抓后,我发现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   
    }                
}
Run Code Online (Sandbox Code Playgroud)

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

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