Entity Framework работает слишком медленно. Какие у меня варианты? [закрыто]

93

Я последовал мантре «Не оптимизируйте преждевременно» и написал свою службу WCF с помощью Entity Framework.

Однако я проанализировал производительность, и Entity Framework работает слишком медленно. (Мое приложение обрабатывает 2 сообщения примерно за 1,2 секунды, в то время как (устаревшее) приложение, которое я переписываю, выполняет 5-6 сообщений одновременно (устаревшее приложение вызывает sprocs для доступа к БД).

Мои профилирование указывает на то, что Entity Framework занимает большую часть времени на сообщение.

Итак, какие у меня варианты?

  • Есть ли лучшие ORM?
    (Что-то, что поддерживает нормальное чтение и запись объектов и делает это быстро ..)

  • Есть ли способ сделать Entity Framework быстрее?
    ( Примечание : когда я говорю «быстрее», я имею в виду долгий срок, а не первый вызов. (Первый вызов медленный (15 секунд для сообщения), но это не проблема. Мне просто нужно, чтобы он был быстрым для остальных сообщений.)

  • Какой-то таинственный третий вариант, который поможет мне получить больше скорости от моей службы.

ПРИМЕЧАНИЕ. Большинство моих взаимодействий с БД - это создание и обновление. Я очень мало выделяю и удаляю.

Vaccano
источник
Звучит как повторение «linq работает медленно», откуда вы знаете, что это EF? Вы профилировали все свои изменения?
Maess 01
6
Некоторые ответы указывают на вопросы. По моему опыту, медлительность в EF имеет мало общего с запросами, а скорее с затратами на материализацию, и эти затраты часто связаны с отслеживанием изменений и тем, как это влияет на созданные экземпляры. К сожалению, у меня нет для вас серебряной пули, поэтому это всего лишь комментарий, но я бы порекомендовал посмотреть, показывает ли профилирование высокие затраты на материализацию, и, если да, изучить, что можно сделать с указанными затратами.
Энтони Пеграм,
@Maess - Я думал, что указал, что профилировал, и обнаружил, что именно EF / DB работает медленно. В любом случае, да, я сделал. Я профилировал это, и это взаимодействие EF / DB является основным виновником.
Vaccano 01
@Anthony - Разве материализация не работает с первого раза? Если да, то вы правы, что это очень медленно. Первый пробег очень медленный. Но, как я уже сказал, меня это не слишком беспокоит. Проблема заключается в общей пропускной способности. (Если это не материализация, тогда мне нужно провести небольшое исследование, чтобы увидеть, является ли это причиной моей проблемы)
Vaccano
1
@Vaccano, нет, материализация - это процесс взятия данных из базы данных, создания и заполнения графа объектов для представления этих данных. Я не говорю о производительности при первом запуске, поскольку код изменен (или даже когда Sql Server может создать план выполнения запроса), а о том, что происходит каждый раз, когда вы получаете данные в форме объектов.
Энтони Пеграм

Ответы:

45

Вы должны начать с профилирования команд SQL, фактически выданных Entity Framework. В зависимости от вашей конфигурации (POCO, Self-Tracking entity) есть много возможностей для оптимизации. Вы можете отлаживать команды SQL (которые не должны различаться между режимами отладки и выпуска), используяObjectSet<T>.ToTraceString() метод. Если вы столкнулись с запросом, требующим дальнейшей оптимизации, вы можете использовать некоторые прогнозы, чтобы предоставить EF больше информации о том, что вы пытаетесь выполнить.

Пример:

Product product = db.Products.SingleOrDefault(p => p.Id == 10);
// executes SELECT * FROM Products WHERE Id = 10

ProductDto dto = new ProductDto();
foreach (Category category in product.Categories)
// executes SELECT * FROM Categories WHERE ProductId = 10
{
    dto.Categories.Add(new CategoryDto { Name = category.Name });
}

Можно заменить на:

var query = from p in db.Products
            where p.Id == 10
            select new
            {
                p.Name,
                Categories = from c in p.Categories select c.Name
            };
ProductDto dto = new ProductDto();
foreach (var categoryName in query.Single().Categories)
// Executes SELECT p.Id, c.Name FROM Products as p, Categories as c WHERE p.Id = 10 AND p.Id = c.ProductId
{
    dto.Categories.Add(new CategoryDto { Name = categoryName });
}

Я просто набрал это из головы, так что это не совсем то, как он будет выполняться, но EF на самом деле делает некоторые хорошие оптимизации, если вы сообщите ему все, что вы знаете о запросе (в этом случае нам понадобится категория - имена). Но это не похоже на нетерпеливую загрузку (db.Products.Include ("Categories")), потому что проекции могут еще больше уменьшить объем данных для загрузки.

Я. Тихон
источник
40
Этот ответ звучит разумно, пока вы не поймете, что анонимные типы недоступны вне метода, в котором они определены. Если вы хотите загрузить сложный объект, а не писать мегамот, вам нужно десериализовать ваши новые анонимные типы в своего рода POCO. Опять же, это звучит почти разумно, пока вы не поймете, что, поступая таким образом, вы по существу ПЕРЕПИСЫВАЕТЕ РАМКУ СОБСТВЕННОГО ЛИЦА. Это чушь собачья.
Дуг
5
Это привело к увеличению скорости в 15-20 раз.
Дэйв Кузино
12
Интересный и полезный ответ, действующий даже спустя некоторое время. @Doug: Это не совсем чушь, поскольку вы оптимизируете (используя прогнозы) только те несколько запросов, в которых вам действительно нужно использовать дополнительное преимущество. EF и POCO дают разумные значения по умолчанию, что очень приятно!
Виктор
2
@Doug В большинстве приложений есть модели просмотра для сценариев только для просмотра, верно? С таким же успехом можно выполнять сопоставление, как и данные.
Кейси
4
Я привык чувствовать, что будущее за ORM. Они просто имели смысл, пока я не начал их использовать. Потом я нашел Даппера . Теперь, когда я вижу подобные решения, я съеживаюсь от того, как быстро возрастает сложность. Написание абстрактного SQL на C # - это не путь по жизни.
Майкл Сильвер
80

Дело в том, что такие продукты, как Entity Framework, ВСЕГДА будут медленными и неэффективными, потому что они выполняют намного больше кода.

Я также считаю глупым то, что люди предлагают оптимизировать запросы LINQ, смотреть на сгенерированный SQL, использовать отладчики, выполнять предварительную компиляцию, предпринимать много дополнительных шагов и т. Д., Т.е. тратить много времени. Никто не говорит - упрощайте! Каждый хочет еще больше усложнить ситуацию, делая еще больше шагов (тратя время).

Здравый смысл заключается в том, чтобы вообще не использовать EF или LINQ. Используйте простой SQL. В этом нет ничего плохого. Тот факт, что у программистов есть стадное мышление и они чувствуют потребность использовать каждый новый продукт, не означает, что он хорош или будет работать. Большинство программистов думают, что если они включают каждый новый фрагмент кода, выпущенный большой компанией, это делает их более умным программистом; совсем не так. Умное программирование в основном о том, как делать больше с меньшими головными болями, неопределенностями и с минимальным временем. Помните - время! Это самый важный элемент, поэтому постарайтесь найти способы не тратить его на решение проблем с плохим / раздутым кодом, написанным просто для соответствия каким-то странным так называемым `` шаблонам ''.

Расслабьтесь, наслаждайтесь жизнью, отдохните от программирования и перестаньте использовать дополнительные функции, код, продукты, «шаблоны». Жизнь коротка, а жизнь вашего кода еще короче, и это, конечно, не ракетостроение. Удалите такие слои, как LINQ, EF и другие, и ваш код будет работать эффективно, будет масштабироваться, и да, его по-прежнему будет легко поддерживать. Слишком много абстракции - плохой «шаблон».

И это решение вашей проблемы.

Шон
источник
156
Это выплескивание ребенка вместе с водой из ванны. Вы оптимизируете узкие места, глупо отказываться от EF, потому что в некоторых местах он слишком медленный, а в большинстве других - достаточно быстрый. Почему бы не использовать оба? EF отлично справляется с хранимыми процедурами и необработанным SQL. Я только что преобразовал запрос LINQ-to-SQL, который занимал 10+ секунд, в SP, который занимает ~ 1 секунду, но я не собираюсь отбрасывать все LINQ-to-SQL. Это сэкономило МНОГО времени в других, более простых случаях, с меньшим количеством кода и меньшим количеством ошибок, а запросы проверяются компилятором и соответствуют базе данных. Чем меньше кода, тем проще обслуживание и меньше места для ошибок.
JulianR
12
В целом ваш совет хорош, но я не думаю, что правильно отказываться от EF или других абстракций, потому что они не работают хорошо в 10% случаев.
JulianR
50
Простой SQL = легко поддерживать? Это не так для очень больших приложений с большим количеством бизнес-логики. Написание сложного многоразового SQL - задача не из легких. Лично у меня были некоторые проблемы с производительностью с EF, но эти проблемы просто несопоставимы с преимуществами правильного ORM с точки зрения RAD и сохранения СУХИХ вещей (если есть какой-либо уровень сложности).
MemeDeveloper
13
+ 10 ^ 100 Слишком много абстракции - плохой «паттерн»
Макач
58
-1. «EF ВСЕГДА будет медленным и неэффективным». Я не понимаю, почему вы утверждаете, что что-то подобное является абсолютной истиной. Наличие большего количества слоев сделает что-то медленнее, но будет ли эта разница вообще ЗАМЕТИТЬСЯ полностью зависит от ситуации, такой как объем данных и тип выполняемого запроса. Для меня это то же самое, что сказать: «C # ВСЕГДА будет медленным и неэффективным», потому что это более высокая абстракция, чем C ++. Тем не менее, многие люди предпочитают использовать его, потому что прирост производительности намного превышает потерю производительности (если таковая имеется). То же самое и с EF
Despertar
37

Одно из предложений - использовать LINQ to Entity Framework только для операторов CRUD с одной записью.

Для более сложных запросов, поиска, отчетности и т. Д. Напишите хранимую процедуру и добавьте ее в модель Entity Framework, как описано в MSDN .

Это подход, который я применил к нескольким своим сайтам, и кажется, что это хороший компромисс между производительностью и производительностью. Entity Framework не всегда генерирует наиболее эффективный SQL-запрос для решения поставленной задачи. И вместо того, чтобы тратить время на выяснение причин, написание хранимой процедуры для более сложных запросов фактически экономит мое время. Как только вы ознакомитесь с процессом, добавление сохраненных процедур в вашу модель EF не составит большого труда. И, конечно же, преимущество его добавления в вашу модель состоит в том, что вы получаете все те строго типизированные достоинства, которые дает использование ORM.

Стив Уортэм
источник
У вас есть представление о методах, используемых в строительных лесах, таких как db.athlete.find (id) и т. Д. Как они работают по сравнению с ADO.NET или dapper ??
Это ловушка
15

Если вы просто извлекаете данные, это значительно повышает производительность, когда вы указываете EF не отслеживать извлекаемые объекты. Сделайте это с помощью MergeOption.NoTracking. EF просто сгенерирует запрос, выполнит его и десериализует результаты в объекты, но не будет пытаться отслеживать изменения сущностей или что-либо подобное. Если запрос простой (не тратит много времени на ожидание возврата из базы данных), я обнаружил, что установка его на NoTracking может удвоить производительность запроса.

См. Эту статью MSDN о перечислении MergeOption:

Разрешение идентификации, управление состоянием и отслеживание изменений

Кажется, это хорошая статья о производительности EF:

Производительность и Entity Framework

JulianR
источник
9
Прежде чем кто-либо это сделает, было бы неплохо прочитать здесь. stackoverflow.com/questions/9259480/…
leen3o
6

Вы говорите, что профилировали приложение. Вы тоже профилировали ORM? Существует профилировщик EF от Ayende, который укажет, где вы можете оптимизировать код EF. Вы можете найти это здесь:

http://efprof.com/

Помните, что вы можете использовать традиционный SQL-подход вместе с ORM, если вам нужно повысить производительность.

Есть ли более быстрый / лучший ORM? В зависимости от вашей модели объекта / данных вы можете рассмотреть возможность использования одного из микро-ORM, например Dapper , Massive или PetaPoco. .

Сайт Dapper публикует несколько сравнительных тестов, которые дадут вам представление о том, как они сравниваются с другими ORM. Но стоит отметить, что микро-ORM не поддерживают богатый набор функций полных ORM, таких как EF и NH.

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

Шон Кирон
источник
4

Я нашел ответ @Slauma здесь очень полезным для ускорения работы. Я использовал один и тот же шаблон как для вставок, так и для обновлений - и производительность резко возросла.

MemeDeveloper
источник
2

По моему опыту, проблема не в EF, а в самом подходе ORM.

В общем, все ORM страдают от проблемы N + 1, не оптимизированных запросов и т. Д. Лучше всего было бы отследить запросы, которые вызывают снижение производительности, и попытаться настроить инструмент ORM или переписать эти части с помощью SPROC.

Валера Колупаев
источник
2
Люди продолжают мне это говорить. Но я настрою простой оператор выбора с использованием старой школы ADO, и такой же простой выбор с использованием контекста EF и EF всегда значительно медленнее. Я действительно хочу полюбить EF, но он все усложняет, а не облегчает жизнь.
Sinaesthetic
1
@Sinaesthetic Конечно, медленнее. Точно так же код, написанный с использованием Linq to Objects, обычно медленнее, чем код, написанный без него. Вопрос не действительно ли это быстрее или так же быстро (как это может быть, когда под капотом у него все еще должен выдать запрос , который вы выпускали вручную?) , Но является ли 1) это все равно достаточно быстро для ваших потребностей 2) она позволяет экономить время написания кода 3) выгода компенсирует затраты. Основываясь на этих элементах, я думаю, что EF подходит для многих проектов.
Кейси
@Sinaesthetic Я бы также добавил, что если вы не используете ORM, то чаще всего происходит не то, что каждый SQL-запрос настроен и оптимизирован, а то, что приложение в конечном итоге разрабатывает внутреннюю, органическую, плохо -поддерживаемая, плохо работающая ORM, если только ваша команда не очень дисциплинирована и не очень озабочена производительностью.
Кейси
1

