Архив номеров
Форум
Контакты

Проблемы совместного доступа к данным в Oracle

Чем больше количество одновременно работающих с базой данных пользователей, тем больше вероятность конфликта одновременного редактирования одной и той же строки. Что делать серверу, если два пользователя одновременно пытаются обновить одну и ту же запись? Можно принять то изменение, которое пришло позже, но тогда один из пользователей будет видеть у себя некорректные данные. А хуже всего - он будет думать, что все в порядке. Как разрешить подобные проблемы?

Допустим, что два пользователя открыли для редактирования форму с одним и тем же документом. Первый пользователь изменяет важные параметры, цены, количество и нажимает «сохранить».

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

  1. Кто последний, тот и прав. В этом случае вы просто реализуете логику программы и не заботитесь о том, что два пользователя могут одновременно изменять какие-то данные. Прав будет тот, кто чуть позже нажмет кнопку обновления.
  2. Попытаться реализовать «одновременную» работу собственными средствами, с помощью журналов - если в журнале есть запись, что кто-то открыл документ, но не закрыл, то не разрешать повторное открытие другим пользователям. Может быть, где-то это будет удобно и быстро, но в Oracle реализованы хорошие встроенные средства блокировок, которые работают быстрее, эффективнее и надежнее, поэтому данный метод мы не рекомендуем к использованию.
  3. Блокировать записи, которые пользователь собирается изменять, средствами базы данных. Заблокированную запись невозможно изменить, поэтому все запросы на редактирование будут отклоняться. Этот подход является более правильным и именно ему посвящена данная статья.

Заблокированные средствами Oracle записи может изменить только тот пользователь, который установил блокировку. Остальные могут только просматривать данные и не могут выполнять UPDATE или DELETE.

Блокировка

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

Блокировки бывают явными и неявными. Неявные создаются сервером без нашего участия при каждом изменении данных таблицы или структуры и снимаются по завершению выполнения оператора. Такие блокировки существуют только во время выполнения операции модификации данных.

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

Не стоит бояться блокировок, потому что в Oracle они никак не сказываются на производительности системы. Они лишь говорят о том, что какие-то данные взяты определенным пользователем для редактирования.

FOR UPDATE

Когда мы просто используем оператор SELECT для выборки данных, сервер выполняет наш запрос без блокирования каких-либо записей. Но если необходима выборка данных непосредственно для редактирования, то мы должны сообщить серверу о блокировке. Для этого в конец запроса необходимо добавить FOR UPDATE. Например, следующий запрос выбирает все записи из таблицы Users для редактирования:

SELECT *
FROM Users
FOR UPDATE

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

Заблокировать таблицу можно еще с помощью оператора LOCK TABLE, но лучше все же выбирать с помощью запроса SELECT только те данные, которые нужны, и при этом указывать ключевые слова FOR UPDATE.

Чаще всего работа с данными построена по принципу «окно реестра-окно редактирования». Например, у вас есть окно реестра документов, где пользователи могут просматривать счета, накладные и т.д. за определенный период времени. В этом окне происходит только просмотр, поэтому для выборки данных здесь не следует использовать блокирови, иначе это приведет к проблемам при многопользовательской работе. Если один пользователь выберет все документы за месяц, то остальные не смогут открыть данные за тот же период.

Когда пользователь решит отредактировать какой-либо документ, следует открыть отдельное окно, в котором будет выбран именно этот документ и на него будет установлена блокировка. Например:

SELECT *
FROM Docs
WHERE PrimaryKey=10
FOR UPDATE

В этом примере мы выбираем и блокируем запись из таблицы Docs с первичным ключом, равным 10. Блокировка будет поставлена только на одну запись и этот документ больше никто не сможет открыть. Так как в окне реестра документов выполняется запрос SELECT без FOR UPDATE, то он продолжит работать, и остальные пользователи смогут его просматривать и открывать для редактирования другие незаблокированные документы.

