Parallel.ForEach используется с NHibernate, что приводит к блокировкам SQL Server

Во-первых, я не очень разбираюсь в многопоточности и параллельном программировании.

Я пытаюсь оптимизировать производительность устаревшего приложения (.Net 4, NHibernate 2.1).

** Пока что модернизация NHibernate не является приоритетом, но находится в стадии разработки.

Со временем производительность стала кошмаром с ростом данных. Одна вещь, которую я видел, это Parallel.ForEach оператор, который вызывает метод, который выбирает и обновляет сложную сущность (с несколькими взаимосвязями - свойства и коллекции).

Кусок кода имеет следующую форму (упрощено для ясности):

void SomeMethod(ICollection<TheClass> itemsToProcess)
{
   Parallel.ForEach(itemsToProcess, item => ProcessItem(item);
}

TheClass ProcessItem(TheClass i)
{
   var temp = NHibernateRepository.SomeFetchMethod(i);
   var result = NHibernateRepository.Update(temp);
   return result;
}

SQL Server периодически сообщает об ошибках блокировки базы данных со следующей ошибкой:

Transaction (Process ID 20) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction

Я подозреваю, что это из-за какого-то состояния гонки, приводящего к тупику, даже если ISessions являются отдельными.

ICollection<TheClass> может иметь до 1000 элементов, каждый со свойствами и вложенными коллекциями, которые обрабатываются, генерируя много SELECT а также UPDATE заявления (подтверждено с помощью "NHibernate Profiler")

Есть ли лучший способ справиться с этим параллельно, или я должен рефакторинг кода в традиционный цикл?

Я знаю, что могу альтернативно реализовать свой код, используя:

  1. foreach петля в том же ISession контекст
  2. Сессией без гражданства
  3. С Environment.BatchSize установить разумное значение

ИЛИ ЖЕ

  1. Использование SQL BulkCopy

Я также прочитал довольно много полезной информации о взаимоблокировках SQL Server и Parallel.ForEach Быть легкой ловушкой:

  1. Транзакция SQL была заблокирована
  2. Использование SQL Bulk Copy в качестве альтернативы
  3. Потенциальные ловушки в параллелизме данных и задач
  4. Многопоточное приложение C# с вызовами базы данных SQL Server

1 ответ

Это очень сложная тема. Есть одна стратегия, которая гарантированно безопасна и, вероятно, приведет к ускорению:

Повторите попытку в случае тупика.

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

Хорошая вещь о повторных попытках состоит в том, что вы можете сделать простое изменение кода в центральном месте.

Поскольку это не видно из опубликованного кода: убедитесь, что потоки не разделяют сеанс или сущности. Ни один из них не является потокобезопасным.

Другие вопросы по тегам