Entity Framework занадто повільний. Які у мене варіанти? [зачинено]


93

Я дотримувався мантри "Не оптимізувати передчасно" і закодував свою службу WCF за допомогою Entity Framework.

Однак я сформулював продуктивність, і Entity Framework занадто повільний. (Мій додаток обробляє 2 повідомлення приблизно за 1,2 секунди, де (застаріла) програма, яку я переписую, робить 5-6 повідомлень одночасно. (Спадкова програма викликає sprocs для своєї DB Access.)

Моє профілювання вказує на Entity Framework, що займає основну частину часу на повідомлення.

Отже, які у мене варіанти?

  • Чи є там кращі ORM?
    (Щось, що просто підтримує нормальне читання та запис предметів і робить це швидко ..)

  • Чи є спосіб зробити Entity Framework швидшим?
    ( Примітка : коли я кажу швидше, я маю на увазі довготривалий, а не перший дзвінок. (Перший дзвінок повільний (15 секунд для повідомлення), але це не проблема. Мені просто потрібно, щоб він був швидким для решти повідомлень.)

  • Якийсь таємничий 3-й варіант, який допоможе мені швидше скористатися послугою.

ПРИМІТКА. Більшість моїх взаємодій з БД - це створення та оновлення. Я дуже мало вибираю та видаляю.


Це звучить як переробка "linq є повільним", звідки ви знаєте, що це EF? Ви профілізували всі свої зміни?
Месс

6
Деякі відповіді вказують на запитання. На моєму досвіді, повільність у EF мало пов’язана із запитами, а натомість із витратами на матеріалізацію, і ці витрати часто пов’язані з відстеженням змін та тим, як це впливає на створені екземпляри. На жаль, у мене немає для вас срібної кулі, тож це лише коментар, але я б порадив перевірити, чи виявляє профілювання високі витрати на матеріалізацію, а якщо так, то дослідити, що можна зробити щодо зазначених витрат.
Ентоні Пеграм

@Maess - Я думав, що вказав, що профайлював, і виявив, що це EF / DB, що повільно. У будь-якому випадку, так я зробив. Я сформулював це, і саме взаємодія EF / DB є головним винуватцем.
Ваккано

@Anthony - Хіба матеріалізація не є першим видом речей? Якщо так, то ви праві, що це дуже повільно. Перший запуск дуже повільний. Але, як я вже зазначив, я не надто переживаю це. Проблема полягає в загальній пропускній здатності. (Якщо це не те, що Матеріалізація, то мені потрібно провести кілька досліджень, щоб з’ясувати, чи це є причиною мого випуску)
Ваккано

1
@Vaccano, ні, матеріалізація - це процес взяття даних із бази даних та створення та створення графіку об’єктів для представлення цих даних. Я не кажу про продуктивність першого запуску, оскільки код змивається (або навіть як Sql Server може створити план виконання запиту), а про те, що відбувається кожного разу, коли ви отримуєте дані у вигляді об’єктів.
Ентоні Пеграм

Відповіді:


46

Почніть з профілювання команд SQL, фактично виданих Entity Framework. Залежно від вашої конфігурації (POCO, об’єкти самовідстеження) є багато місця для оптимізації. Ви можете налагодити команди 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 ("Категорії")), оскільки прогнози можуть ще більше зменшити обсяг даних для завантаження.


40
Ця відповідь звучить розумно, поки ви не зрозумієте, що анонімні типи недоступні поза методом, у якому вони визначені. Якщо ви хочете завантажити складний об'єкт, а не писати мегамот, вам потрібно десеріалізувати нові анонімні типи в якийсь POCO. Знову ж таки, це майже звучить розумно, поки ви не зрозумієте, що, роблячи це, ви по суті переписали ВЛАСНУ РАМКУ АНТИТЕТІВ. Що фігня.
Дуг

5
це призвело до збільшення швидкості в 15x-20x для мене.
Dave Cousineau

11
Цікава та корисна відповідь, дійсна через деякий час. @Doug: Що насправді не фігня, оскільки ви лише оптимізуєте (використовуючи прогнози) ті декілька запитів, де вам дійсно потрібно скористатися додатковою вигодою. EF та POCO дають вам розумний дефолт, що дуже приємно!
Віктор

2
@Doug У більшості програм є моделі перегляду для сценаріїв лише для перегляду, чи не так? Ви можете також зробити стільки картографії, скільки витягнете дані.
Кейсі

4
Я звик вважати, що ОРМ - це майбутнє. Вони просто мали сенс, поки я не почав ними користуватися. Тоді я знайшов Даппера . Тепер, бачачи подібні рішення, я злякаюся від того, як швидко зростає складність. Написання абстрагованого SQL на C # - це не шлях у житті.
Michael Silver

80

Справа в тому, що такі продукти, як Entity Framework, ЗАВЖДИ будуть повільними та неефективними, оскільки вони виконують набагато більше коду.

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

Здоровим підходом було б взагалі не використовувати EF або LINQ. Використовуйте звичайний SQL. У цьому немає нічого поганого. Те, що серед програмістів існує ментальність стада, і вони відчувають бажання використовувати кожен новий продукт, ще не означає, що він хороший або він буде працювати. Більшість програмістів вважають, що якщо вони включають кожен новий фрагмент коду, випущений великою компанією, це робить їх розумнішими програмістами; зовсім неправда. Розумне програмування - це здебільшого те, як зробити більше, зменшивши головний біль, невизначеність і за найменший проміжок часу. Запам’ятайте - Час! Це найважливіший елемент, тому спробуйте знайти способи не витрачати його на вирішення проблем у поганому / роздутому коді, написаному просто для того, щоб відповідати якимось дивним так званим "шаблонам"

Розслабтеся, насолоджуйтесь життям, перервіться у кодуванні та припиніть користуватися додатковими функціями, кодом, продуктами, 'шаблонами'. Життя коротке, а життя вашого коду ще коротше, і це, звичайно, не ракетна наука. Видаліть шари, такі як LINQ, EF та інші, і ваш код працюватиме ефективно, масштабуватиметься, і так, його все одно буде легко підтримувати. Занадто велика абстракція - це поганий "шаблон".

І це рішення вашої проблеми.


155
Це викидання дитини з водою для ванни. Ви оптимізуєте вузькі місця, глупо викидати EF, бо в деяких місцях це занадто повільно, а в більшості інших це досить швидко. Чому б не використовувати обидва? EF чудово обробляє збережені процедури та необроблений SQL. Я щойно перетворив запит LINQ-to-SQL, який зайняв 10+ секунд, у SP, який займає ~ 1 секунду, але я не збираюся викидати всі LINQ-to-SQL. Це заощадило БАГАТО часу в інших простих випадках, з меншим кодом і менше місця для помилок, а запити перевіряються компілятором і відповідають базі даних. Менше коду полегшує обслуговування і менше місця для помилок.
JulianR

11
Загалом, ваша порада хороша, але я не вважаю правильним відмовлятися від EF або інших абстракцій, оскільки вони не працюють добре у 10% випадків.
JulianR

49
Звичайний SQL = простий в обслуговуванні? Просто це не стосується дуже великих програм з великою кількістю ділової логіки. Написання складного багаторазового SQL - справа непроста. Особисто у мене були деякі проблеми з продуктивністю з EF, але ці проблеми просто не порівнюються з перевагами належного ORM з точки зору RAD та збереження речей на сухій основі (якщо це пов'язано з будь-яким рівнем складності).
MemeDeveloper

13
+ 10 ^ 100 Занадто велика абстракція - це поганий "шаблон"
Макач

57
-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:

Ефективність та структура організації


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, і це описано на веб-сайті товару .


3

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


2

З мого досвіду, проблема не з EF, а з самим підходом ORM.

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


1
Люди постійно мені це говорять. Але я налаштую простий оператор select, використовуючи стару школу ADO, і той самий простий вибір, використовуючи контекст EF та EF, завжди значно повільніший. Мені дуже хочеться сподобатися EF, але це постійно ускладнює життя, а не полегшує.
Sinaesthetic

1
@Sinaesthetic Звичайно це повільніше. Таким же чином, код, написаний за допомогою Linq to Objects, зазвичай повільніший, ніж код, написаний без нього. Питання не дійсно це швидше або так само швидко (як це може бути, коли під капотом у нього все ще повинен видати запит , який ви випускали вручну?) , Але є чи 1) це все одно достатньо швидко для ваших потреб 2) вона дозволяє економити ви час написання коду 3) вигоди компенсують витрати. Виходячи з цих пунктів, я вважаю, що EF підходить для багатьох проектів.
Кейсі