Не ждите!

А что произойдет, если пользователь попытается открыть документ, который уже заблокирован другим пользователем? Ответ прост - запрос зависнет в ожидании освобождения ресурсов. Если в вашей программе не предусмотрено возможности прерывания запросов, а блокировка оказалась мертвой, то программа зависнет навечно. Завершить работу можно будет только прерыванием процесса. Самое страшное, если какой-то пользователь открыл окно и ушел на обед. Ресурс оказывается заблокированным надолго, и это мешает работе других пользователей.

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

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

SELECT *
FROM Docs
WHERE PrimaryKey=10
FOR UPDATE NOWAIT

Такой запрос попытается получить данные и установить на них блокировку, но если это невозможно, то ожидания не будет. Сервер просто вернет ошибку с номером ORA-00054:

ORA-00054 Resource busy and acquire with NOWAIT specified

Теперь, когда мы увидели, что данные заблокированы, можно показать пользователю сообщение о том, что кто-то уже редактирует таблицу, и открыть карточку документа, но только в режиме редактирования. Для этого нужно снова выполнить запрос SELECT без попытки блокирования ресурсов.

Пример

Давайте посмотрим, как реализовать возможность открытия карточки редактирования с использованием блокировок на Delphi. Допустим, у нас есть форма TSomeDocument для редактирования и данные выбираются с помощью компонента TOracleDataSet (назовем его odsDocs) из состава DOA (Direct Oracle Access, прямой доступ к Oracle). В компоненте odsDocs прописан запрос на выборку данных без каких-либо блокировок. По событию OnShow для формы пишем код, показанный в листинге 1.

Разберем содержимое представленного листинга. Сначала сохраняем запрос, который прописан в компоненте, а затем добавляем к запросу опции FOR UPDATE NOWAIT. Теперь открываем набор данных внутри блока try...except. Если код отработал нормально, то ресурс свободен и уже заблокирован нами. Нужно только проверить количество записей на 0. А вдруг, пока мы работали с выборкой в реестре документов, этот документ уже кто-то удалил?

Если во время открытия набора данных произошла ошибка из-за блокировки, то выполнение программы переходит на блок except. Здесь возвращаем сохраненный запрос в компонент odsDocs, сообщаем пользователю, что данные невозможно открыть для редактирования, и открываем набор данных, но уже без опции FOR UPDATE NOWAIT.

Это достаточно простой, но эффективный способ блокирования документов.

Блокировки в связанных запросах

Допустим, что у нас есть две таблицы Docs и Users. В таблице Docs есть поле UserID, где сохраняется первичный ключ из таблицы Users. Таким образом, каждый документ привязан к определенному пользователю, например, создавшему, ответственному или кому-то еще. Посмотрим, как будет выглядеть запрос на выборку данных для редактирования:

SELECT *
FROM Docs d, Users u
WHERE d.PrimaryKey=10
AND d.UserID =u.PrimaryKey
FOR UPDATE

В результате блокировка будет установлена не только на выбранный документ под номером 10, но и на запись в таблице Users, которая связана с данным документом. Это очень плохо. Теперь, если кто-то другой попытается открыть на редактирование другой документ, но тоже связанный с этим пользователем, то сервер не даст этого сделать. Все документы пользователя будут заблокированы, а это неправильно. Блокироваться должен только определенный документ, а таблица пользователей не будет редактироваться (из нее только выбирается запись), и ее сервер не должен трогать.

Как сообщить Oracle, что записи в Users блокировать нельзя? Для этого нужно явно указать таблицу, а лучше - первичный ключ в этой таблице: FOR UPDATE OF имя поля. После ключевого слова OF указывается поле, по которому сервер узнает, какую запись из связанных таблиц нужно заблокировать. Итак, наш запрос должен выглядеть следующим образом:

SELECT *
FROM Docs d, Users u
WHERE d.PrimaryKey=10
AND d.UserID =u.PrimaryKey
FOR UPDATE OF d.PrimaryKey

