Как избежать переполнения памяти при запросе больших наборов данных с Entity Framework и LINQ - PullRequest
12 голосов
/ 08 мая 2011

У меня есть класс, который обрабатывает все методы базы данных, включая вещи, связанные с Entity Framework.Когда требуются данные, другие классы могут вызывать метод в этом классе, такой как

public List<LocalDataObject> GetData(int start, int end);

База данных запрашивает, используя LINQ to EF, и вызывающий класс может затем выполнять итерацию по данным.Но так как другие классы не имеют доступа к сущностям в EF, мне нужно выполнить операцию «ToList ()» для запроса и тем самым извлечь полный набор данных в память.

Что произойдет, если этот набор ОЧЕНЬ большой (10–100 с ГБ)?

Существует ли более эффективный способ выполнения итерации и при этом сохранение слабой связи?

Ответы [ 5 ]

20 голосов
/ 08 мая 2011

Правильный способ работы с большими наборами данных в платформе Entity:

  • Использование объектов EFv4 и POCO - это позволит совместно использовать объекты с верхним уровнем без зависимости от структуры Entity Framework
  • Отключите создание прокси / отложенную загрузку, чтобы полностью отсоединить сущность POCO от контекста объекта
  • Предоставьте IQueryable<EntityType>, чтобы позволить верхнему уровню более точно определять запрос и ограничивать количество записей, загружаемых из базы данных
  • При выставлении IQueryable установите MergeOption.NoTracking на ObjectQuery в вашем методе доступа к данным.Объединение этого параметра с отключенным созданием прокси-сервера должно приводить к некэшированным объектам, а итерация по результату запроса всегда должна загружать только один материализованный объект (без кэширования загруженных объектов).

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

5 голосов
/ 08 мая 2011

Как бы мне ни нравился EF для быстрого / простого доступа к данным, я бы, наверное, не использовал его для такого сценария. При работе с данными такого размера я бы выбрал хранимые процедуры, которые возвращают именно то, что вам нужно, и ничего лишнего. Затем используйте облегченный DataReader для заполнения ваших объектов.

DataReader обеспечивает небуферизацию поток данных, который позволяет процедурный логика эффективно результаты процесса из источника данных последовательно. DataReader - хороший выбор, когда извлечение больших объемов данных потому что данные не кэшируются в память.

Кроме того, что касается управления памятью, конечно, убедитесь, что вы обернули свой код, обрабатывающий неуправляемые ресурсы, в , используя блок для правильного удаления / сбора мусора.

Вы также можете рассмотреть возможность реализации paging .

3 голосов
/ 08 мая 2011

Просто краткое замечание по этому вопросу:

Но так как другие классы не имеют доступа для сущностей в EF, мне нужно выполнить операцию «ToList ()» на запрос и тем самым полный выбор набор данных в память.

Проблема, с которой вы здесь сталкиваетесь, на мой взгляд, вообще не связана с EF. Что бы вы сделали, если бы не использовали EF для доступа к данным, а просто ADO.NET? «Отсутствие доступа к EF» переводится как «Отсутствие доступа к подключению к базе данных» .

Если у вас есть службы или методы, которые должны работать с большим количеством объектов, но не можете получить доступ к базе данных - либо через EF, либо через соединение другого типа - вы должны загрузить данные в память, прежде чем передавать их этим службам / методам. Затем вы можете подумать о решениях для буферизации загруженных данных на жестком диске на стороне клиента, но это не имеет никакого отношения к источнику данных и способу их получения. Если вы не буферизуете и загружаете все в память, вы ограничены доступной памятью. Я думаю, что от этого ограничения невозможно избавиться.

Если у вас есть соединение с EF, я думаю, что решение, представленное в ответе Ладислава, является правильным, поскольку описанные им настройки и процедура приводят EF почти к поведению простого DataReader.

0 голосов
/ 08 мая 2011

Один из способов убедиться в этом - всегда устанавливать верхний порог, который вы вернете, чтобы избежать гигантского набора, заканчивая запрос .Take(MAX_ROWS).Это может быть обходной путь или превентивный переход от плохого вызова, который лишает вас обслуживания, но лучше всего переосмыслить решение

0 голосов
/ 08 мая 2011

Я бы использовал ленивый IEnumerable и реализовал бы некоторую подкачку для ваших внутренних данных.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...