Обработка больших наборов данных с использованием LINQ - PullRequest
6 голосов
/ 10 ноября 2009

Каждый раз, когда я пишу программу приведенной ниже формы с использованием LINQ to SQL, я получаю программу, которая просто захватывает все больше и больше памяти при запуске и падает в кучу, занимающую 2 ГБ после, возможно, всего лишь 25 000 записей. Я всегда заканчиваю тем, что переписывал это, используя 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();
    }
}

Ответы [ 4 ]

6 голосов
/ 10 ноября 2009

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

Попробуйте вместо этого использовать скомпилированный запрос.

3 голосов
/ 10 ноября 2009

Вы обращаетесь к базе данных дважды за каждую итерацию цикла - один раз, чтобы получить строку, затем снова, чтобы обновить строку. Это не очень эффективно.

Вы должны работать партиями:

  • Получить набор строк заранее, выбрав диапазон, а не одно значение, то есть 0-100 для первой партии, 101-200 для следующей партии и т. Д. Это будет быстрее, если у вас есть кластеризованный индекс, определенный в столбце Код.

  • Создание контекста данных до входа в цикл

  • Внутри цикла, просто обновите объекты

  • Вызовите SubmitChanges () ПОСЛЕ того, как цикл завершится, это отправит все обновления в базу данных за одно соединение / транзакцию

  • Повторите для следующей партии

Вы должны настроить размер пакета, так как вы не можете быть уверены, какой размер пакета даст лучшую производительность - не вводите его жестко в приложение.

Кроме того, я бы использовал SingleOrDefault () с нулевой проверкой вместо Single (), если только вы не можете гарантировать, что всегда будет строка для любого значения i.

EDIT:

С точки зрения использования памяти контролировать намного сложнее, но это не свойственно LINQ to SQL, с этим должен справиться любой алгоритм пакетной обработки. Хотя я не рекомендую использовать GC.Collect () на практике, этого обычно достаточно в качестве обходного пути после обработки большой партии.

Вы также можете посмотреть на уменьшение количества данных, которые вы извлекаете за строку (в зависимости от того, сколько это нужно для начала). Вы можете создать новую сущность, которая сопоставляется с гораздо меньшим набором столбцов из одной и той же таблицы, возможно, только с одним или двумя, так что при выборе этой сущности вы получаете только те столбцы, с которыми намереваетесь начать работу. Это улучшит как скорость, так и объем занимаемой памяти, так как меньше данных передается по проводам, а объекты намного меньше.

1 голос
/ 10 ноября 2009

Мне не удалось воспроизвести проблему. Использование памяти было плоским. Низкая производительность, но постоянная память.

Вы уверены, что не протекаете в другом месте? Можете ли вы создать минимальный пример кода, который воспроизводит проблему?

Edit:

Я использовал практически тот же пример кода:

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();
        }
    }
}

без проблем.

Итак, что следует исключить:

  • Версия фреймворка. Я на последнем.
  • DataContext / сложность объекта. Моя тестовая таблица состоит из двух полей: Id (int) и Name (nvarchar (max)).

Можете ли вы воспроизвести проблему с последним FW с небольшим образцом DataContext?

0 голосов
/ 10 ноября 2009

вы можете попробовать поместить создание datacontext вне цикла for, хотя не знаете, сколько памяти это спасет.

Возможно, вы можете вызывать GC.Collect () после каждого цикла, посмотрите, можно ли вызвать сборку мусора вручную.

...