|
Программирование >> Oracle
Стоит заметить, что и в этом случае тоже можно использовать оператор SELECT FOR UPDATE NOWAIT. Представленный выше оператор UPDATE позволяет избежать потери изменений, но может приводить к блокированию, зависая в ожидании завершения изменения строки другим сеансом. Если все приложения используют оптимистическое блокирование, то применение простых операторов UPDATE вполне допустимо, поскольку строки блокируются на очень короткое время выполнения и фиксации изменений. Однако если некоторые приложения используют пессимистическое блокирование, удерживая блокировки строк достаточно долго, имеет смысл выполнять оператор SELECT FOR UPDATE NOWAIT непосредственно перед оператором UPDATE, чтобы избежать блокирования другим сеансом. Итак, какой же метод лучше? По моему опыту, пессимистическое блокирование очень хорошо работает в Oracle (но вряд ли так же хорошо подходит для других СУБД) и имеет много преимуществ по сравнению с оптимистическим. При использовании пессимистического блокирования пользователь может быть уверен, что изменяемые им на экране данные сейчас ему принадлежат - он получил запись в свое распоряжение, и никто другой не может ее изменять. Можно возразить, что, блокируя строку до изменения, вы лишаете к ней доступа других пользователей и, тем самым, существенно снижаете масштабируемость приложения. Но обновлять строку в каждый момент времени сможет только один пользователь (если мы не хотим потерять изменения). Если сначала заблокировать строку, а затем изменять ее, пользователю будет удобнее работать. Если же пытаться изменить, не заблокировав заранее, пользователь может напрасно потерять время и силы на изменения, чтобы в конечном итоге получить сообщение: Извините, данные изменились, попробуйте еще раз . Чтобы ограничить время блокирования строки перед изменением, можно снимать блокировку в приложении, если пользователь занялся чем-то другим и некоторое время не использует строку, или использовать профили ресурсов (Resource Profiles) в базе данных для отключения простаивающих сеансов. Более того, блокирование строки в Oracle не мешает ее читать, как в других СУБД; блокирование строки не мешает обычной работе с базой данных. Все это исключительно благодаря соответствующей реализации механизмов одновременного доступа и блокирования в Oracle. В других СУБД верно как раз обратное. Если попытаться использовать в них пессимистическое блокирование, ни одно приложение не будет работать. Тот факт, что в этих СУБД блокирование строки не дает возможности выполнять к ней запросы, не позволяет даже рассматривать подобный подход. Поэтому иногда приходится забывать правила, выработанные в процессе работе с одной СУБД, чтобы успешно разрабатывать приложения для другой. Блокирование Блокирование происходит, когда один сеанс удерживает ресурс, запрашиваемый другим сеансом. В результате запрашивающий сеанс будет заблокирован - он повиснет до тех пор, пока удерживающий сеанс не завершит работу с ресурсом. Блокирования практически всегда можно избежать. Если оказывается, что интерактивное приложение заблокировано, проблема, скорее всего, связана с ошибкой, подобной описанному выше потерянному изменению (логика работы приложения ошибочна, что и приводит к блокированию). Заблокированные изменения и удаления В интерактивном приложении, которое запрашивает данные из базы, позволяет пользователю манипулировать ими, а затем возвращает их в базу данных, заблокированные операторы UPDATE или DELETE показывают, что в коде может быть проблема потерянного изменения. Вы пытаетесь изменить с помощью UPDATE строку, которую уже изменяет другой пользователь, другими словами, которая уже кем-то заблокирована. Этого блокирования можно избежать с помощью запроса SELECT FOR UPDATE NOWAIT, позволяющего: проверить, не изменились ли данные с момента их прочтения (для предотвращения потерянного изменения); заблокировать строку (предотвращая ее блокирование другим оператором изменения или удаления). Как уже упоминалось, это можно сделать независимо от принятого подхода - как при пессимистическом, так и при оптимистическом блокировании можно использовать оператор SELECT FOR UPDATE NOWAIT для проверки того, что строка не изменилась. При пессимистическом блокировании этот оператор выполняется в тот момент, когда пользователь выражает намерение изменять данные. При оптимистическом блокировании этот оператор выполняется непосредственно перед изменением данных в базе. Это не только решает проблемы блокирования в приложении, но и обеспечивает целостность данных. Блокирование в базе данных выполняют четыре основных оператора ЯМД: INSERT, UPDATE, DELETE и SELECT FOR UPDATE. Решение проблемы в последнем случае тривиально: добавьте конструкцию NOWAIT, и оператор SELECT FOR UPDATE больше не будет заблокирован. Вместо этого приложение должно сообщать пользователю, что строка уже заблокирована. Интерес представляют остальные три оператора ЯМД. Мы рассмотрим каждый из них и увидим, почему они не должны блокировать друг друга и как это исправить, если блокирование все-таки происходит. Заблокированныевставки Единственный случай блокирования операторами INSERT друг друга, - когда имеется таблица с первичным ключом или ограничением уникальности и два сеанса одновременно пытаются вставить строку с одним и тем же значением. Один из сеансов будет заблокирован, пока другой не зафиксирует изменение (в этом случае заблокированный сеанс получит сообщение об ошибке, связанной с дублированием значения) или не откатит его (в этом случае операция заблокированного сеанса будет выполнена успешно). Такое обычно происходит с приложениями, позволяющими генерировать пользователю первичные ключи или значения уникальных столбцов. Этой проблемы проще всего избежать за счет использования при генерации первичных ключей последовательностей Oracle - средства генерации уникальных ключей, обеспечивающего максимальный параллелизм в многопользовательской среде. Если нельзя использовать последовательность, можно применить метод, описанный в приложении А при рассмотрении пакета DBMS LOCK. Там я демонстрирую, как решить эту проблему, прибегнув к явному блокированию вручную. Взаимные блокировки Взаимные блокировки возникают, когда два сеанса удерживают ресурсы, необходимые другому сеансу. Взаимную блокировку легко продемонстрировать на примере базы данных с двумя таблицами, А и В, в каждой из которых по одной строке. Для этого необходимо начать два сеанса (скажем, два сеанса SQL*Plus) и в сеансе А изменить таблицу А. В сеансе Б надо изменить таблицу В. Теперь, если попытаться изменить таблицу А в сеансе Б, он окажется заблокированным, поскольку соответствующая строка уже заблокирована сеансом А. Это еще не взаимная блокировка - сеанс просто заблокирован. Взаимная блокировка еще не возникла, поскольку есть шанс, что сеанс А зафиксирует или откатит транзакцию и после этого сеанс Б продолжит работу. Если мы вернемся в сеанс А и попытаемся изменить таблицу В, то вызовем взаимную блокировку. Один из сеансов будет выбран сервером в качестве жертвы , и в нем произойдет откат оператора. Например, может быть отменена попытка сеанса Б изменить таблицу А с выдачей сообщения об ошибке следующего вида: update a set x = х+1 ERROR at line 1: ORA-00060: deadlock detected while waiting for resource Попытка сеанса А изменить таблицу В по-прежнему блокируется - сервер Oracle не откатывает всю транзакцию. Откатывается только один из операторов, ставших причиной возникновения взаимной блокировки. Сеанс Б по-прежнему удерживает блокировку строки в таблице В, а сеанс А терпеливо ждет, пока эта строка станет доступной. Получив сообщение о взаимной блокировке, сеанс Б должен решить, фиксировать ли уже выполненные изменения в таблице В, откатить ли их или продолжить работу и зафиксировать транзакцию позднее. Как только этот сеанс зафиксирует или откатит транзакцию, другие заблокированные сеансы смогут продолжить работу. Для сервера Oracle взаимная блокировка - настолько редкий, необычный случай, что при каждом ее возникновении создается трассировочный файл. Содержимое трассировочного файла примерно таково: *** 2001-02-23 14:03:35.041 *** SESSION ID:(8.82) 2001-02-23 14:03:35.001 DEADLOCK DETECTED Current SQL statement for this session: update a set x = x+1 The following deadlock is not an ORACLE error. It is a deadlock due to user error in the design of an application or from issuing incorrect ad-hoc SQL. The following.. . Очевидно, сервер Oracle воспринимает взаимные блокировки как ошибки приложения, и в большинстве случаев это справедливо. В отличие от многих других реляционных СУБД, взаимные блокировки настолько редко происходят в Oracle, что вполне можно игнорировать их существование. Обычно взаимную блокировку необходимо создавать искусственно.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |