Entity Framework 6 против Dapper против хранимой процедуры против запроса в SQL Server - PullRequest
0 голосов
/ 17 марта 2019

У меня EF с LINQ с SQL Server в моем проекте долгое время.

И я искал способ улучшить производительность моих запросов к БД.И я много читал о Dapper и процедурах, которые работают быстрее, чем EF.Я добавил Dapper в проект, я добавил процедуру .... но мои тесты показали странные результаты.EF, Dapper и хранимые процедуры дают почти одинаковые результаты - никаких преимуществ.

Прежде всего, я проверил запрос с большим количеством Join.Я получил почти такие же результаты между Dapper и Процедурой и EF.Тогда я решил сделать тест с одной простой таблицей без отношений.

У меня есть таблица ZipCodes.Есть 43200 записей.

Таблица ZipCode

Я провел тестирование на 1 000 записей, 10 000 записей и 43200 записей, используя EF, Dapper, хранимую процедуру и запросв SQL Server.

Dapper

string query =
            "SELECT TOP (43200) [Zip]\r\n      ,[City]\r\n      ,[State]\r\n      ,[Latitude]\r\n      ,[Longitude]\r\n      ,[TimeZone]\r\n      ,[DST]\r\n  FROM [dbo].[ZipCodes]";

using (IDbConnection connection = new SqlConnection(_connectionString))
{
    var result = connection.QueryAsync<ZipCodes>(query).Result.ToList();
    return result;
}

EF

var zip = db.ZipCodes.AsNoTracking().Take(43200).ToList();

Хранимая процедура

ALTER PROCEDURE [dbo].[ZIPTest]
AS 
BEGIN 
    SELECT TOP (43200) 
        [Zip], [City], [State], [Latitude], [Longitude], [TimeZone], [DST] 
    FROM 
        [dbo].[ZipCodes] 
END

Запрос в SQL Server со временем

SELECT GETDATE(); 

SELECT TOP (43200) 
    [Zip], [City], [State], [Latitude], [Longitude], [TimeZone], [DST]
FROM 
    [dbo].[ZipCodes]

SELECT GETDATE();

В коде я использую Секундомер

string first = "", second = "", third="";
System.Diagnostics.Stopwatch swatch = new System.Diagnostics.Stopwatch();
swatch = new Stopwatch();
swatch.Start(); Dapper request;

Затем

swatch.Stop();
first = swatch.Elapsed.ToString(@"m\:ss\.fff");
swatch = new Stopwatch();
swatch.Start();

И так далее

Результаты: (вмиллисекунды)

                       1000     10000      43200
-------------------------------------------------
EF                      107      1085       4527
Dapper                  139      1084       4036
Stored procedure        129      1089       4519
SQL query                 8        17         60

Разница между EF, Dapper и хранимой процедурой очень мала.Почему это так?

И почему запрос в SQL Server так быстр, а запросы из кода в 15-70 раз медленнее?

Это нормально или нет?

Ответы [ 2 ]

1 голос
/ 18 марта 2019

Код с использованием EF, демонстрирующий проблемы с производительностью, не будет волшебным образом работать быстрее с Dapper или ADO + Sprocs. Чтобы разобраться в проблеме с производительностью, вам нужно исследовать и устранить причины проблем с производительностью.

На верхнем уровне эти проблемы производительности связаны с двумя основными проблемами.

  • Загрузка слишком большого количества данных.
  • Загрузка данных слишком часто.

