2010-09-10 5 views
10

Я повторяю небольшую таблицу (~ 10 ГБ) с помощью foreach/IQueryable и LINQ-to-SQL. Выглядит примерно так:Итерация через IQueryable с foreach приводит к исключению из памяти

using (var conn = new DbEntities() { CommandTimeout = 600*100}) 
{ 
    var dtable = conn.DailyResults.Where(dr => dr.DailyTransactionTypeID == 1); 
    foreach (var dailyResult in dtable) 
    { 
     //Math here, results stored in-memory, but this table is very small. 
     //At the very least compared to stuff I already have in memory. :) 
    } 
} 

Visual Studio отладчик выдает вне-исключение памяти после короткого времени на базе цикла Еогеаспа. Я предполагаю, что строки dtable не очищаются. Что делать?

+0

что вы уже хранятся в памяти, которая больше, чем 10GB? Возможно, вы имели в виду 10 МБ? – msarchet

+0

У меня на этой машине 16 ГБ памяти, но по крайней мере половина из них используется любыми раздутыми окнами и кэшами SQL. Я не мог вместить 10 ГБ в память, поэтому у меня не получилось. Я удивлен тем, что IQueryable извлекает всю таблицу ... Я бы ожидал, что она выберет одно или небольшое количество строк за раз. – Gleno

+0

Кажется, мне удалось немного обойти это, изменив цель компиляции на x64 вместо x86, которая использует больше памяти на моей машине. Тем не менее, данные, которые я повторяю в моем цикле foreach, не огромны, поэтому я думаю, что материал внутри цикла не получает сбор мусора должным образом. –

ответ

12

IQueryable<DailyResult> dtable будет пытаться загрузить весь результат запроса в памяти, когда перечисленные ... до каких-либо итераций цикла Еогеасп. Он не загружает одну строку во время итерации цикла foreach. Если вы хотите этого поведения, используйте DataReader.

+0

К настоящему времени я фактически экспортировал таблицу в плоский файл и читал ее по строкам. В следующий раз я буду использовать DataReader, как профессионал. :) – Gleno

6

Вы называете ~ 10 ГБ маленьким? у вас хорошее чувство юмора!

Вы можете рассмотреть возможность загрузки строк в куски, а также разбиение на страницы.

conn.DailyResults.Where(dr => dr.DailyTransactionTypeID == 1).Skip(x).Take(y); 
+0

Если ОП не имеет 20 ГБ ОЗУ, это единственный способ справиться с ситуацией. – Justin

+1

Я не уверен, вы хотите сказать, что этот метод разбивки на страницы эффективен? Я просто удивлен тем, что IQueriable хочет загружать вещи в память. Я имею в виду, почему бы не сделать его массивным, чтобы указать беспомощному программисту на его неприятные намерения. :) – Gleno

2

Использование DataReader - шаг назад, если нет способа использовать его в LINQ. Я думал, что мы пытаемся уйти от ADO.

Решение, предложенное выше, работает, но оно действительно уродливое. Вот мой код:

int iTake = 40000; 
int iSkip = 0; 
int iLoop; 
ent.CommandTimeout = 6000; 
while (true) 
{ 
    iLoop = 0; 
    IQueryable<viewClaimsBInfo> iInfo = (from q in ent.viewClaimsBInfo 
             where q.WorkDate >= dtStart && 
             q.WorkDate <= dtEnd 
             orderby q.WorkDate 
             select q) 
             .Skip(iSkip).Take(iTake); 
    foreach (viewClaimsBInfo qInfo in iInfo) 
    { 
    iLoop++; 
    if (lstClerk.Contains(qInfo.Clerk.Substring(0, 3))) 
    { 
      /// Various processing.... 
    } 
    } 
    if (iLoop < iTake) 
    break; 
    iSkip += iTake; 
} 

Вы можете видеть, что я должен проверить выбежав из записей, поскольку цикл Еогеасп завершится в 40000 записей. Нехорошо.

Обновлено 6/10/2011: Даже это не сработает. При 20000000 записей или около того я получаю исключение из памяти. Это также мучительно медленно. Когда я модифицировал его для использования OleDB, он запустил около 15 секунд (в отличие от 10 + минут) и не исчерпал память. У кого-нибудь есть решение LINQ, которое работает и работает быстро?

+0

Я не уверен, что я следую некоторым из более странных частей, но идея => Query, Skip, Take.Фантастический, за исключением той части, где теперь у вас возникает другая проблема - сколько нужно взять. Также приветствуем stackoverflow! : D – Gleno

+0

Gleno, спасибо. Я не уверен, что вы считаете «более странными частями», хотя «странным» кажется мое среднее имя. :) Я вернулся к ADO.Net, к сожалению, как отмечалось. –

1

Я бы предпочел использовать SQL вместо этого, чтобы изменить эти данные.

0

Использование .AsNoTracking() - это говорит DbEntities не кэшировать извлекаемых строк

using (var conn = new DbEntities() { CommandTimeout = 600*100}) 
{ 
    var dtable = conn.DailyResults 
       .AsNoTracking()  // <<<<<<<<<<<<<< 
       .Where(dr => dr.DailyTransactionTypeID == 1); 
    foreach (var dailyResult in dtable) 
    { 
     //Math here, results stored in-memory, but this table is very small. 
     //At the very least compared to stuff I already have in memory. :) 
    } 
} 
Смежные вопросы