|
Программирование >> Проектирование баз данных
Блокировки не дают пользователям выполнять те или иные операции, и это им не всегда нравится. Конечно, пользователи хотят, чтобы им дали возможность заниматься своим законным делом и не указывали, что нельзя, например, изменять данные о кредитоспособности покупателя, так как для него в текущий момент вводится заказ. Действительно, специалисты отдела кредитного контроля вполне могут полагать, что если для данного покупателя вводится заказ, а его кредит необходимо уменьшить, то их обновление имеет большее право на немедленное выполнение, чем обычно. Главный вопрос здесь - почему экранная форма ввода заказа прежде всего блокировала запись об этом покупателе? Большинство разработчиков (по крайне мере те, кто окончил курсы Oracle) дают такой ответ; если вы выбираете строку и планируете обновить ее (или хотя бы думаете, что можете обновить ее), то строку нужно выбрать с помощью операции FOR UPDATE и установить блокировку на уровне строк. Это гарантирует, что строка останется неизменной на протяжении всей транзакции и что в силу этого спокойно можно использовать выбранные значения как основу для обновления. Если же требуется лишь обновить остаток, то совершенно не нужно явно блокировать запись. Остаток можно обновить с помощью такого предложения: UPDATE customers SET account balance = account balance - :this amount , latest trans id = : this trans id WHERE cust id = :this cust; Конечно, как только программа вьщаст это предложение, Oracle установит блокировку на уровне строк, которая будет действовать до конца транзакции. Но мы, по крайней мере, оттянули установку этой блокировки до самого последнего момеггга (вот что мы имели в виду, формулируя правило блокировать позже ). По умолчанию Oracle Forms блокирует строки, как только пользователь вводит изменение, даже несмотря на то, что это может быть за несколько минут до фиксации этих изменений пользователем. Мы не рекомендуем этот метод, предпочитая устанавливать блокировку в самый последний момент, а затем проверять, чтобы эта запись (строка) за прошедшее время не изменилась. Поздняя блокировка Простейший способ проконтролировать, изменилась ли строка после того, как ее выбрали, - использовать номера ее версий. В каждую таблицу, которую вы хотите заблокировать позже, можно ввести столбец, значение которого по умолчанию при вставке строки равно нулю, а при каждом обновлении увеличивается на единицу. Если назвать этот столбец ROWVERSION и запомнить его значение при первом отображении данных строки, то можно снабдить наше предложение UPDATE дополнительным условием: UPDATE customers SET account balance = account balance - :this amount , latest trans id = :this trans id WHERE cust id = :this cust AND row version = :version; Если это предложение не возвратит обработанную строку, мы будем знать, что она за этот промежуток времени была удалена или обновлена. В любом случае перед продолжением работы мы должны вьщать повторный запрос, а приложение должно обработать его. Методы, при которых блокировка производится поздно, а не рано, и которые вследствие этого создают риск ошибки, обычно называют оптимистической блокировкой. Мы предпочитаем называть эти методы оптимистической неблокировкой, чтобы подчеркнуть их преимушества. Хотя, честно говоря, Oracle все равно в конце концов установит блокировку независимо от того, хотим мы этого или нет. Использование сегментов отката транзакции Стандартная стратегия блокировки Oracle определяется следующей интригующей формулировкой: Чтение не блокирует запись, а запись не блокирует чтение . Это действительно так, но за это приходится дорого платить. В Oracle нормальным явлением являются зафиксированные изменения, внесенные с момента начала вашей транзакции. Конечно, можно воспользоваться командой SQL: SET TRANSACTION READ ONLY; чтобы установить точку согласованности по чтению, остающуюся неизменной до фиксации или отката транзакции. Это существенно, если вы собираетесь работать со структурой данных типа главная-подчиненная, используя вложенные запросы, а не соединение. Почему? Да потому, что в противном случае вы в конечном итоге получите несогласованные результаты, поскольку запрос к главной таблице и запрос к подчиненной таблице будут иметь разные точки согласованности по чтению. Чем дольше длиться транзакция, для которой задано READ ONLY, тем вероятнее, что она столкнется с ошибкой SNAPSHOT ТОО OLD. Это означает, что у Oracle в сегменте отката транзакции больше нет информации, необходимой для восстановления того или иного блока в состояние, в котором он находился в точке согласованности по чтению. В этот момент ваш запрос умирает , и это может случиться с любым долго выполняющимся запросом. Сферу влияния этой проблемы помогает сузить увеличение сегментов отката. Однако при этом гарантировать, что длительный запрос не столкнется с этой проблемой, можно лишь путем блокировки DML-операций для этой таблицы, т.е. путем установки разделяемой блокировки для таблицы до начала выполнения запроса. Такая мера относится к радикальным, и это - еще одна причина того, почему длительные запросы могут лучше поддерживаться копией базы данных, не подверженной непрерывному обновлению. Внешние ключи Ограничения по внешнему ключу оказывают неожиданное влияние на стандартную стратегию блокировки Oracle. Если создать простую таблицу, например, с помощью такого предложения: CREATE TABLE emp assigns ( emp# REFERENCES employees , pro]# REFERENCES projects , PRIMARY KEY (emp#, pro]#) TO вы будете очень удивлены, обнаружив, что при выполнении DML-операций в таблице EMP ASSIGNS транзакция, в которой выдаются эти DML-операции, устанавливает разделяемую блокировку на таблице PROJECTS (но не на таблице EMPLOYEES, как в более поздних версиях). Если таблица PROJECTS изменяется очень редко, это вряд ли будет серьезной проблемой. Если же обе таблицы подвержены изменениям, то разделяемые блокировки могут существенно снизить производительность. Эту аномалию вызывает отсутствие индекса для таблицы EMP ASSIGNS, который в качестве лидирующей части имеет PROJ#. Суть этой проблемы состоит в том, что когда пользователь пытается изменить или удалить первичный ключ в таблице PROJECTS, то эта операция разрешена только в случае, если в таблице EMP ASSIGNS нет ссылок на этот ключ. При помощи индекса (и нетранзакционной внутренней блоьшровки) Oracle может быстро и легко проверить допустимость данной операции. Если же такого индекса нет, то Oracle приходится сканировать таблицу EMP ASSIGNS и искать ссылку (что может потребовать довольно много времени). Oracle делает это с помощью рекурсивного SQL, тогда как индексная операция выполняется посредством внутренних вызовов в базе данных. Этот рекурсивный запрос должен обеспечивать согласованность по чтению и не может видеть незафиксированные изменения, внесенные в подчиненную таблицу. Даже если бы он мог их видеть, имела бы место неоднозначность, поскольку они не зафиксированы. Проектировщики Oracle решили устранить эту проблему, предотвратив ее, ~ разделяемая блокировка означает, что вы не сможете вносить изменения в главную таблицу, пока в подчиненной таблице есть незафиксированные транзакции. Если же у вас есть индекс для внешнего ключа в подчиненной таблице, это ограничение не действует.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |