Тупик при транзакции с несколькими таблицами

Мой сценарий распространен: у меня есть хранимая процедура, которой нужно обновить несколько таблиц. если одно из обновлений не удалось - нужно откатить все обновления. прямой ответ - включить все обновления в одну транзакцию и просто откатить ее. однако в системе, подобной нашей, это вызовет проблемы параллелизма. когда мы разбиваем обновления на несколько коротких транзакций - мы получаем пропускную способность ~ 30 одновременных выполнений в секунду, прежде чем начнут возникать проблемы с блокировкой. если мы поместим его в одну транзакцию, которая охватывает их все, мы получим параллельную ~ 2 в секунду, прежде чем появится взаимоблокировка.

в нашем случае мы размещаем блок try-catch после каждой короткой транзакции и вручную DELETE/Update обратно изменения из предыдущих. поэтому, по сути, мы имитируем поведение транзакции очень дорогим способом... Он работает нормально, поскольку он хорошо написан и не получает много "откатов"... одна вещь, которую этот подход вообще не может решить, - это случай тайм-аута команды из Интернета. сервер/клиент.

Я много читал во многих формах и блогах, просматривал MSDN и не нашел хорошего решения. многие представили проблему, но я еще не видел хорошего решения.

Вопрос в следующем: существует ли ЛЮБОЕ решение этой проблемы, которое позволит стабильно откатывать обновления для нескольких таблиц без необходимости устанавливать монопольную блокировку для всех строк на протяжении всей длительной транзакции.

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

это может быть через SQL, C# на стороне клиента, C# на стороне сервера (расширить SQL-сервер?). Есть ли такое решение в какой-либо книге/блоге, которое я не нашел?

мы используем SQL Server 2008 R2, к которому подключается клиент/веб-сервер .NET. Пример кода:

Создать процедуру sptest Начать транзакцию Обновить таблицу1 Обновить таблицу2 Подтвердить транзакцию

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

Создать sptest2 Обновить таблицу1 Обновить таблицу2

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

Спасибо, Дж.С.


person user2995891    schedule 19.03.2014    source источник
comment
Попытка реализовать транзакции, поддерживаемые приложением, обречена на провал (раньше я работал в системе, которая пыталась). Что хорошо на уровне базы данных, так это то, что он выдерживает такие вещи, как внезапные перебои в подаче электроэнергии и сбои сервера. Можете ли вы сказать то же самое о своем приложении? Система БД предварительно протестирована, чрезвычайно хороша и, возможно, работает быстрее, чем любая система в целом, которую вы можете внедрить в приложение. Можем ли мы получить более подробную информацию? Почему у вас есть несколько потоков, обновляющих одни и те же строки? Транзакции предназначены для предотвращения ошибок, но в первую очередь лучше избегать конфликтов...   -  person Clockwork-Muse    schedule 21.03.2014


Ответы (2)


Я бы сказал, что вам следует копнуть глубже, чтобы выяснить причину возникновения взаимоблокировки. Возможно, вам следует изменить порядок обновлений, чтобы избежать их. Может быть, какой-то индекс "виноват".

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

person Jānis    schedule 21.03.2014

Для всех внутренних объединенных таблиц, которые в основном являются статическими или с высокой степенью вероятности не влияют на запрос, используя грязные данные, вы можете применить:

INNER JOIN LookupTable (with NOLOCK) lut on lut.ID=SomeOtherTableID

Это сообщит запросу, что меня не волнуют обновления, внесенные в SomeOtherTable.

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

person Ross Bush    schedule 20.03.2014
comment
Спасибо за ответ. На самом деле мы используем with(nolock). Более того, мы пытаемся полностью исключить все выборки из транзакции. Я знаю, что есть решения для управления взаимоблокировками, мы используем некоторые, мой вопрос касается любого программного решения, которое может решить проблему, а не управлять ею. - person user2995891; 20.03.2014
comment
Есть ли у вас какие-либо длительные процессы, запущенные одновременно с вашим обновлением, которые могут устанавливать эксклюзивные блокировки на ваши ресурсы, такие как отчеты, сервисные агенты, пакетные обновления и т. д. и т. д. - person Ross Bush; 20.03.2014