Как оптимизировать звонки в Castle ActiveRecord? - PullRequest
3 голосов
/ 27 октября 2010

Как оптимизировать вызовы ActiveRecord в ваших веб-приложениях ASP.NET MVC 2?

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

На планете много стран. В стране много штатов / провинций. В штате много городов. В городе много кварталов.

Ниже приведен пример постоянного бизнес-объекта Castle ActiveRecord / NHibernate

[ActiveRecord]
public class Country {

    [Property]
    public String CountryName { get; set; }

    [HasMany(typeof(States))]
    public IList<State> States { get; set; }
}

Теперь предположим, что вы хотите сделать невинный запрос, например, получить список всех стран на планете

По умолчанию ActiveRecord / Nhibernate будет загружать все дерево до самой последней зависимости.

Это может быть ОЧЕНЬ много вызовов SQL.

Хорошо, мы можем решить эту проблему с ленивой загрузкой [ActiveRecord(lazy=true)], но потом, когда вы захотите сделать что-то аккуратное, как показано ниже

String text = "This country of " + country.CountryName + " has the following states:";

foreach(State s in country.States)
{
    text += s.StateName + " ";
}

С атрибутом отложенной загрузки activerecord каждый раз, когда он выбирает s.StateName, это еще один вызов sql.

Это слишком много вызовов sql.

Некоторые мои друзья предложили использовать методы ActiveRecordBase, такие как FindAll (критерии).

Но в конечном итоге он становится действительно уродливым и трудным для чтения со всеми этими Expression.Eq, а что нет.

А потом другие люди также предложили использовать что-то вроде HQL. HQL выглядит очень похоже на SQL.

Итак, суть в том, что самый чистый и оптимизированный способ - это писать простые и простые SQL-запросы. Это идет прямо к делу.

SELECT * FROM Countries //No loading of an entire tree of dependencies

SELECT * FROM States WHERE CountryId = @selectedId   //1 call and you have all your states

В любом случае, сейчас я использую SQL-запросы и хранимые процедуры для извлечения данных и ActiveRecord для сохранения / удаления объектов.

Пожалуйста, поправьте меня, если я ошибаюсь ...?

Спасибо Оценил.

Ответы [ 3 ]

4 голосов
/ 27 октября 2010

Рекомендуется использовать по умолчанию lazy mappings, а затем охотно извлекать то, что вам нужно для каждого случая.

Если вы не используете ленивый по умолчанию, вы получаете почти всю базу данных в каждом запросе, как вы уже заметили.

Если вы используете ленивый, но не выбираете с нетерпением, вы сталкиваетесь с проблемой SELECT N + 1 , которую вы заметили в примере foreach.

Все, что я упомянул до сих пор, не зависит от используемого вами API запроса. Вы можете сделать все это, используя HQL, Criteria, Linq или QueryOver. Некоторые запросы легче выразить в HQL, а в некоторых случаях удобнее использовать Criteria, Linq или QueryOver. Но опять же, это ортогональный вопрос.

Кроме того, это ничем не отличается от запуска необработанных SQL-запросов в ADO.NET. В вашем примере, если вы хотите, чтобы страна имела свои штаты, вы бы включили INNER JOINed, чтобы получить все штаты заранее, а не выдавать отдельные SELECts для каждого штата.

2 голосов
/ 27 октября 2010

Это хорошо известная проблема с ORM.Одним из способов решения этой проблемы является добавление параметра BatchSize nhibernate, чтобы при каждой поездке на БД вы возвращали n записей (в данном случае состояния) вместо одной.

[HasMany(typeof(States), BatchSize = 20)]
public IList<State> States { get; set; }
0 голосов
/ 27 октября 2010

Звучит очень странно, извините, скажем!

Одним из больших преимуществ ORM является то, что вы можете абстрагировать свою базу данных ...!Так что да, HQL может ощущаться, выглядеть и звучать как SQL, но у него есть одно большое отличие: он независим от СУБД.Тот же HQL будет работать для SQL Server, Oracle или MySQL ...

Вам также понадобится немного поработать, чтобы разделить соединение с вашей базой данных между NHibernate и SQL Server.Возможно, но немного некрасиво.

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

OTOH, в реальных сценариях выВозможно, потребуется отображение нумерации страниц для отображения такого большого количества данных.И если это так, то ленивый подход будет намного лучше, даже если он потенциально генерирует гораздо больше операторов SQL.

Просто подумайте об этом: если пользователь запрашивает набор результатов из 1 000 000записей, но он / она видит только первые 20 записей на веб-странице, мне не нужно получать ВСЕ 1 000 000 записей из базы данных.И это именно то, для чего предназначена ленивая загрузка ...

Редактировать: почти забыл кэширование NHibernate первого и второго уровня.Используя это, ваше приложение может избежать запросов к уровню СУБД.Одно это может привести к ОГРОМНОМУ повышению производительности ...

...