2015-03-24 2 views
11

У меня есть Entity Framework 4.1 с .NET 4.5, работающий на ASP.NET в Windows 2008R2. Я использую EF-код для подключения к SQL Server 2008R2 и выполнения довольно сложного запроса LINQ, но в результате получается только Count().Чрезвычайно медленное и неэффективное выполнение запроса из Entity Framework

Я воспроизвел проблему на двух разных веб-серверах, но только с одной базой данных (производство, конечно). Недавно он начал происходить без приложения, структуры базы данных или изменений сервера в Интернете или на стороне базы данных.

Моя проблема заключается в том, что выполнение запроса при определенных обстоятельствах занимает смехотворное количество времени (около 4 минут). Я могу взять фактический запрос, извлечь из SQL Profiler и выполнить в SSMS примерно через 1 секунду. Это непротиворечиво и воспроизводимо для меня, но если я изменил значение одного из параметров (параметр «Дата после 2015-01-22») на что-то раньше, например 2015-01-01 или позже, как 2015-02- 01, он отлично работает в EF. Но я вернул его в 2015-01-22, и он снова замедлился. Я могу повторять это снова и снова.

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

Но это все говорит мне, что сами данные в порядке.

В профайлере, когда запрос выполняется должным образом, он занимает около секунды или два и отображает около 2 000 000 при чтении и около 2000 в CPU. Когда он работает медленно, это занимает 3,5 минуты, а значения - 300 000 000 и 200 000 - поэтому чтение примерно в 150 раз выше, а процессор в 100 раз выше. Опять же, для идентичного оператора SQL.

Любые предложения по поводу того, что EF может делать по-другому, что бы не отображалось в тексте запроса? Есть ли какое-то скрытое свойство соединения, которое может привести к другому плану выполнения при определенных обстоятельствах?

EDIT

запрос, который EF строит это один из тех, где он строит гигантский строку с параметром, включаемые в текст, а не в качестве параметра SQL:

exec sp_executesql 
    N'SELECT [GroupBy1].[A1] AS [C1] 
    FROM ( 
      SELECT COUNT(1) AS [A1] 
      ... 
      AND ([Extent1].[Added_Time] >= convert(datetime2, ''2015-01-22 00:00:00.0000000'', 121)) 
      ... 
      ) AS [GroupBy1]' 

EDIT

Я не добавляю это в качестве ответа, так как на самом деле он не затрагивает основную проблему, но в конечном итоге это достигается путем восстановления индексов и пересчет статистики. Это не было сделано дольше, чем обычно, и, похоже, выяснилось, что вызвало эту проблему.

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

Спасибо за все идеи.

+0

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

+0

Как масштабируется запрос? Увеличивает ли количество записей прямое влияние на производительность? –

+3

Это звучит как плохой параметр, нюхающий меня. Просмотрите эту статью по этой теме. http://sqlinthewild.co.za/index.php/2007/11/27/parameter-sniffing/ (убедитесь, что вы прочитали все три сегмента) –

ответ

0

Существует замечательная статья об оценке производительности Entity Framework here.

Хотелось бы обратить ваше внимание на раздел о Холоде против.Теплое выполнение запросов:

Самый первый раз, когда любой запрос сделан в отношении данной модели Entity Framework делает много работы за кулисами, чтобы загрузить и проверки модели. Мы часто ссылаемся на этот первый запрос как на «холодный» запрос . Дальнейшие запросы к уже загруженной модели - это , известные как «теплые» запросы, и намного быстрее.

Во время выполнения запроса LINQ шаг «Загрузка метаданных» оказывает большое влияние на производительность для выполнения холодных запросов. Однако, когда загруженные метаданные будут кэшироваться, а будущие запросы будут выполняться намного быстрее. Метаданные кэшируются вне DbContext и будут повторно использоваться до тех пор, пока пул приложений будет работать.

В целях повышения эффективности работы, рассмотрят следующие действия:

  • использовать предварительно сгенерированный вид
  • использования кэширование плана запроса
  • использовать не отслеживание запросов (только если доступ только для чтения)
  • создания собственного образа Entity Framework (релевантно только при использовании EF 6 или более поздней версии)

Все эти POIN ts хорошо описаны в приведенной выше ссылке. Кроме того, вы можете найти дополнительную информацию о создании собственного образа Entity Framework here.

+0

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

+0

Ничто из этого не занимает 4 минуты. – usr

+3

