Взаимоблокировки объединенных подклассов Hibernate с MSSQL

Я использую Hibernate с присоединенными подклассами для сопоставления иерархии классов с базой данных. К сожалению, это вызывает взаимоблокировки, когда объект обновляется, когда другой поток пытается загрузить тот же объект. С объектами, сопоставленными с одной таблицей, это не проблема. По-видимому, это вызвано тем, как MSSQL получает блокировки для таблиц иерархии классов.

Когда Hibernate загружает объект из базы данных, он использует SELECT с JOIN:

SELECT ...
FROM
    subclass
    LEFT JOIN class
        ON ...
WHERE ...

Когда Hibernate обновляет объект этого подкласса, он делает:

UPDATE
    class
SET ...
WHERE ...

UPDATE
    subclass
SET ...
WHERE ...

Проблема в том, что если объект загружается между двумя операторами обновления, это вызывает взаимоблокировку. Оператор SELECT блокирует 2 таблицы одну за другой. Итак, что, кажется, происходит:

  1. Поток 1 загружает объект и устанавливает общие блокировки на обе таблицы.
  2. Поток 1 выполняет оператор UPDATE для таблицы классов и обновляет блокировку таблицы классов до монопольной блокировки.
  3. Поток 2 пытается загрузить тот же объект, выполняя оператор SELECT, он устанавливает общую блокировку на таблицу подклассов, а затем ждет, пока монопольная блокировка таблицы класса не будет снята.
  4. Поток 1 выполняет оператор UPDATE для таблицы подклассов, он хочет обновить свою блокировку таблицы подклассов до эксклюзивной блокировки, но таблица уже заблокирована потоком 2, ожидающим потока 1.
  5. Поток 2 прерван из-за взаимоблокировки с потоком 1.

График взаимоблокировок выглядит следующим образом: График взаимоблокировок

Эти объекты часто обновляются тихо, и это постоянно вызывает взаимоблокировки, даже когда загружается только один объект. Я также пытался воспроизвести проблему с HSQLDB, но тогда она не блокируется, кажется, что HSQLDB либо блокирует обе таблицы одновременно, либо ждет, пока не сможет заблокировать обе, так что эта проблема возникает только с MSSQL.

Каким было бы решение, чтобы избежать этой проблемы с Hibernate без изменения схемы (кроме индексов)?


person Reboot    schedule 02.09.2010    source источник
comment
Я бы оставил Hibernate в стороне, чтобы задать ваш вопрос, на самом деле это больше вопрос MSSQL, чем вопрос Hibernate (и если вы пометите его соответствующим образом, вы, вероятно, получите больше внимания). В любом случае, какую именно изоляцию транзакций вы используете? PS: даже не пытайтесь воспроизвести проблему с HSQLDB (не уверен, какую версию вы используете, но HSQLDB 1.8.2 поддерживает только уровень изоляции READ_UNCOMMITTED, поэтому ситуация сильно отличается).   -  person Pascal Thivent    schedule 02.09.2010
comment
Возможно, но тот факт, что загрузка объекта создает соединение для обеих таблиц, а обновление объекта создает 2 отдельных оператора UPDATE, исходит из Hibernate. Если я оставлю Hibernate, если это так, то решением может быть запуск обоих обновлений в отдельных транзакциях, возможно, или изменение оператора SELECT. Но это то, что я не могу сделать, потому что эти утверждения генерируются Hibernate. Сначала я протестировал HSQLDB 1.8.x и заметил, что он не работает, а затем переключился на 2.0, который откладывал оператор SELECT потока 2 до тех пор, пока не будет зафиксирована транзакция потока 1.   -  person Reboot    schedule 02.09.2010
comment
Тот факт, генерируются операторы или нет, не имеет значения, что я и имел в виду. Просто задайте вопрос об этом конкретном сценарии (что, кстати, правильно, запуск обновлений в отдельных запросах невозможен). Но не стесняйтесь игнорировать мой совет :)   -  person Pascal Thivent    schedule 02.09.2010
comment
Мне удалось использовать LockMock.Upgrade в моем выборе, но это относится только к таблице базовых классов, а не к таблице подклассов. Поэтому, когда NHibernate, наконец, пытается обновить подкласс, все еще возникает тупиковая ситуация. Вот мой пост на nhusers с подробным описанием моего открытия: groups.google.com/d/topic /nhusers/6857P97StD8/обсуждение   -  person Trinition    schedule 07.01.2013


Ответы (2)


Вы включили флаги трассировки взаимоблокировок SQL Server 1204 или 1222? Это поможет точно определить, какие ресурсы вызывают взаимоблокировку. Дополнительные сведения см. в статье MSDN по Обнаружение и устранение взаимоблокировок.

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

person Paul Williams    schedule 02.09.2010
comment
Я создал график взаимоблокировки с помощью профилировщика SQL Management Studio и добавил его графику к вопросу. В обеих таблицах осуществляется доступ только к одной строке, и мне кажется, что оператор select блокирует две таблицы в порядке, противоположном операторам UPDATE. Доступ осуществляется только к одной строке в каждой таблице. Я не знаю, как индекс поможет изменить это. - person Reboot; 03.09.2010

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

person Danny Thomas    schedule 02.09.2010
comment
Они выполняются в одной транзакции. Если бы это было не так, они бы не вызвали взаимоблокировку с другой транзакцией. Действия обоих потоков выполняются в одной транзакции. Может быть, я должен был сделать это более ясным. - person Reboot; 02.09.2010