К сожалению, применение любого механизма блокировки для поддержки параллельного выполнения транзакций приводит к возникновению проблемы, которая называется тупиковой ситуацией. На следующем рисунке изображена типичная тупиковая ситуация. Программа А обновляет таблицу ORDERS и поэтому блокирует часть этой таблицы. Тем временем программа B обновляет таблицу PRODUCTS и блокирует ее часть. Затем программа А пытается обновить таблицу PRODUCTS, а программа B пытается обновить таблицу ORDERS, причем каждая пытается обновить ту часть таблицы, которая заблокирована другой программой.
Рисунок 31 Тупик двух транзакций
Без внешнего вмешательства обе программы будут бесконечно ожидать, пока другая программа завершит транзакцию и разблокирует данные. Но могут возникнуть и более сложные ситуации, когда три, четыре или даже больше программ попадают в “цикл” блокировок и каждая из них ожидает освобождения данных, заблокированных другой программой.
Как правило, для устранения тупиковых ситуаций СУБД периодически (скажем, каждые пять секунд) проверяет блокировки, удерживаемые различными транзакциями. Если СУБД обнаруживает тупик, то произвольно выбирает одну транзакцию в качестве “проигравшей” и отменяет ее. Это освобождает блокировки, удерживаемые проигравшей транзакцией, позволяя “победителю” продолжить работу. Проигравшей программе посылается код ошибки, показывающий, что она проиграла в тупиковой ситуации и её текущая транзакция была отменена.
Такая схема устранения тупиков означает, что любая инструкция SQL может вернуть код ошибки “проигрыша в тупиковой ситуации”, хотя сама по себе инструкция будет правильной. Транзакция, пытающаяся выполнить эту инструкцию, отменяется не по вине последней, а из-за параллельной работы с базой данных многих пользователей. Такая схема может показаться несправедливой, но на практике она лучше двух возможных альтернатив: “зависания” или нарушения целостности данных. Если ошибка “проигрыша в тупиковой ситуации” происходит в интерактивном режиме, пользователь может просто повторно ввести инструкции (одну или несколько). В программном SQL прикладная программа сама должна обрабатывать код подобной ошибки. Обычно в таких случаях программа либо выдает предупреждающее сообщение пользователю, либо пытается выполнить транзакцию повторно.
Вероятность возникновения тупиков можно резко уменьшить, если тщательно планировать обновления базы данных. Все программы, которые в своих транзакциях обновляют несколько таблиц, по возможности должны всегда обновлять таблицы в одном и том же порядке. Это приводит к плавному перемещению блокировок по таблицам и сводит к минимуму возможность возникновения тупиков. Кроме того, для дальнейшего уменьшения количества возникающих тупиков можно использовать средства явной блокировки, описанные ниже.