SQL-запрос NHibernate медленный - PullRequest
11 голосов
/ 28 июля 2011

Я использую LINQ to NH, чтобы получить кучу данных при запуске приложения. Я специально добавил ToList() для немедленного выполнения запроса:

Group group = GetGroup();
Log.Info("started");
var list = Session.Linq<Data>()
    .Where(p => p.Group.Id == group.Id)
    .OrderByDescending(p => p.Stamp.Counter) /* Stamp is composite mapping */
    .Select(p => new
    {
        Counter = p.Stamp.Counter,
        Status = p.Status,
    })
    .Take(4000)
    .ToList();
Log.Info("done");

Проверка журнала DEBUG для NHibernate.SQL регистратор выдает следующий SQL, как и ожидалось (и этот же запрос появляется в SQL Profiler, когда я запускаю мониторинг):

SELECT top 4000 this_.Counter as y0_, this_.Status as y1_
FROM [Data] this_ 
LEFT OUTER JOIN [Group] group1_ ON this_.Group_id=group1_.Id
WHERE group1_.Id = @p0 
ORDER BY this_.Counter desc; @p0 = 1

Проблема в том, что этот запрос занимает 2 минуты при вызове из моего приложения, по сравнению с 0,5 с при выполнении в SSMS! На самом деле, пока приложение ожидает завершения запроса, я могу выполнить его в SSMS и мгновенно получить результаты.

Как вы думаете, откуда эта разница?

Ответы [ 4 ]

4 голосов
/ 29 июля 2011

Так как информации о вашем приложении не так много, я могу только догадываться.

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

Log.Info("Flushing");
Session.Flush();
Session.FlushMode = FlushMode.Never;

Log.Info("Query");
var list = Session.Linq<Data>()
    //...
Log.Info("Done");
// for production code, this belongs into a finally block
Session.FlushMode = FlushMode.Auto; 

Если на самом деле является проблемой сброса, вам необходимо выполнить сброс вручную в определенных точках транзакции.Будьте осторожны при отключении автоматической промывки.Это может вызвать неприятные побочные эффекты.Это очень специфично для вашей транзакции, и я не могу сказать вам, как правильно ее осуществить.Вы также можете использовать StatelessSession, но для меня это никогда не работало (у него есть некоторые ограничения).Вы также можете очистить сеанс, который также требует, чтобы вы точно знали, что вы делаете.

Если это не проблема сброса, ее будет сложно отследить.Используйте Profiler, чтобы увидеть, действительно ли это занимает время в запросе к серверу SQL.Это может даже быть проблемой кеширования на сервере SQL.В этом случае на выполнение первого запроса уходит минуты, а во второй раз - секунды.Создание правильных индексов может помочь.Здесь я перестаю догадываться ...

3 голосов
/ 29 июля 2011

Мое предположение, что есть некоторые перехватчики, которые замедляют материализацию объектов или стремительную загрузку (т.е. проблема N + 1).

Я провел несколько тестов, и даже 30 000 объектов не могут замедлить получение списка объектов (с 500 мс на локальном компьютере до 30000 объектов, с удаленной базы данных - 4 секунды).

2 голосов
/ 25 апреля 2013

Один хороший момент из проекта сегодня:

Я искал около недели, потому что мой запрос nHibernate (многокритериальный, использующий фьючерсы для загрузки некоторых коллекций) занял 11 секунд (продолжительность в MSSQL профилировщике) и около 2 секунд, если я выполняю точно такой же комбинированный запрос в SSMS.

Решение было так: у меня было активировано ведение журнала для запуска профилировщика Ayendes. NHProf dll, где отсутствует, но: Некоторые методы GetRows в вызовах журнала триггеров nHibernate во время гидратации. И разница была: 9 секунд!

Я только что закомментировал вызов конфигурации log4net, и задержка почти прошла.

У меня было около 14.000 экземпляров плюс 60.000 записей коллекции HasMany. Гидратация теперь занимает 0,6 секунды, поскольку оператор SQL занимает 2 секунды (это еще одна история оптимизации).

И: я думаю, что продолжительность гидратации вместе с продолжительностью выполнения запроса показана в столбце «продолжительность» профилировщика SQL.

Еще одна история, произошедшая 2 недели назад: «Планы выполнения в профилировщике SQL отличались от планов выполнения при выполнении запроса в SSMS. Причиной этого было то, что я использовал провайдера OLEDB в nHibernate. Я переключился на соединения ADO, и план выполнения был таким же. Я нашел это, глядя на какой-то столбец «версия протокола» в MS SQL profiler.

Существует множество причин для ловушек производительности помимо n + 1:)

С наилучшими пожеланиями! Майкл

2 голосов
/ 28 июля 2011

Есть несколько возможных причин:

  • Вы загружаете не менее 4000 объектов в память, гидратируете их, а NHibernate должен сохранять контроль над каждым загруженным объектом в сеансе и кэше 1-го уровня
  • Я не видел ваших отображений, но весьма вероятно, что в какой-то момент происходит какая-то нетерпеливая загрузка, которая также будет спамить другие запросы и загружать другие объекты и т. Д.

Они приходят с макушки головы, может быть, я еще. Также проверьте, не установлен ли уровень журнала NHibernate на DEBUG, он ОЧЕНЬ подробен и может потреблять МНОГО ресурсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...