Вот теперь будет заблокирована только одна запись документа и только из таблицы Docs.

Продолжительность

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

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

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

Система

Теперь поговорим о системных представлениях, с помощью которых вы можете управлять и контролировать блокировки. Все блокировки можно получить с помощью представления v$lock:

SELECT *
FROM v$lock

Результат не очень информативен, потому что содержит какие-то адреса и цифры, да и записей очень много. В поле sid находиться идентификатор сессии, а в поле Type можно увидеть тип блокировки. Когда вызывается SELECT FOR UPDATE, то создается блокировка транзакции, а в поле Type можно увидеть TX. Существуют и другие типы блокировки, например, блокировка сервера, изменение структуры таблиц и т.д. Более подробно об этом можно прочитать в документации по Oracle.
Исходя из вышесказанного, более информативным будет следующий запрос:

SELECT s.username, l.*
FROM v$lock l, v$session s
WHERE l.TYPE = 'TX'
and l.sid=s.sid

Здесь мы связались с представлением v$session, которое возвращает сессии, и теперь в результат попадает имя пользователя, который удерживает блокировку.
Из представления v$session можно получить много полезной информации. Просто выполните следующий запрос, чтобы определиться, какие еще поля можно включить в запрос, показанный выше:

SELECT *
FROM v$session

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

  • Идентификатор документа;
  • Идентификатор пользователя;
  • Дата.

Теперь, после каждого удачного открытия ресурса на редактирование можно занести запись в эту таблицу с указанием документа, пользователя и даты. В программе (в окне просмотра реестра) можно реализовать функцию просмотра журнала по определенному документу. Теперь, если что-то не открывается на редактирование, то с помощью журнала пользователи сами смогут узнать, кто последний заблокировал запись и не дает работать другим.

Журнал позволит избежать вам множества звонков с вопросами, кто и что заблокировал. Если злополучного пользователя нет, то тогда уже будут обращаться к вам, а вы с помощью таблиц v$lock и v$session сможете отыскать блокировки и снять их.

LOCK TABLE

Допустим, что нам нужно произвести несколько действий по изменению данных во всей таблице (или в большинстве ее записей), и все эти изменения невозможно уложить в один единственный запрос UPDATE, да и сама работа с данными отнимет не пять минут. При выполнении одной операции UPDATE блокировать таблицу не имеет смысла, но при серьезных изменениях это просто необходимо. Если после выполнения некоторых действий кто-то заблокирует хотя бы одну запись, дальнейшие ваши действия будут парализованы. Такой трюк может привести к нарушению целостности данных, поэтому перед большим количеством изменений лучше заблокировать всю таблицу.

Для блокировки всей таблицы лучше использовать не SELECT FOR UPDATE, а LOCK TABLE IN EXCLUSIVE MODE. Этот оператор блокирует всю таблицу сразу, а не каждую строку в отдельности.

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

Итого

Блокировки - очень мощное и удобное средство для многопользовательских приложений. Используйте их и вы избавитесь от множества проблем. Главное - следовать правилам:

  1. Старайтесь блокировать минимально необходимое количество записей в таблице.
  2. Не забудьте после закрытия формы сохранить или откатить изменения и закрыть набор данных, чтобы освободить ресурс.

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

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

Листинг 1

procedure TSomeDocument.FormShow(Sender: TObject);
var
oldSql : String;
begin
// сохраняем запрос и добавляем операторы блокировки
oldSql:=odsDocs.SQL.Text;
odsDocs.SQL.Add(' FOR UPDATE NOWAIT');
try
// пытаемся открыть набор данных
odsDocs.open;
odsDocs.ReadOnly:=false;