Ключевые вещи, на которые я обращаю внимание: (для начала, предметов гораздо больше, но это большие победы)

  1. Ленивая загрузка: здесь код загружается из набора связанных объектов, но при этом код обращается к этим связанным объектам после начальной загрузки, в результате чего каждый из этих связанных объектов загружается индивидуально .

    • Метод поиска: подключите профилировщик SQL к базе данных, с которой работает только ваш отладочный экземпляр приложения. (Т.е. локальная БД)
    • Симптом: просмотр большого количества запросов «SELECT TOP (1) ...» после основного запроса для загрузки одного объекта или коллекции.
    • Исправление: Быстрое исправление состоит в том, чтобы вводить активные нагрузки (.Include()) для загрузки этих коллекций. Лучшее решение - использовать .Select(), чтобы просто загрузить свойства, необходимые для рассматриваемого кода.
  2. .ToList(): неуместные вызовы .ToList() могут вызвать огромные проблемы с производительностью по мере взросления систем, поскольку разработчики столкнулись с проблемой EF, которая была решена путем вызова .ToList. Обычно они появляются, когда разработчики пытаются вызвать метод внутри выражения .Where() или .Select(). EF не может понять, что они передают эквивалент SQL, поэтому добавление .ToList() преобразует его в Linq2Object, и "та-да" работает!

    • Метод охоты: найдите экземпляры .ToList() и отметьте все случаи, когда вы найдете .ToList() перед .Select() или .Where() и т. Д.
    • Симптом: удаление лишних .ToList() вызывает ошибку EF.
    • Исправлено: Проверьте, имеет ли функция-нарушитель эквивалент DbFunctions. Распространенной проблемой является работа с функциями DateTime, которые можно найти в DbFunctions. В других случаях найдите нарушающую функцию и создайте модель представления для ожидаемых данных, которые выбираются, затем создайте свойство для запуска функции в модели представления.
  3. Пагинация на стороне клиента + сущности: еще один грех разработки без надлежащих выборочных данных Написаны запросы, которые эффективно возвращают всех данных без учета общего числа записей. Данные отображаются на стороне клиента в сетке с разбиением на страницы, которая «работает», но на самом деле очень медленно. Он работал нормально, когда в базе данных было только 200 строк, но теперь сканирует с 50000. (и будет только хуже)

    • Метод охоты: посмотрите на любой метод API / Controller, который возвращает коллекции. Используются ли эти запросы .ToList() или .Skip() + .Take()? Возвращают ли эти методы объекты или просматривают модели?
    • Симптом: нумерованные списки загружаются очень медленно. После загрузки переключение страниц происходит быстро.
    • Исправлено: вызовы из элементов управления нумерацией страниц должны быть изменены, чтобы использовать нумерацию на стороне сервера. Это означает отправку на сервер вызовов сортировки, размера страницы и номера страницы. Таким образом, запросы EF могут быть более эффективными для загрузки только того количества строк, которое должно отображаться элементом управления. Код также должен возвращать модели просмотра для результатов поиска, которые представляют только отображаемые столбцы, и ключи, необходимые для загрузки полной сущности по требованию. (например, когда пользователь щелкает, чтобы открыть запись.) Объекты могут быть тяжелыми, когда вам нужно только отобразить несколько полей.
  4. Индексация базы данных: контролировалась и поддерживалась ли база данных? Существуют ли индексы и ведение индексов? Для SQL Server резервируется ли база данных и хранится ли в ней журнал Tx? Внедрения Code First изобилуют этими проблемами, когда система запускается без учета резервной базы данных. По мере роста систем не уделяется внимания базе данных, поддерживающей их.

    • Метод охоты: У вас есть специальный администратор баз данных, который следит за базой данных и дает полное представление о том, что она работает с нуля?
    • Симптомы: БД или проверка не были предоставлены базе данных. Приложение использует, например, GUID PK, для которых установлены NEWID () или Guid.New() без какого-либо обслуживания индекса. База данных не имеет установленных индексов. Журнал транзакций (.LDF) в 15 раз больше файла базы данных (.MDF) и т. Д. И т. Д. И т. Д.
    • Исправлено: наймите DBA. Если вы используете ключи GUID, переключитесь на NEWSEQUENTIALID () и установите некоторые запланированные задания по обслуживанию индекса.
0 голосов
/ 17 марта 2019

На производительность вашего приложения влияют многие факторы, но получение данных из базы данных в основном можно разделить на 3:

  • время выполнения запроса
  • передача данныхвремя
  • время обработки клиента

При этом, если ваши самые высокие затраты находятся в самой базе данных или при передаче данных, то переключение с FE на Dapper или на простой считыватель данных делаетне имеет смысла.Однако, если ваша самая высокая стоимость приходится на ваше клиентское приложение (высокий параллелизм / небольшие полезные нагрузки / много соединений (linq) / несколько наборов данных / несколько столбцов для сопоставления), то имеет смысл переключиться с FE на более привлекательный или на устройства чтения данных.Однако вы должны понимать, что вы будете торговать простотой использования для производительности.В приложениях, критичных ко времени, это может иметь смысл, в большинстве случаев - нет.

...