Это простой вариант без фреймворка, без ORM, который загружается со скоростью 10 000 в секунду с 30 полями или около того. Работает на старом ноутбуке, вероятно, быстрее, чем в реальной среде.

https://sourceforge.net/projects/dopersistence/?source=directory

Андрей
источник
1

Я тоже столкнулся с этой проблемой. Ненавижу выгружать EF, потому что он работает так хорошо, но просто медленно. В большинстве случаев я просто хочу найти запись или обновить / вставить. Даже такие простые операции выполняются медленно. Я извлек 1100 записей из таблицы в список, и эта операция с EF заняла 6 секунд. Для меня это слишком долго, даже сохранение занимает слишком много времени.

В итоге я сделал свой собственный ORM. Я извлек те же 1100 записей из базы данных, и мой ORM занял 2 секунды, что намного быстрее, чем EF. Все с моим ORM происходит почти мгновенно. Единственное ограничение на данный момент заключается в том, что он работает только с MS SQL Server, но его можно изменить для работы с другими, такими как Oracle. Я использую MS SQL Server для всего прямо сейчас.

Если вы хотите попробовать мой ORM, вот ссылка и веб-сайт:

https://github.com/jdemeuse1204/OR-M-Data-Entities

Или, если вы хотите использовать самородок:

PM> Install-Package OR-M_DataEntities