// проверяем, найден ли документ
if odsDocs.RecordCount=0 then
begin
Showmessage('Документ не найден, пока вы думали, его уже удалили');
Close;
exit;
end;
except
// документ заблокирован, поэтому открываем его только для чтения
odsDocs.SQL.Text:=oldSql;
Showmessage('Документ заблокирован другим пользователем, открываем только для чтения');
odsDocs.open;
odsDocs.ReadOnly:=true;
end;
end;


Михаил Фленов
Профессиональный программист. Автор бестселлеров «Библия Delphi», «Программирование в Delphi глазами хакера», «Программирование на C++ глазами хакера». Некоторые книги переведены на иностранные языки и популярны в США, Канаде, Польше и других странах. Основал компании Heapar Software (www.heapar.com) и CyD Software Labs (www.cydsoft.com).

Обсуждение статьи
Логин:
Пароль:
Регистрации на сервере не требуется. Если у вас есть форумный логин, вы можете использовать его.
Если нету, то вы можете зарегистрироваться на forum.itspecial.ru
Обсуждение этой статьи на forum.itspecial.ru
Для отправки сообщения введите код, указанный на картинке
Заголовок
Сообщение

Guest guest@gameland.ru Отправлено: 13.05.2010 4:42:15
RE: Проблемы совместного доступа к данным в Oracle
Best wish for you! What I Like About You replica watches and still do not need to spend that much money as with the original jaeger lecoultre watch buy replica watches
Guest guest@gameland.ru Отправлено: 11.10.2010 4:38:21
RE: Проблемы совместного доступа к данным в Oracle
Along with painting become time pets, all kinds of high quality oil painting receive a kind welcome in the market. Oil painting reproductions also took the eye of collectors rapidly, even strive with Original painting.
Страницы: << 1 >>

Теги: Oracle, база данных, программирование


Keywords: zPOSTz zCODEz z10105z
Для Авторов: edit Lock delete Lock

Автор: Михаил Фленов
Дата: 11.06.2009 12:04:24©


Другие материалы номера
Java для SMB: Удобство решения определенных задач
Java vs .NET: почему .NET
JavaOne 2007. Репортаж с конференции
Небезопасная безопасная Java
Новое - хорошо забытое старое: уязвимость Java-приложений
Будьте бдительны: Java-мидлеты
Сервер приложений и JavaBeans: современная альтернатива клиент-серверной технологии
Золотая рыбка GlassFish: сервер приложений от Sun с открытым исходным кодом
Искусственный интеллект на страже: применение самоорганизующихся карт для анализа инцидентов ИБ
Биометрическая идентификация в масштабах компании
IT-практикум: Delphi и AutoCAD. Организация связи двух популярных программных комплексов
Корпоративные сетевые хранилища данных
История одной сети
Дженнифер Трелевич: безопасность — одно из неубиваемых достоинств мэйнфреймов
Языки, которые мы потеряли
Серверные системы для SMB – приоритетное направление производителей?
Электронный документооборот для SMB
Рынок информационной безопасности России
Обработка и анализ информационных потоков: системы поддержки принятия решений

В этом разделе
Введение в SOA. Часть 1. Проектирование информационных систем
Delphi и Oracle: разрабатываем утилиту для администрирования ORACLE
Языки, которые мы потеряли
IT-практикум: Delphi и AutoCAD. Организация связи двух популярных программных комплексов
Проблемы совместного доступа к данным в Oracle
Рубиновый кофе
Королевство Zend: Инструменты для профессионального php-разработчика
Отлажено до автоматизма: автоматизация Windows штатными средствами
Каркасы ACE
Программирование в ACE: обмен данными
Программирование в ACE: параллелизм
Разделяй и властвуй: совместная разработка кода
Быстрый и меткий: Fastreport как средство корпоративной отчетности


Хакер | GameLand | Мобильные компьютеры | Купи Камеру | Total Football | All Hockey | Onboarg Magazine | Хулиган | Sync
Total DVD | DVDxpert | Maxi Tuning | (game)land company | GamePost | Свой Бизнес


Rambler's Top100