2009-05-29 3 views
1

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

Недавно мне нужно было добавить код в процедуру, которая обращается к таблице поиска из базы данных. Код поиска уже существовал в другом бизнес-объекте, поэтому я использовал этот бизнес-объект. Например:

Using tr As DbTransaction = myConnection.BeginTransaction() 
    ExecuteCommand1(tr) 
    ExecuteCommand2(tr) 
    If myLookupTable.GetLookupTable().FindById(id).HasFlagSet Then 
     ExecuteCommand3(tr) 
    End If 
End Using 

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

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

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

+0

Выполняет ли command1 или command2 что-то с помощью поисковой системы? –

+0

Нет, они этого не делают. Они даже не читают. Однако они работают с таблицами, которые имеют FK для таблицы поиска.Ничто из этого не интуитивно не предполагает проблему блокировки для меня.

ответ

3

Если вы делаете чтение в середине своей транзакции, вы должны сделать это в контексте транзакции, не используя другую транзакцию и грязные чтения. К счастью, есть простое решение: вместо использования объектов транзакций ADO.Net используйте объект .Net TransactionScope. Код ADO.Net разумен для этого и закроет все ваши операции в этой транзакции, включая чтение вашего другого бизнес-компонента. Просто убедитесь, что ваш бизнес-объект не открывает другое соединение, это приведет к попытке эскалации существующей транзакции в распределенную транзакцию и привлечение к ней нового подключения.

Альтернативой является передача вашей пары SqlConnection/SqlTransaction при каждом вызове, но она распространяется ужасно уродливым везде в вашем коде.

0

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

0

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

0

Очевидно, ваш поиск пытается получить доступ к строкам, которые заблокированы транзакцией tr. Если вы используете транзакцию readuncommitted или, альтернативно, используете WITH (NOLOCK) в своем поисковом запросе, вы увидите все незафиксированные изменения транзакциями, которые могут возникать и выполняться в вашей логике поиска. Поэтому я не уверен, насколько это было бы желательно.

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

Смежные вопросы