Там же есть документация

TheMiddleMan
источник
0

Оптимизация имеет смысл только после профилирования. Если вы обнаружите, что доступ к БД медленный, вы можете перейти на использование хранимых процедур и сохранить EF. Если вы обнаружите, что медленным является сам EF, возможно, вам придется переключиться на другой ORM или вообще не использовать ORM.

Гейб
источник
0

У нас есть похожее приложение (Wcf -> EF -> database), которое легко выполняет 120 запросов в секунду, поэтому я более чем уверен, что EF - не ваша проблема, при этом я заметил значительные улучшения производительности с помощью скомпилированных запросов.

нп-жесткий
источник
98% моего кода - это вызовы Create и Update. Не знаю, имеет ли это значение, но это намного медленнее, чем 120 в секунду.
Vaccano 01
да, это не было бы типичным приложением, я бы посоветовал вам профилировать ваше приложение. для нас в основном это читается ...
np-hard
0

Я использовал EF, LINQ to SQL и dapper. Dapper самый быстрый. Пример: мне нужно 1000 основных записей по 4 подзаписи в каждой. Я использовал LINQ to sql, это заняло около 6 секунд. Затем я переключился на dapper, получил 2 набора записей из одной хранимой процедуры и для каждой записи добавил вспомогательные записи. Общее время 1 секунда.

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

Я бы посоветовал использовать EF или LINQ to SQL и в определенных ситуациях переключиться на dapper.

tfa
источник
-1

Entity Framework сама по себе не должна вызывать серьезных узких мест. Скорее всего, есть и другие причины. Вы можете попробовать переключить EF на Linq2SQL, оба имеют функции сравнения, и код должен быть легко преобразован, но во многих случаях Linq2SQL быстрее, чем EF.

Виктор Зихла
источник