Entity Framework - первый запрос медленный
Как видно из названия, у меня возникла проблема с первым запросом к базе данных SQL Server с использованием Entity Framework.
Я пробовал искать ответ на разных сайтах, но никто, похоже, не имеет решения для этого.
Я загружаю довольно много строк из базы данных, включая два отношения 0-много.
Тесты были выполнены в Visual Studio 2010 с использованием модели Entity Framework 4.0 и генератора POCO (разница между синхронизациями между обычными объектами и объектами POCO невелика). Я также использовал шаблон T4 Views для предварительной компиляции представлений.
База данных находилась на SQL Server 2008.
Мне бы очень хотелось знать, почему первый запрос намного медленнее любых вторичных запросов.
Я также хочу знать, можно ли что-то сделать, чтобы увеличить скорость первого запроса до точки, где она находится в допустимых пределах.
Это большой запрос, и мы можем получить другие запросы, которые еще больше, и понятно, что они могут быть немного медленными, но 30 секунд слишком медленны, чтобы пользователь мог ждать, особенно когда наборы данных могут получать одни и те же данные. быстрее.
Я провел несколько тестов времени, чтобы попытаться выяснить, в чем проблема, и я был немного удивлен, увидев, что похоже на то, что SQL Server медленнее первого запроса.
Сроки были следующими:
Приложение для тестирования .NET:
- Первый запрос: 29,6 секунд
- Второй запрос: 3,2 секунды
SQL Profiler:
- Первый запрос: 27 секунд
- Второй запрос: 3,2 секунды
Окно запросов SQL Server
- Первый запрос: 8 секунд
- Второй запрос: 4 секунды
Сроки в приложении измерялись с помощью класса Stopwatch
. Был запрограммирован только запрос и .ToList()
был использован для выполнения запроса.
Сроки в SQL Server Profiler предназначены для тех же запросов, которые были выполнены в приложении, которое показывает, что приложение использует только 2,6 секунды для заполнения данных в объектах.
Последние 27 секунд используются для выполнения запроса на SQL Server.
Рассматривая вторичный запрос, тайминги одинаковы для приложения и SQL-сервера, но выполнение запроса на этот раз намного быстрее.
Я могу понять, почему приложение не использует какое-либо время, потому что нет новых строк, которые нужно преобразовать в объекты, но почему запрос намного быстрее, я бы ожидал несколько секунд из-за планов выполнения, но не 24 секунды.
Просто для тестирования я скопировал SQL, который Entity Framework генерирует и открывает новое окно запроса с отдельным подключением и выполняет запрос в нем.
Как вы видите, для первого запроса требуется 8 секунд и 4 секунды для второго.
Я надеюсь, что у кого-то есть предложения.
пс. Прошу прощения за стену текста:)
Редактировать 19-10-2010:
Вчера я сделал тест, который, похоже, поддерживает, что строки возвращаются последовательным образом. Это означает, что когда строка возвращается из базы данных, она немедленно материализуется (если она еще не существует в контексте), возвращается следующая строка и т.д.
Вот почему кажется, что запрос занимает много времени на сервере базы данных, потому что время материализации включено в тайминги профилировщика SQL Server.
Я не считаю, что это случай чтения SQL Server из жесткого диска.
Медленный запрос выполняется каждый раз, когда в EF есть "первый запрос".
ех.
- Запустите первый запрос с EF, оператор SQL будет медленнее, чем любой вторичный запрос
- Утилизировать контекст/репозиторий
- Создать новый контекст
- Запустите тот же запрос, что и раньше (снова первый запрос выполняется медленно, а также оператор SQL)
Это похоже на то, что EF отправляет некоторые параметры вместе с первым запросом, который замедляет работу сервера.
Что касается компиляции запросов, как я помню, запрос скомпилирован в первый раз, когда он используется, что означает, что первый запрос займет еще больше времени.
Вторичные запросы будут быстрее, но скорость по вторичным запросам не является проблемой.
Я также проверил, где я создал скомпилированный запрос как статический, чтобы он был скомпилирован для всех созданных контекстов.
Затем я создал контекст, выполнил запрос, уничтожил контекст и создал новый, и снова выполнил тот же запрос.
Разница была не такой большой, всего несколько секунд, и в первый раз, когда я запустил запрос, все еще продолжалось до тех пор, пока он не был предварительно скомпилирован.
Что касается генерации представлений, мы уже реализуем это с использованием шаблонов T4.
Действительно ли ответ, что EF работает, только если вы ничего не делаете, кроме простейших запросов, которые возвращают только относительно небольшой объем данных?
Ответы
Ответ 1
У нас была такая же проблема в EF 5.0, и на сегодняшний день поверхностный поиск Google не выявил достаточного ускорения.
В соответствии с этой ссылкой http://msdn.microsoft.com/en-us/library/cc853327(v=vs.100).aspx "Загрузка метаданных" несет умеренную временную стоимость, но она должна выполняться только один раз за AppDomain. Я не нашел предварительной компиляции, как трюки для загрузки метаданных.
Обходной путь, который мы реализовали, состоит в том, чтобы выполнить небольшой запрос в Контексте в отдельном потоке при запуске приложения. Это загружает метаданные, это по-прежнему занимает много времени (18-19 секунд в нашем случае), но приложение реагирует во время загрузки. Также первая фактическая нагрузка не занимает столько времени.
Обратите внимание, что в нашем контексте пользователь может потратить 18-19 секунд в приложении до того, как вызов EF необходимо будет выполнить в ответ на их действия. Очевидно, что если это невозможно в вашем приложении, эта работа может не обеспечить большую часть увеличения скорости.
Ответ 2
У меня была такая же проблема. И я использовал трюк, чтобы решить эту проблему. Поскольку инфраструктура Entity занимает немного больше времени при первом доступе, а затем кэширует часть результата в первый раз на этом уровне (сервер sql слишком кэширует результат отдельно). Таким образом, я получил доступ к инфраструктуре Entity для моего приложения, асинхронно. Это сработало для меня. И мое приложение стало более плавным.
На странице Global.asax
protected void Application_Start()
{
Start(() =>
{
using (EF.DMEntities context = new EF.DMEntities())
{
context.DMUsers.FirstOrDefault();
}
});
}
private void Start(Action a)
{
a.BeginInvoke(null, null);
}
Ответ 3
Ну, многие вещи могут сделать запрос SQL Server медленнее при первом запуске. Однако большинство из них не принимают несколько секунд.
... Кроме случайного доступа к жесткому диску. При первом запуске запроса SQL Server, возможно, придется читать страницы базы данных с жесткого диска. При следующем запуске запроса эти страницы, вероятно, будут в памяти.
Что касается платформы Entity Framework, при первом запуске запроса она должна быть скомпилирована в SQL. Вы можете использовать тип CompiledQuery
для предварительной компиляции запросов Entity Framework, чтобы выполнить эту работу заблаговременно, прежде чем конечный пользователь должен ее дождаться.
В очень большой модели поколение просмотров занимает некоторое время. Вместо этого вы можете переместить это, чтобы скомпилировать время. См. эту статью для получения дополнительных советов.
Ответ 4
У нас та же проблема. Это только с первым подходом Code. У нас есть около 1500 POCOs (+1500 картографических файлов POCO). Просто компиляция занимает около 1-2 минут. Метод Context.table.Add() занимает около 3-4 минут, но для первого объекта. Это как плохая шутка без решения.
Эти 3-4 минуты, вероятно, являются своего рода EF "POCO transform". Одно ядро процессора работает на 100%, и в профилировщике SQL ничего не происходит.
Использование первого подхода к базе данных (создание файла edmx xml) для тех же 1500 таблиц работает нормально. Это быстро, как ожидалось.
Пока нет решения. Возможно, EF6 решит это.
Ответ 5
Я просто наткнулся на это сообщение в своем поиске, чтобы улучшить время запуска EF. Поскольку у него действительно нет ответа, я добавлю свои выводы, чтобы другие люди могли воспользоваться им, если они наткнулись на этот пост тоже.
Обратите внимание, что я использую EF 6, и это решение применимо только для EF 6.
Дэвид Рот опубликовал статью в которой рассматривается проблема.
Микаэль Элиассон в своем ответе очень хорошо ответил на аналогичный вопрос:
- Использование хранилища моделей кэшированных db
- Создание предварительно скомпилированных представлений
- Сгенерируйте предварительно скомпилированную версию сущностной схемы, используя n-gen, чтобы избежать джитта