2016-03-17 4 views
2

У меня есть веб-сервис, который касается 50+ таблиц базы данных (база данных в значительной степени нормализована), чтобы создать ответ. Служба возвращает все рейсы, измененные в пределах диапазона дат, указанного клиентом.Получение данных из 50 + таблиц с использованием Linq-to-Nhibernate

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

Я сломал запрос на более мелкие части, используя Nhibernate Fetch + ToFuture для нетерпеливого нагрузки данные мне нужно:

var fetchQuery = Session.Query<Voyage>() 
.Fetch(v => v.VoyageStatus) 
.FetchMany(v => v.VoyageLocations) 
.Where(v => voyageIds.Contains(v.VoyageID)) 
.ToFuture(); 

Session.Query<Ship>() 
.FetchMany(s => s.ShipCsos) 
.Where(s => shipIds.Contains(s.ShipID)) 
.ToFuture(); 

Session.Query<Ship>() 
.Fetch(s => s.ShipFlagCode) 
.ThenFetch(sf => sf.Country) 
.Fetch(s => s.ShipType) 
.Fetch(s => s.ShipStatus) 
.Fetch(s => s.ShipSource) 
.Fetch(s => s.ShipHullType) 
.Fetch(s => s.ShipLengthType) 
.Fetch(s => s.ShipBreadthType) 
.Fetch(s => s.ShipSpeedType) 
.Fetch(s => s.ShipPowerType) 
.FetchMany(s => s.ShipAttributes) 
.ThenFetch(sa => sa.ShipAttributeName) 
.Where(s => shipIds.Contains(s.ShipID)) 
.ToFuture(); 

//[Lots of similar Session.Query<X>...ToFuture() calls] 

return fetchQuery.ToList(); 

Проблема

Я начал бить в SQL Server предел параметра 2100, когда диапазон дат достигает определенного диапазона. Я думал, что ограничение параметра применяется только к одному предложению IN, но, по-видимому, оно относится к запросу в целом; используя Futures. Я получаю один запрос SQL с одним оператором SELECT для каждого вызова ToFuture (каждый оператор SELECT содержит предложение IN с умеренным размером).

Есть ли обходной путь для этого? Например, существует ли способ отправить меньшие группы фьючерсов, чтобы они оставались в пределах предела параметра и все еще гидратировали сущности?

Я попытался выполнить вызов fetchQuery.ToList() на полпути через Futures. Это ограничивает исключения параметров в заливке, но объекты не гидратируются надлежащим образом в соответствии с Nhibernate Profiler (свойства загружаются лениво).

Любые указатели были бы высоко оценены!

ответ

4

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

(Хотеть, чтобы переключиться на жадную загрузку по причинам производительности могут быть признаком того, не зная, как оптимизировать отложенную загрузку с NHibernate. NHibernate может избежать классического вопроса п +-производительность ленивых нагрузок.)

Почему ленивая загрузка может хорошо работать с NH

(Даже в вашем случае.)

ленивый загрузкой с NHibernate может быть очень выполнения. Он имеет тенденцию сохранять хороший баланс между выступлениями во время исполнения и результатами разработки. Эффективное исполнение, а также эффективное развитие и поддержка.

Отрегулируйте ленивую загрузку batch-size собственности на ваши объекты и коллекции.

(Linked reference дать подробное объяснение того, как это работает.)

<class name="YourEntity" batch-size="20"> 
    ... 
    <set name="SomeChildren" batch-size="15" ...> 

Настройка, что вызывает NHibernate не только загружать связанные объекты/коллекции, когда они доступны, но и включать в загрузке до batch-size - 1 связанные объекты/коллекции, которые он отслеживал в своем кеше первого уровня сессии. Разумеется, скорректируйте значения batch-size для соответствия коэффициентам загрузки ваших обычных случаев.

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

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

Вы можете глобально настроить размер партии по умолчанию для всех ленивых нагрузок коллекций и объектов с глобальным параметром конфигурации default_batch_fetch_size (для установки в спящий режим .cfg.xml, или установить через Configuration.SetProperty(Environment.DefaultBatchFetchSize, ...)).

Почему жадная загрузка может быть худшим выбором

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

EF до 6-й версии делал это. (7, может быть, и нет.) В своей стратегии запроса на загрузку загружаемых на основе результатов наборов результатов содержались дублированные данные, как только «корневые» сущности устанавливали, где имеется множество ссылок на те же экземпляры дочерних объектов с нетерпением. (И все это, хотя в моем нынешнем состоянии знания, я склонен считать EF более удобным, чем NHibernate, о загрузке с нетерпением. Но это довольно давно я не рассматривал и не изучал нетерпеливую загрузку с NHibernate, ее ленивую загрузку быть способ более эффективен, чем один EF в.)

Дополнительная оптимизация доступны с ленивой загрузкой

NHibernate имеет строение в поддержку second level caching. Кэширование второго уровня позволяет кэшировать данные и делиться ими между различными сеансами NHibernate.

С загрузкой кеша второго уровня нельзя использовать для загрузки зависимых объектов из памяти (если вы используете память cache provider для кеша второго уровня). Кэш второго уровня лучше всего использовать с ленивой загрузкой.

Это полнофункциональный кэш данных, который автоматически обрабатывает недействительность данных. (Provided you work with transactions). Если взаимоблокировки мешают вам сделать это, возможно, вам стоит включить режим read committed snapshot на SQL Server, но это немного не по теме. Без явных транзакций кеш будет отключен, как только вы начнете обновлять объекты в своем приложение.)

Вам нужно только включить его в глобальной конфигурации (cache.provider_class, cache.use_second_level_cache), и объявить в своем отображении, что кэшируется (на юридических лиц и/или коллекции сущностей, с <cache usage="..." /> тега). Используйте регионы кеша для истечения срока действия. Вы можете даже кэшировать запросы (cache.use_query_cache и указывать на запросы, если они кэшируются). См. here for an example.

Конечно, для вашего случая, если ваши данные не подходят для кэширования, эта функция не полезна. (Это может быть в случае, если другие процессы действительно обновить свои данные, в то время как вы не хотите использовать и настроить провайдер SysCache2, который может получить уведомление посредством SQL-сервера каких-либо изменений данных.)

Примечание стороны

хорошо принятое решение вашей проблемы подразумевает большую работу. В идеале ваше приложение на передней панели должно работать с де-нормированной копией ваших данных, легко и эффективно запрашивать, в то время как ваш бэк-офис поддерживает нормализованную базу данных.

+0

Большое спасибо за ваш подробный ответ! Ссылка, которую вы рассказали о пакетной загрузке, действительно интересна, я не знал, что эта функция существует - она, похоже, обеспечивает хороший баланс между ремонтопригодностью и производительностью. Я также рассмотрю кеш второго уровня. Опять же, спасибо! – matsho