Спасибо за информацию - если бы это было всего несколько секунд, я бы согласился, что это может быть связано с загрузкой модели, но так как это несколько минут и происходит снова и снова, а результаты SQL Profiler настолько смехотворно отличаются, я 'm склоняется к какой-то проблеме, выполняющей фактический запрос, а не к загрузке EF. –

-3

Понимая, что вы используете Entity Framework 4.1, я хотел бы предложить вам перейти на Entity Framework 6.

Там уже было много улучшений производительности и EF 6 намного быстрее, чем EF 4.1.

MSDN article about Entity Framework performance consideration упоминается в моем другом ответ имеет также сравнение между EF 4.1 и EF 6.

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

+2

Это неспецифично для вопроса. Он может быть опубликован как ответ на вопрос о 1000 вопросах переполнения стека. – usr

+0

Я могу рассмотреть возможность обновления или, желательно, сброса EF в целом, но, как вы сказали, это займет значительное количество рефакторинга и тестирования. –

+0

@usr: он может не стать приемлемым ответом, но я считаю, что это немного жестко, чтобы уменьшить голос, который предлагает обновить до последней версии EF, когда у вас есть проблемы с производительностью. Это было бы первым, что я сделал бы. –

0

У меня нет конкретного ответа на вопрос, почему это происходит, но, похоже, это связано с тем, как запрос обрабатывается больше, чем сам запрос. Если вы скажете, что у вас нет проблем с тем же сгенерированным запросом из SSMS, то это не проблема.

Обходной путь, который вы можете попробовать: Хранимая процедура. EF может справиться с ними очень хорошо, и это идеальный способ справиться с потенциально сложными или дорогостоящими запросами.

+0

Мне бы хотелось перейти на procs или обычный SQL, но, как я уже упоминал, это массивный запрос EF LINQ (с большим количеством динамических предложений, зависящих от того, что вы ищете), так что это огромная задача, что я Еще не готов. –

5

У меня недавно был очень похожий сценарий, запрос выполнялся очень быстро, выполняя его непосредственно в базе данных, но имел ужасную производительность с использованием EF (версия 5, в моем случае). Это не проблема сети, разница была от 4 мс до 10 минут.

Проблема оказалась проблемой сопоставления. У меня был столбец, сопоставленный с NVARCHAR, а в базе данных было VARCHAR. Кажется безобидным, но это привело к неявному преобразованию в базу данных, что полностью испортило производительность.

Я не совсем уверен, почему это происходит, но из тестов, которые я сделал, это привело к базе данных делает в Index Scan вместо Index Seek, и, видимо, они очень разные точки зрения производительности ,

comparison index scan and index seek

Я писал о этом here (оговорке: это на португальском языке), но позже я обнаружил, что Джимми Богард описал эту точную проблему в post с 2012 года, я предлагаю вам проверить это.

Поскольку у вас есть конвертер в вашем запросе, я бы сказал, начните с него. Дважды проверьте все сопоставления столбцов и проверьте различия между столбцом таблицы и свойством объекта. Избегайте неявных преобразований в вашем запросе.
Если вы можете проверить ваш execution plan, чтобы найти какие-либо несоответствия, быть в курсе желтого предупреждающего треугольника, который может указывать на проблемы, как эта о делает неявное преобразование:

query issue implicit conversion warning

Я надеюсь, что это поможет вам как-то , это была действительно сложная проблема для нас, но в конце концов она имела смысл.

+0

Это была золотая информация с сопоставлением столбцов. Спасибо! – KroaX

0

Просто поставить это там, поскольку он не рассматривался как возможность:

Учитывая, что вы используете Entity Framework (EF), если вы используете отложенную загрузку сущностей, то EF требует несколько активных Result Устанавливает (MARS) для включения через строку подключения. Хотя это может показаться полностью несвязанным, MARS иногда производит такое точное поведение чего-то быстро работающего в SSMS, но ужасно медленно (секунды становятся несколько минут) через EF.

Один из способов протестировать это, чтобы отключить Lazy Loading и либо удалить MultipleActiveResultSets=True; (по умолчанию «false»), либо, по крайней мере, изменить его на MultipleActiveResultSets=False;.

Насколько я знаю, к сожалению, к сожалению, нет работы или исправления (в настоящее время).

Вот пример этого вопроса: Same query with the same query plan takes ~10x longer when executed from ADO.NET vs. SMSS

+0

Интересно, спасибо - не фактор здесь, так как это был до одного однозначного запроса, поэтому MARS не вступал в игру.Но это дает мне еще одну причину быть осторожным с Entity Framework. –

Смежные вопросы