Запрос Entity Framework медленный, но тот же SQL в SqlQuery выполняется быстро


Я вижу некоторые действительно странные perf, связанные с очень простым запросом с использованием кода Entity Framework-сначала с .NET framework версии 4. Запрос LINQ2Entities выглядит следующим образом:

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

Это занимает более 3000 миллисекунд для выполнения. Сгенерированный SQL выглядит очень просто:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

этот запрос выполняется почти мгновенно при запуске через Management Studio. Когда я изменяю код C# для использования функции SqlQuery, он работает в 5-10 миллисекунды:

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

Итак, точно такой же SQL, результирующие сущности отслеживаются в обоих случаях, но дикая разница perf между ними. Что это дает?

7 61

7 ответов:

нашли его. Оказывается это проблема типов данных SQL. Элемент SomeStringProp столбец в базе данных был varchar, но EF предполагает, что типы строк .NET являются nvarchars. Результирующий процесс перевода во время запроса к БД для выполнения сравнения-это то, что занимает много времени. Я думаю, что EF Prof немного сбивал меня с толку здесь, более точное представление выполняемого запроса было бы следующим:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

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

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}

причиной замедления моих запросов, сделанных в EF, было сравнение не нулевых скаляров с нулевыми скалярами:

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

этот запрос занял 35 секунд. Но крошечный рефакторинг вроде этого:

long? userId = 10;
long userIdValue = userId.Value; // I've done that only for the presentation pursposes

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

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

Если вы используете свободное отображение, вы можете использовать IsUnicode(false) в составе конфигурации, чтобы получить тот же эффект -

http://msdn.microsoft.com/en-us/data/jj591617.aspx#1.9

http://msdn.microsoft.com/en-us/library/gg696416%28v=vs.103%29.aspx

У меня была та же проблема (запрос выполняется быстро из SQL manager), но при выполнении из EF истекает тайм-аут.

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

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

чтобы найти эту настройку (ef 6), перейдите в раздел .edmx-файл и посмотрите в свойствах - > генерация кода - > ленивая загрузка включена. Задать значение false.

массовое улучшение производительности для меня.

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

  1. Set ctx.Configuration.ProxyCreationEnabled до false прямо перед тем, как вы получите контекст.
  2. и .Select(c => new {c.someproperty}) будет получать только необходимые данные, а не всю кучу.

Дайте мне знать, если это помогло.

У меня тоже была эта проблема. Оказывается виновником в моем случае был SQL-сервер параметр sniffing.

первая подсказка, что моя проблема была на самом деле из-за нюхания параметров, заключалась в том, что выполнение запроса с "set arithabort off" или "set arithabort on" привело к совершенно разным временам выполнения в Management Studio. Это потому что ADO.NET по умолчанию используется "set arithabort off", а среда Management Studio по умолчанию - "set arithabort on". Кэш плана запроса сохраняет разные планы в зависимости от этого параметра.

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