@Sinaesthetic Я б також додав, що якщо ви не використовуєте ORM, частіше за все трапляється не те, що кожен SQL-запит точно налаштований та оптимізований, а те, що додаток закінчує розробкою власної, органічної, погано -підтримувана, неефективна ORM, якщо ваша команда не має виняткової дисципліни та не турбується про ефективність.
Кейсі

1

Це простий нефреймовий варіант, який не має ORM, який завантажується зі швидкістю 10000 / секунду з 30 полями або близько того. Працює на старому ноутбуці, тож, швидше за все, швидше, ніж у реальному середовищі.

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


1

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

У підсумку я зробив свій власний 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

Документація також є там


0

Має сенс оптимізувати лише після того, як ви створили профіль. Якщо ви виявите, що доступ до БД повільний, ви можете перейти на використання збережених процедур і зберегти EF. Якщо ви виявите, що саме EF є повільним, можливо, вам доведеться перейти на інший ORM або взагалі не використовувати ORM.


0

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


98% мого коду - Створення та оновлення дзвінків. Не знаю, чи має це різницю, але це набагато повільніше, ніж 120 на секунду.
Ваккано

так, це не було б типовим додатком, я б запропонував вам створити профіль вашої програми. для нас це переважно читається ...
np-hard

0

Я використовував EF, LINQ to SQL та dapper. Dapper - це найшвидший. Приклад: Мені потрібно було 1000 основних записів з 4 підзаписами в кожному. Я використовував LINQ для sql, це займало близько 6 секунд. Потім я перейшов на dapper, отримав 2 набори записів з однієї збереженої процедури і для кожного запису додав допоміжні записи. Загальний час 1 секунда.

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

Моя порада полягала б у використанні EF або LINQ для SQL, а в певних ситуаціях перехід на dapper.


-1

Структура організації не повинна сама по собі спричиняти великі вузькі місця. Швидше за все, є й інші причини. Ви можете спробувати переключити EF на Linq2SQL, обидва мають порівняльні функції, і код повинен бути легким для перетворення, але в багатьох випадках Linq2SQL швидший, ніж EF.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.