NHibernate IsUpdateNecessary занимает огромное количество времени

Мое приложение C# 3.5 использует SQL Server 2008 R2, NHibernate и CastleProject ActiveRecord. Приложение импортирует электронные письма в базу данных вместе с их вложениями. Сохранение электронных писем и вложений выполняется по 50 электронных писем в новом сеансе и области транзакции, чтобы убедиться, что они не хранятся в памяти (в некоторых почтовых ящиках может быть 100 000 писем).

Изначально электронные письма сохраняются очень быстро. Однако ближе к 20 000 писем производительность резко снижается. С помощью dotTrace я получил следующую картину:

введите здесь описание изображения

Очевидно, что когда я сохраняю вложение, NHibernate пытается понять, действительно ли оно должно быть сохранено, и, возможно, сравнивает с другими вложениями в сеансе. Для этого он сравнивает их побайтно, что занимает почти 500 секунд (для снимка на картинке) и 600M операций счетчика.

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

Однако я не могу понять, как указать NHibernate избегать этой проверки (IsUpdateNecessary). Пожалуйста, порекомендуйте.

P.S. Я не уверен, но может показаться, что снижение производительности ближе к 20K не связано с наличием в памяти некоторых старых писем: я заметил, что в почтовом ящике, с которым я работаю, большие письма сохраняются позже, чем маленькие, поэтому проблема может быть только в сравнение вложений.

Обновление: Похоже, мне нужно что-то вроде StatelessSessionScope, но документации по нему нет даже на сайте CastleProject! Если я сделаю что-то вроде

using (TransactionScope txScope = new TransactionScope())
using (StatelessSessionScope scope = new StatelessSessionScope())
{
  mail.Save();
}

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


person Alex    schedule 23.07.2011    source источник
comment
Чем я крайне недоволен, так это тем, что эта грязная проверка выполняется побайтно. Это единственное объяснение числа 600M. Однако я почти уверен, что эти вложения отличаются первым или вторым байтом, поэтому я ожидаю 1-2 сравнения на пару вложений, всего около 1500 сравнений на 50 писем, а не 600 миллионов!   -  person Alex    schedule 24.07.2011


Ответы (2)


Может быть, я пропустил это в этом длинном тексте, но вы используете сеанс без сохранения состояния для импорта данных? Использование этого предотвращает множество проверок, а также обходит кеш первого уровня, таким образом используя минимальные ресурсы.

person Vijay Gill    schedule 23.07.2011
comment
Я использую следующее: using (new TransactionScope()) using (SessionScope scope = new SessionScope()) { DoDatabaseOperation(); } Простите меня за невежество, но я не уверен, как создать сеанс без сохранения состояния через SessionScope. - person Alex; 24.07.2011
comment
Похоже, мне нужно StatelessSessionScope вместо SessionScope... сейчас проверю. Спасибо, Виджай! - person Alex; 24.07.2011
comment
К сожалению, это не удается: Castle.ActiveRecord.Framework.ActiveRecordException: Не удалось выполнить сохранение для почты ---› Castle.ActiveRecord.Framework.NotWrappedException: Вызванный метод не поддерживается. ActiveRecord в настоящее время работает в StatelessSessionScope. Сеансы без сохранения состояния выполняются быстрее, чем обычные сеансы, но они не поддерживают все методы и свойства, которые допускает обычный сеанс. - person Alex; 24.07.2011
comment
Я думаю, что еще может мне помочь, так это FlushMode.Always для текущего сеанса. Однако у меня нет сеанса, у меня есть SessionScope, и я понятия не имею, как установить этот режим очистки. - person Alex; 24.07.2011
comment
Я использую это в своем коде: var session = ActiveRecordMediator.GetSessionFactoryHolder().GetSessionFactory(typeof(ActiveRecordBase)).OpenStatelessSession()) для открытия сеанса без сохранения состояния, а затем с помощью session.Create(ваш объект объекта) для фактического сохранения объекта. - person Vijay Gill; 24.07.2011
comment
Если у вас возникнут какие-либо проблемы, спросите меня, потому что я использую Castle AR последние 5 лет, и этот опыт может быть полезен и другим. Но я не говорю, что я гуру :) - person Vijay Gill; 24.07.2011

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

protected override int[] FindDirty(
    object id, 
    System.Collections.IDictionary previousState, 
    System.Collections.IDictionary currentState, NHibernate.Type.IType[] types)
  {
    return new int[0];
  }

Таким образом, грязная проверка всегда считается грязной и не делает этого сумасшедшего побайтового сравнения.

person Alex    schedule 24.07.2011