как копировать и повторять тупики в nhibernate - PullRequest
7 голосов
/ 18 марта 2011

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

1) Есть ли способ повторить эту проблему. то есть: я видел это только в логах.

2) Какой лучший / самый простой способ повторить попытку, если транзакция заблокирована

3) Если я завернул вызов в попытке / поймать. Каким будет тип исключения.

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

Ответы [ 2 ]

14 голосов
/ 18 марта 2011

Нахождение тупиков

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

Retry

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

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

Как избежать тупиков

Сокращение времени блокировки

Если вы используете Sql Server, он очень уязвим для мертвых блокировок из-за своего пессимистического механизма блокировки (в отличие от баз данных Oracle). Более новый уровень изоляции Snapshot чем-то похож на то, что делает Oracle, и может в некоторой степени решить проблему, но я никогда не использовал до сих пор, поэтому я не могу много говорить об этом.

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

Использование идентификатора («авто номера») в качестве первичных ключей, вероятно, является самой известной ошибкой . Это заставляет NH вставлять сущности, когда они помещаются в сеанс, что приводит к блокировке всей таблицы (в SQL Server).

Сложнее исправить проблему с промывкой. NH должен сбрасывать изменения перед выполнением запросов, чтобы обеспечить согласованность. Вы можете обойти это, установив FlushMode в Never, что может вызвать проблемы согласованности, поэтому делайте это только тогда, когда вы точно знаете, что делаете. Лучшее решение - использовать только Get или Load или перейти к свойствам корневого объекта вместо выполнения запросов в середине транзакции.

Делая все это, NH может ожидать любую команду Вставить, Обновить и Удалить в базу данных до конца транзакции. Значительно сокращает время блокировки и, следовательно, снижает риск мертвых блокировок.

Общие правила, как избежать тупика

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

1 голос
/ 18 марта 2011

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

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

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

2) Необходимо повторить весь фрагмент кода, который работает с транзакцией, и повторить инициализацию данных. Это означает, что если вы заполняете наборы данных в транзакции, вы должны очистить их в начале повторяющегося блока кода.

3) Это .Numer = 1205 SqlException. В общем, вы также можете повторить попытку тайм-аута и сетевых ошибок:

        switch (sqlEx.Number)
        {
            case 1205:
            {
                DebugLog("DEADLOCK!");
                canRetry = true;
                break;
            }
            case -2:
            case -2147217871:
            {
                DebugLog("TIMEOUT!");
                canRetry = true;
                break;
            }
            case 11:
            {
                DebugLog("NETWORK ERROR!");
                canRetry = true;
                break;               
            }
            default:
            {
                DebugLog(string.Format("SQL ERROR: {0}", sqlEx.Number));
                break;
            }
        }

По моему опыту, при повторной попытке в тупике лучше отбросить соединение из пула с помощью SqlConnection.ClearPool (соединение), поскольку оно может быть сброшено неправильно в следующий раз.

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