Оптимизация выполнения предложений на SQL не является темой настоящего материала. Тем не менее общая логическая схема обработки запросов SQL дает шанс в некоторых случаях подобрать такую формулировку запроса, которая способна позволить разработчику СУБД предложить более выгодный по сравнению с другими формулировками план обработки. Иногда такая выгодная формулировка может сопровождаться некоторым изменением смысла запроса. Программист обязан это понимать и быть уверенным в правомерности замены формулировки в конкретных обстоятельствах приложения.
Реляционная модель избыточна в том смысле, что допускает наличие разных выражений над отношениями, дающих одинаковый результат оптимизацию выполнения предложений SQL напрямую не затрагивает, полагая выбор той или иной формулировки запроса делом удобства программиста и передавая задачу построения оптимального плана обработки исключительно в компетенцию СУБД. Тем не менее в порядке помощи разработчикам в рамках реляционной теории были предложены способы ускорить вычисления ответов на запросы. Этому служит техника равносильных преобразований. Однако подобная техника не в полной мере применима к SQL ввиду отличий модели данных, подразумеваемой этим языком, от модели отношений (реляционной).
SQL предполагает возможное влияние формулировки запроса на эффективность вычислений. Кроме этого, он явно вводит специальные структуры в БД для ускорения вычислений: это индексы и овеществленные представления данных (materialized views). Многие советы по формулировкам запросов в SQL ставят своей целью побудить СУБД использовать при доступе к данным индекс.
Oracle при формировании плана обработки поступившего запроса пытается переформулировать запрос в более выгодный для вычислений вид. При этом используются шаблоны равносильных преобразований, наличие вспомогательных структур БД и статистика объектов хранения. Сверх этого Oracle дает такие средства влияния на схему вычисления, как подсказки оптимизатору, особые параметры СУБД и особые конфигурации структур хранения объектов.
Приводимые ниже формулировки запросов в некоторых случаях следуют сразу нескольким рекомендациям. Рекомендации в целом носят качественный характер, в то время как количественная оценка выигрыша есть предмет отдельного и конкретного изучения.
Полное указание имен объектов в запросе пусть незначительно, но сокращает время разбора. Например, есть два запроса:
SELECT emp.ename FROM scott.emp; SELECT ename FROM emp;
Второй имеет предпосылки обрабатываться дольше, так как при разборе запроса требует дополнительной работы по уточнению принадлежности таблицы схеме и столбца таблице. Заметьте к тому же, что, строго говоря, эти предложения не равносильны: при разборе второго может оказаться, что таблица EMP не принадлежит пользователю SCOTT (если запрос выдавался другим пользователем, имеющим одноименную таблицу, или если переменная сеанса CURRENT_SCHEMA имела значение, отличное от SCOTT).
В некоторых случаях формулировка запроса позволяет отказаться от повторного вычисления выражений. Примерами могут служить логические выражения BETWEEN и IN. Например, формулировка условного выражения
( sal + comm ) BETWEEN 1000 AND 2000
способна обрабатываться эффективнее равносильной формулировки
( sal + comm >= 1000 ) AND ( sal + comm <= 2000 )
Ответ на вопрос, будет ли первая формулировка действительно обрабатываться быстрее, зависит от сложности выражения. В данном случае подвыражение ( SAL + COMM ) достаточно просто, чтобы при построении плана, на этапе анализа формулировки, оптимизатор заметил, что во втором случае подвыражение повторяется, и фактически вычислял бы его однократно. Однако если бы вместо этого подвыражения стояла более сложная конструкция или если бы подвыражение содержало бы обращение к функции пользователя (все аспекты вычисления которой оптимизатору неизвестны), формулировку условного выражения через BETWEEN следовало бы признать более выгодной с вычислительной точки зрения. (Другие выгоды от BETWEEN — в надежности кода, из-за однократности записи выражения в запросе и в возможности задействовать в плане вычисления индекс, когда таковой имеется).
Аналогичные рассуждения обосновывают выгоду от использования для построения условных выражений операторов IN или = ANY перед употреблением цепочек сравнения, составленных с помощью OR, и выгоду от ссылки на имя столбца, данное во фразе SELECT, перед воспроизведением выражения в другой фразе. Так, возвращаясь к одному из примеров выше, предпочтение следует отдать формулировке
SELECT job, AVG ( sal ) avgsal FROM emp GROUP BY job ORDER BY avgsal ;
перед формулировкой
SELECT job, AVG ( sal ) FROM emp GROUP BY job ORDER BY AVG ( sal ) ;
Опять-таки, что касается вычислений, то для этого простого случая обе формулировки скорее всего приведут к одному общему сценарию обработки (это не совсем просто проверить), но когда вместо AVG ( SAL ) будет стоять более сложное выражение или оно будет содержать обращение к функции пользователя, бесспорно предпочтительней окажется формулировка первого типа.
Логические выражения, составленные цепочками с помощью связок OR или AND, при отсутствии у Oracle информации о сложности вычисления подвыражений вычисляются во вполне определенном направлении. Это обстоятельство можно использовать для размещения элементов цепочек, наиболее вероятно дающих TRUE или же FALSE в соответствующих концах цепочки.
При просмотре сотрудников выражение
( sal > 1000 ) OR ( mgr IS NULL )
будет вычисляться скорее, чем
( mgr IS NULL ) OR ( sal > 1000 )
Это объясняется тем, что цепочка, построенная с помощью связок OR, вычисляется слева направо, а сотрудников с зарплатой более 1000 — больше, чем "президентов". Мизерная в данном случае разница может оказаться заметной на вычислительно более сложных выражениях.
Перестановка фильтров строк способна сократить объем обработки. Так, предложение SELECT с фразой HAVING иногда можно переформулировать в содержательно равносильное, но вычислительно более эффективное за счет перенесения отбора строк из фразы HAVING во фразу WHERE. Вот пример запроса на количество разных сотрудников по специальностям, помимо клерков:
SELECT job, COUNT ( * ) FROM emp GROUP BY job HAVING job <> 'CLERK' ;
Из сказанного ранее следует, что логическая последовательность действий по вычислению результата на этот запрос будет следующей:
WHERE;GROUP BY;HAVING.Памятуя логический порядок вычислений, объем вычислений затратной группировки можно сократить, переписав запрос в виде
SELECT job, COUNT ( * ) FROM emp WHERE job <> 'CLERK' GROUP BY job ;
Некоторые специалисты вовсе дают рекомендацию избегать отсева групп фразой HAVING.
Подобный перенос фильтра на более раннюю фазу обработки иногда возможен и в иных случаях, например, в операциях соединения. Сравните:
SELECT e.ename, d.dname FROM emp e INNER JOIN dept d USING ( deptno ) WHERE e.job <> 'SALESMAN' ; SELECT e.ename, d.dname FROM ( SELECT * FROM emp WHERE job <> 'SALESMAN' ) e INNER JOIN dept d USING ( deptno ) ;
В то же время не следует недооценивать оптимизатор Oracle: для двух последних запросов (пусть и не самых сложных) он дает одинаковый
Хотя доступ к строкам таблицы по индексу не гарантирует высокую скорость (а иногда приводит даже к замедлению), часто он оказывается оправдан. Но решению СУБД употребить существующий индекс может, помимо прочего, препятствовать формулировка условного выражения. Например, в случае указания поля, соответствующего индексированному обычным индексом (древовидным и без функционального преобразования ключа) столбцу, в качестве параметра для функции СУБД откажется от использования индекса:
SELECT empno FROM emp WHERE TRUNC ( empno ) = 7369;
СУБД может воспользоваться индексом (если сочтет целесообразным), только если индексированное поле присутствует в одной из частей сравнения без каких-либо преобразований:
SELECT empno FROM emp WHERE empno >= 7369;
но не в этом случае:
SELECT empno FROM emp WHERE empno + 0 >= 7369;
Oracle не будет также отказываться от использования индекса в сравнениях с помощью других операторов:
empno BETWEEN 7000 AND 8000 empno IN ( 7369, 7865, 8888 ) empno = ANY ( 7369, 7865, 8888 ) empno = ALL ( 7369, 7865, 8888 )
В сравнении оператором LIKE СУБД сможет привлекать индекс только если в проверочной маске первый слева символ не является специальным:
job LIKE 'SAL%'
но не:
job LIKE '_SAL%'
Транзакции и блокировки есть механизм регулирования доступа к БД из приложений.
Транзакция в SQL есть логическая последовательность операций DML по внесению изменений в БД, принимаемая или же отвергаемая СУБД в конечном итоге ("по завершению транзакции") в целом. В английском языке слово transaction обозначает единицу общения двух агентов (в нашем случае — программы и СУБД), завершающуюся оказанием взаимного воздействия друг на друга.
Понятие транзакции в реляционной теории отсутствует и составляет самостоятельный по отношению к ней предмет изучения. В случае баз данных транзакции позволяют:
Широко известны общие требования к механизму транзакций ("свойства ACID"):
Некоторые эксперты полагают, что требование согласованности для транзакций в БД, то есть соблюдение ограничений целостности, должно обеспечиваться на уровне не транзакции (как то допускают и стандарт SQL, и Oracle), а отдельного оператора DML. Получается, что выполнение этого требования механизмом тразнакций свидетельствует о недостаточности языка SQL для моделирования событий в предметной области. К сожалению это не единственная возможная претензия к языку.
Стандарт SQL предлагает определенный перечень средств для управления транзакциями. Oracle не поддерживает их в полном объеме и в полной мере, однако средства для управления транзакциями в Oracle обладают свойствами ACID и достаточны для нужд большинства приложений.
Для работы с транзакциями Oracle поддерживает следующие операторы SQL:
COMMIT [ WORK ] ROLLBACK [ WORK ] [ TO SAVEPOINT имя_точки_сохранения ] SAVEPOINT имя_точки_сохранения SET TRANSACTION тип_транзакции
Слово WORK в COMMIT и ROLLBACK носит косметический характер и употребляется по желанию.
В Oracle отсутствует команда для создания новой транзакции (в отличие от стандарта SQL), но есть две команды завершения: фиксацией результатов выполнявшихся в последней транзакции команд DML (COMMIT) и отказом от них (ROLLBACK). Соединение с СУБД автоматически приводит к началу новой транзакции, и то же случается по завершению отработки любой команды COMMIT или ROLLBACK. Таким образом, все операции с данными (таблиц, индексов, внутренних объектов LOB) волей-неволей всегда выполняются в Oracle в рамках какой-нибудь транзакции, а сеанс связи программы с СУБД выглядит последовательностью сменяющих друг друга транзакций. Команды завершения транзакции затрагивают только операции DML, но в некоторых случаях СУБД порождает такие команды самостоятельно. Так, всякая команда DDL завершается неявной (скрытой) выдачей COMMIT; аварийный разрыв сеанса или возникновение исключительной ситуации на уровне программы сопровождается неявной выдачей ROLLBACK.
Пример:
CONNECT scott/tiger -- открыта новая транзакция INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'BUSH' ); UPDATE emp SET ename = 'LADEN' WHERE empno = 1111; ROLLBACK; -- старая транзакция завершена отменой UPDATE и INSERT; открыта новая транзакция
Иногда приводят два методических правила по употреблению COMMIT:
COMMIT, как только представится возможным, иCOMMIT раньше необходимого.Операция COMMIT затратна для СУБД и при особо частой выдаче может заметно тормозить работу СУБД. В версии 10 введена возможность ускоренного выполнения COMMIT. Для этого в команде можно указать ключевые слова BATCH ("групповая фиксация": запись о выдаче COMMIT заносится в буфер журнала в СУБД, но не провоцирует перенос журнальных записей в файл) или (СУБД начинает обрабатывать следующую команду сеанса, не дожидаясь фактического завершения отработки COMMIT):
COMMIT WRITE [BATCH | IMMEDIATE] [WAIT | NOWAIT]
Пример такого поведения можно наблюдать, прогнав в SQL*Plus с помощью сценарного файла следующий текст:
CONNECT / AS SYSDBA SELECT dname, ora_rowscn FROM scott.dscn; UPDATE scott.dscn SET dname = dname WHERE ROWNUM <= 2; COMMIT; STARTUP FORCE SELECT dname, ora_rowscn FROM scott.dscn; UPDATE scott.dscn SET dname = dname WHERE ROWNUM <= 2; COMMIT WRITE BATCH NOWAIT; STARTUP FORCE SELECT dname, ora_rowscn FROM scott.dscn;
Здесь разрыв транзакции достигается форсированной перезагрузкой СУБД: STARTUP FORCE.
Любое из указаний BATCH или в команде COMMIT WRITE отменяет гарантию со стороны СУБД попадания последних изменений в БД в случае сбоя, невзирая на выдачу программой пользователя COMMIT.
Умолчательный способ отработки COMMIT при наличии указанных вариантов можно установить для всей СУБД (ALTER SYSTEM …) и для отдельных сеансов (ALTER SESSION …) параметрами СУБД:
COMMIT_WRITE[10] COMMIT_LOGGING[11-) COMMIT_WAIT[11-)
[10] В версии 10.
[11-)] С версии 11.
Примеры:
ALTER SESSION SET COMMIT_WRITE = 'batch, nowait';
С версии 11:
ALTER SESSION SET COMMIT_LOGGING = 'batch'; ALTER SESSION SET COMMIT_WAIT = 'force_wait';
Команда SAVEPOINT позволяет поставить в последовательности команд DML поименованную "точку сохранения", к которой можно вернуться в рамках текущей транзакции с тем, чтобы дать ей с этого места новое продолжение:
CONNECT scott/tiger -- новая транзакция ... INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'BUSH' ); UPDATE emp SET job = 'PRESIDENT' WHERE ename = 'BUSH'; SAVEPOINT try_new_employee; -- поставили точку сохранения TRY_NEW_EMPLOYEE UPDATE emp SET ename = 'LADEN' WHERE ename = 'BUSH'; DELETE FROM emp WHERE ename = 'LADEN'; SELECT ename FROM emp WHERE empno = 1111; -- сотрудник 'LADEN' ROLLBACK TO SAVEPOINT try_new_employee; -- вернулись к точке сохранения, отказавшись от последних DELELE и UPDATE SELECT ename FROM emp WHERE empno = 1111; -- сотрудник 'BUSH' UPDATE emp SET job = 'CLERK' WHERE ename = 'BUSH'; ALTER TABLE emp ADD UNIQUE ( job ); -- хотя ошибка, но операции DDL, а потому неявная выдача COMMIT и новая транзакция ... ROLLBACK; -- новая транзакция, и далее без ошибок SELECT ename FROM emp WHERE empno = 1111; DELETE FROM emp WHERE ename = 'BUSH'; COMMIT;
Точки сохранения могут иметься во множестве, и не возбраняется использовать одно и то же имя несколько раз. Однако если имена в пределах транзакции совпали, то вернуться можно будет только к той, что выдана последней. Это следует учитывать при выборе имени очередной точки сохранения.
В некоторых типах СУБД нет точек сохранения, зато есть более развитый аппарат
Команда SET TRANSACTION позволяет в начале транзакции (точнее, до выдачи первого изменяющего данные оператора DML) назначить тип транзакции.
Транзакция любого типа в Oracle не сможет увидеть изменения незавершенных других транзакций (нет так называемых "грязных" транзакций).
Тип транзакции READ WRITE умолчательный и не требует явного указания командой SET TRANSACTION. Транзакция этого типа позволяет программе выдавать команды DML изменения данных и наблюдать их результат, как будто бы он непосредственно совершается в БД.
Задание типа READ ONLY дает начало "читающей" транзакции, в течение которой программа изолируется от изменений в БД (выполняемых другими транзакциями) и видит состояние БД на момент выдачи SET TRANSACTION READ ONLY.
"Читающие" транзакции полезны при составлении отчетов, когда программе нужны согласованные данные из нескольких таблиц базы. Однако попытка выполнить в них INSERT, UPDATE или DELETE приведет к ошибке.
Тип транзакции аналогичен READ ONLY, но не запрещает выполнять собственные операции INSERT, UPDATE или DELETE. Последнее дает программисту свободу действия по сравнению с READ ONLY, однако чревато риском для транзакции оказаться заблокированной.
Пример последовательности выдачи команд в SQL*Plus:
CONNECT scott/tiger -- новая транзакция, по умолчанию — READ WRITE ... INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'BUSH' ); HOST sqlplus scott/tiger SELECT ename FROM emp WHERE empno = 1111; -- сотрудник не виден EXIT COMMIT; -- новая транзакция ... SELECT ename FROM emp WHERE empno = 1111; -- сотрудник 1111 находится в БД и виден SET TRANSACTION READ ONLY; -- установили тип READ ONLY ... DELETE FROM emp WHERE empno = 1111; -- ошибка: транзакция READ ONLY ! HOST sqlplus scott/tiger DELETE FROM emp WHERE empno = 1111; COMMIT; EXIT SELECT ename FROM emp WHERE empno = 1111; -- сотрудник все еще виден ROLLBACK; SELECT ename FROM emp WHERE empno = 1111; -- ... а по завершению транзакции уже нет SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- установили тип ISOLATION LEVEL SERIALIZABLE ... INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'OBAMA' ); HOST sqlplus scott/tiger INSERT INTO emp ( empno, ename ) VALUES ( 2222, 'LADEN' ); COMMIT; EXIT SELECT ename FROM emp WHERE empno IN ( 1111, 2222 ); -- виден только 'OBAMA' ROLLBACK; SELECT ename FROM emp WHERE empno IN ( 1111, 2222 ); -- виден только 'LADEN' DELETE FROM emp WHERE empno = 2222; COMMIT; -- "почистили" данные
Для удобства можно поменять умолчательный тип дальнейших транзакций в пределах текущего сеанса на желаемый, например:
ALTER SESSION SET ISOLATION LEVEL SERIALIZABLE;
Oracle не запрещает разным транзакциям одновременно править разные строки одной и той же таблицы. Однако попытка транзакции изменять строку, уже изменяемую другой незавершенной транзакцией, приведет к блокировке претендующей транзакции. Ниже на двух экранах показана работа двух транзакций, сначала с разными строками EMP, а затем с общей строкой (текущее время отображается подсказкой для ввода строки в SQL*Plus):
Обратите внимание на "долгое" выполнение последнего оператора UPDATE на нижнем экране (42,51 секунды). В данном случае оно означает не то, что уменьшение зарплаты Миллеру на 100 единиц происходило так медленно, а то, что транзакция на нижнем экране после обращения к СУБД с заявкой на UPDATE была переведена в состояние ожидания (сеанс "завис"). Ее работу "заблокировала" транзакция на верхнем экране. Как только блокирующая транзакция завершилась, блокированная продолжила работу и на деле выполнила UPDATE. Об этой синхронизованности событий указывает (в подсказке SQL*Plus) окончательно одно и то же текущее время двух сеансов — для одной транзакции после ее последней команды ROLLBACK, а для другой — после ее последней команды UPDATE.
Упражнение. Проверьте возможность правки разными транзакциями (сеансами) одной строки таблицы, но разных полей.
Легко убедиться, что приведенная схема блокировок не препятствует появлению взаимных блокировок двух разных транзакций (deadlocks). Хорошая новость в том, что Oracle автоматически распознает появление взаимных блокировок и отменяет действие операции, виновной в этом. Одна из транзакций сможет продолжать работу и по своему окончанию освободит вторую.
Упражнение. Спровоцируйте взаимную блокировку двух транзакций и проверьте реакцию на это Oracle.
Показанное на примере блокирование действий транзакции, изменяющей данные, препятствует разрушению целостности данных в случае попыток одновременной правки со стороны разных программ. Технически этот механизм защиты данных строится на основе использования "замков". Строго говоря, Oracle не употребляет замки индивидуально для каждой строки, но для понимания логики происходящего можно пойти на такое допущение.
Замки (locks, иначе — "блокировки") в Oracle являются средством предотвращения нежелательного одновременного доступа к данным и внутренним структурам СУБД путем либо выстраивания процессов СУБД в очередь, либо прерывания операции с возвращением в программу ошибки доступа.
Замки имеют тип (type) и режим наложения (mode).
Oracle использует несколько десятков различных типов замков, однако для регулирования изменений данных в таблицах первоочередную важность имеют замки всего двух типов, необходимость наличия которых на объекте доступа и обусловливает возможность выполнения изменяющей операции DML:
Режимов наложения замка на объект шесть:
| Режим блокировки | Код — краткое название | (Иногда) иное название | Старое название |
|---|---|---|---|
ROW SHARE | 2 — RS | SUB SHARED (SS) | SHARE UPDATE |
Режимы с характеристикой EXCLUSIVE предполагают монопольное овладевание объектом, а режимы с характеристикой SHARE — долевое, допускающее одновременное нахождение на объекте нескольких однотипных замков (для режима SRX первенствует поведение EXCLUSIVE). Эти две характеристики ("виды блокировок") существуют в общем подходе к устройству транзакций. Слово ROW в названиях сообщает о действии замка на группу строк, а отсутствие этого слова — о действии на всю таблицу.
Замки типа TX могут налагаться СУБД в режимах RS и RX; типа TM — во всех.
Если транзакция пытается наложить на объект замок в режиме, несовместимом с режимом ранее наложенного на тот же объект замка, она либо (а) будет поставлена в очередь, либо (б) получит от СУБД сообщение об ошибке. Правила совместимости режимов наложения замков:
| Режим претендующей блокировки | ||||||
|---|---|---|---|---|---|---|
| Режим имеющейся блокировки | NL | RS | RX | S | SRX | X |
| NL | OK[1] | OK | OK | OK | OK | OK |
| RS | OK | (OK)[2] | (OK) | (OK) | (OK) | Несовм.[3] |
| RX | OK | (OK) | (OK) | Несовм. | Несовм. | Несовм. |
| S | OK | OK | Несовм. | OK | Несовм. | Несовм. |
| SRX | OK | OK | Несовм. | Несовм. | Несовм. | Несовм. |
| X | OK | Несовм. | Несовм. | Несовм. | Несовм. | Несовм. |
[1] режим нового замка совместим с режимом ранее наложенного, и замок будет применен
[2] режим нового замка совместим с режимом ранее наложенного, если замок устанавливается командой LOCK TABLE, и "условно совместим", если замок устанавливается командами UPDATE, DELETE и SELECT … FOR UPDATE; в последнем случае транзакция встанет в очередь ожидания, если другие транзакции не заблокировали требуемые строки (TX)
[3] режим нового замка несовместим с режимом ранее наложенного и попытка его наложить на объект приведет к ошибке
Замки могут накладываться СУБД автоматически (неявно) и программой пользователя явочным порядком. Все замки, наложенные в течение транзакции как явно, так и неявно, автоматически снимаются по ее завершению. При возврате к точке сохранения (ROLLBACK TO SAVEPOINT …) автоматически снимаются замки, наложенные в течение транзакции с момента выдачи команды создания точки сохранения.
При поступлении из программы любой из команд INSERT, UPDATE или DELETE, СУБД сначала автоматически попытается наложить на объект определенные замки и только в случае успеха приступит к самому изменению данных. По меньшей мере, замков два:
типа TM в режиме ROW EXCLUSIVE;
типа TX в режиме EXCLUSIVE.
Наблюдать блокировки можно запросами SQL к таблицам словаря-справочника, но нагляднее их представляет Oracle Enterprize Manager (OEM — программа для администрирования Oracle). Возвращаясь к примеру выше, после первой выдачи UPDATE в рамках первой транзакции (она выполнялась сеансом 128) программа OEM показала следующие замки:
После попытки выполнить UPDATE для той же строки другой транзакцией (сеанс 139) эта транзакция "подвисла", а программа OEM показала следующие замки:
Заметьте, что замки типа TM в режиме наложения ROW EXCLUSIVE не помешали друг другу, а попытка второй транзакции наложить замок типа TX в режиме EXCLUSIVE привела к постановке этой транзакции в очередь ожидания. Мешающий этому действию замок будет снят только по концу первой транзакции.
Наличие внешних ключей в схеме обычно приводит к дополнительным замкам на объектах БД и к дополнительным шансам блокирования работы транзакций.
Если таблицы связаны внешним ключом, выполнение INSERT, UPDATE внешнего ключа одной таблицы или ключа (первичного или уникального) другой и DELETE для подчиненной таблицы автоматически добавит третий и, возможно, четвертый замок:
ROW SHARE на партнерскую таблицу;EXCLUSIVE на партнерскую таблицу.В некоторых версиях Oracle в подобном поведении возможны непринципиальные варианты.
При выполнении команды SELECT … FOR UPDATE применительно к родительской таблице СУБД автоматически добавит второй замок:
ROW SHARE на эту таблицу.Вот как OEM показывает замки, возникшие после выдачи UPDATE на изменение значения DEPTNO сотрудника из таблицы EMP:
Обратите внимание на наложение двух "лишних" замков, уже на таблицу DEPT. Пример показывает, что полезные с точки зрения моделирования предметной области внешние ключи приводят к увеличению количества замков на объектах, а значит — повышают вероятность блокирования одними транзакциями других.
Используемая в Oracle техника замков позволяет предотвратить искажение данных изменяющими их транзакциями. Однако с точки зрения конкретной программы она может приводить к неожиданным "подвисаниям", смысл которых не всегда удается осознать конечному пользователю (для него программа просто "перестает работать"). Чтобы не терять контроль над работой программы, разработчик может побеспокоиться заранее и до действий по изменению строк явочным порядком наложить на объект требуемый замок. После этого транзакция сможет свободно изменять необходимые данные вплоть до своего завершения, не опасаясь быть заблокированной.
Предложение LOCK TABLE в Oracle позволяет наложить замок в требуемом режиме на таблицу:
LOCK TABLE имя_таблицы IN режим_блокировки MODE [NOWAIT | WAIT [ n ]]
Само по себе это предложение не полностью решает задачу сохранения контроля над работой программы, так как сама команда LOCK TABLE может столкнуться с несовместимым замком, повешенным ранее другой транзакцией. Для окончательного закрытия проблемы применяется указание .
Указание заменит в случае несовместимости замков ожидание на немедленный возврат в программу сообщения об ошибке. Теперь уже дело программиста обработать такую ошибку (исключительную ситуацию, exception) и довести ее в понятном виде до конечного пользователя.
Указание WAIT соответствует умолчательному поведению, когда при занятости хотя бы одной строки таблицы другими транзакциями выдающая LOCK TABLE будет ждать их завершения. Указание же WAIT n тоже приведет к ожиданию, но не более n секунд. Если за это время мешающий замок будет с таблицы снят, претендующая транзакция повесит на нее свой и продолжит работу. Если же по истечении n секунд таблица не освободится, программа получит сообщение об ошибке. Указание WAIT 0 равносильно .
Вариант WAIT n разрешен с версии 11.
Примеры:
LOCK TABLE dept IN SHARE MODE; LOCK TABLE emp IN EXCLUSIVE MODE NOWAIT;
Блокировку таблицы в режимах SHARE и EXCLUSIVE можно специально запретить или же наоборот, разрешить.
Пример:
ALTER TABLE emp DISABLE TABLE LOCK;
Недостатком блокирования данных таблицы командой LOCK TABLE может оказаться чересчур широкий охват строк, способный порождать по существу ненужные ожидания среди прочих транзакций. Зарезервировать для собственных нужд группы строк, а не все строки таблицы целиком, можно оператором SELECT, завершив его специальной фразой FOR UPDATE.
Предложение
SELECT ... FOR UPDATE [список_имен_столбцов] [NOWAIT | WAIT [ n ]]
не только вернет в программу запрашиваемые данные, но и автоматически наложит два замка, связывающие с действиями в этой транзакции группы строк, участвующих в формировании результата запроса:
ROW SHARE на эту таблицу;EXCLUSIVE.Примеры:
SELECT * FROM emp WHERE deptno = 10 FOR UPDATE;
Здесь программа получает сведения о сотрудниках 10-го отдела и тут же резервирует за собой право свободно их изменять до конца транзакции.
Примечательно, что ограничений на однотабличность предложения SELECT не накладывается. Это позволяет одним предложением зарезервировать для последующих изменений группы строк сразу из нескольких таблиц, что важно, когда данные разных таблиц содержательно связаны:
SELECT empno, sal, comm
FROM emp INNER JOIN dept
USING ( deptno )
WHERE job = 'CLERK'
AND loc = 'NEW YORK'
FOR UPDATE
;
Здесь программа получает сведения о сотрудниках-клерках из Нью-Йорка и тут же блокирует строки об этих сотрудниках в таблице EMP и строкe об отделе этих сотрудников из таблицы DEPT. В подобных случаях снова может возникать проблема чересчур широкого блокирования. Сузить охват блокирования искусственно позволяет уточнение OF …:
SELECT empno, sal, comm
FROM emp INNER JOIN dept
USING ( deptno )
WHERE job = 'CLERK'
AND loc = 'NEW YORK'
FOR UPDATE OF emp.sal
;
Здесь программа получает те же сведения, что и в предыдущем запросе, но резервироваться до конца транзакции будут только строки из таблицы EMP с сотрудниками-клерками из Нью-Йорка. Строки из таблицы DEPT не блокируются.
Казалось бы, в уточнении OF … достаточно сослаться на имена таблиц, строки которых мы хотим зарезервировать, в то время как Oracle требует указывать здесь список столбцов таблиц. Логика такого синтаксического оформления следующая: сообщить после слова OF перечень тех полей строк используемых в запросе таблиц, которые мы намерены править до конца транзакции. Таким образом, фраза FOR UPDATE OF приобретает документирующий характер, способствующий лучшему восприятию текста программистом.
Указания даются и действуют аналогично таким же в команде LOCK TABLE, однако вариант WAIT n стал доступен много раньше, с версии 8.
С версии 8.1 существует разновидность групповой блокировки с обходом уже кем-то блокированных в данный момент строк. Запрос ниже выдаст в программу и одновременно пометит для текущей транзакции замками только те строки о сотрудниках отдела 10, которые свободны в настоящее время от несовместимых замков, принадлежащих другим транзакциям:
SELECT * FROM emp WHERE deptno = 10 FOR UPDATE SKIP LOCKED;
Строки с существующими несовместимыми замками "наша" транзакция попросту проигнорирует, обойдет.
Такая возможность позволяет сократить и упростить программный код. Долгое время она была недокументирована, но с версии 11 стала официальной.
На время выполнения предложений DDL ALTER TABLE, DROP TABLE и LOCK TABLE … IN EXCLUSIVE MODE СУБД пытается "блокировать" таблицу замком типа TM в режиме EXCLUSIVE. Если там уже имеется замок от предшествовавшей операции DML, попытка выполнить операцию DDL может завершиться неудачей.
Упражнение. Проверьте работу блокировок при выполнении операций DDL. Выполните в одном сеансе (т. е. в одной транзакции):
UPDATE emp SET sal = sal WHERE ename = 'SCOTT';
Выполните в другом сеансе (т. е. в другой транзакции):
DROP TABLE emp;
В основе "статических таблиц" словаря-справочника Oracle лежит относительно небольшое число исходных (основных) таблиц, таких как OBJ$, TAB$, COM$, и им подобных. О наличии этих таблиц в схеме SYS документация по Oracle не сообщает. Но на их основе построено большое число выводимых (виртуальных) таблиц, "представлений" данных, доставляющих справочную информацию об объектах в более удобном виде, собственно и предназначенных разработчиками Oracle для обычных потребителей (технически при помощи вдобавок одноименных таблицам публичных синонимов), например, таблицы:
USER(ALL, DBA)_TABLES USER(ALL, DBA)_TAB_COLUMNS USER(ALL, DBA)_INDEXES USER(ALL, DBA)_CONSTRAINTS USER(ALL, DBA)_CONS_COLUMNS USER(ALL, DBA)_IND_COLUMNS USER(ALL, DBA)_SEQUENCES USER(ALL, DBA)_TRIGGERS USER(ALL, DBA)_TRIGGER_COLS ...
Префикс USER обозначает перечисление объектов пользователя, префикс ALL — объектов, доступных для работы из схемы, префикс — всех объектов БД. Таблицы с префиксом обычному пользователю, как правило, не видны. В то же время некоторое количество таблиц-представлений словаря-справочника не имеют префиксов USER, ALL или и именованы по-своему.
Так, таблица DICTIONARY является "справочной по справочнику"; она содержит список названий практически всех (виртуальных) таблиц словаря-справочника (за редкими исключениями) с краткими пояснениями — в том числе название самой себя. Она предназначена для поиска нужной справочной таблицы путем составления запроса на SQL, например:
SELECT table_name FROM dictionary WHERE table_name LIKE 'USER%DICT%';
Для простоты употребления заведено несколько дополнительных публичных (PUBLIC) синонимов:
TABS (синоним для USER_TABLES) — список всех таблиц схемы пользователя;COLS (синоним для USER_TAB_COLUMNS) — список всех столбцов всех таблиц схемы пользователя;SEQ (синоним для USER_SEQUENCES) — список всех генераторов последовательностей чисел в схеме пользователя;SYN (синоним для USER_SYNONYMS) — список всех синонимов схемы пользователя;CAT (синоним для USER_CATALOG) — список основных таблиц и представлений данных, синонимов и генераторов последовательностей чисел в схеме пользователя;OBJ (синоним для USER_OBJECTS) — список всех объектов схемы пользователя;IND (синоним для USER_INDEXES) — список всех индексов схемы пользователя;DICT (синоним для DICTIONARY).Введя в словарь-справочник представления данных, разработчики Oracle достигли сразу две цели:
Когда пользователь работает в графической среде разработки типа SQL Developer, он редко испытывает нужду в прямом обращении к таблицам словаря-справочника: за него это скрытно делает программа. Однако даже в этом случае запросы к справочным таблицам на SQL способны иногда дать ответ, недоступный вовсе или получаемый крайне неудобно с помощью графической среды; например: "в каких таблицах имеется столбец с таким-то именем?"
Включающие языки для Oracle: С/C++, Ada, COBOL, PL/1, FORTRAN, Java (SQLJ), SQL*Plus.
Соответствующие компоненты программного обеспечения Oracle: Pro*C, Pro*Ada, Pro*PL/1, Pro*FORTRAN, SQLJ, ... .
Пример оформления запроса SQL для включающего языка C/C++ (переменные привязки name, salary, d_no):
exec sql select ename, sal
into :name, :salary
from emp
where deptno = :d_no;
Пример для включающего языка Java (переменная привязки cnt):
#sql {
SELECT COUNT ( * )
INTO :cnt
FROM emp
}
Пример из PL/SQL в окружении SQL*Plus (name, salary, d_no — переменные в SQL*Plus):
BEGIN SELECT ename, sal INTO :name, :salary FROM emp WHERE deptno = :d_no ; END;
Порядок обработки исходного текста программы в общем случае:
Оптимизация выполнения предложений на SQL не является темой настоящего материала. Тем не менее общая логическая схема обработки запросов SQL дает шанс в некоторых случаях подобрать такую формулировку запроса, которая способна позволить разработчику СУБД предложить более выгодный по сравнению с другими формулировками план обработки. Иногда такая выгодная формулировка может сопровождаться некоторым изменением смысла запроса. Программист обязан это понимать и быть уверенным в правомерности замены формулировки в конкретных обстоятельствах приложения.
Реляционная модель избыточна в том смысле, что допускает наличие разных выражений над отношениями, дающих одинаковый результат оптимизацию выполнения предложений SQL напрямую не затрагивает, полагая выбор той или иной формулировки запроса делом удобства программиста и передавая задачу построения оптимального плана обработки исключительно в компетенцию СУБД. Тем не менее в порядке помощи разработчикам в рамках реляционной теории были предложены способы ускорить вычисления ответов на запросы. Этому служит техника равносильных преобразований. Однако подобная техника не в полной мере применима к SQL ввиду отличий модели данных, подразумеваемой этим языком, от модели отношений (реляционной).
SQL предполагает возможное влияние формулировки запроса на эффективность вычислений. Кроме этого, он явно вводит специальные структуры в БД для ускорения вычислений: это индексы и овеществленные представления данных (materialized views). Многие советы по формулировкам запросов в SQL ставят своей целью побудить СУБД использовать при доступе к данным индекс.
Oracle при формировании плана обработки поступившего запроса пытается переформулировать запрос в более выгодный для вычислений вид. При этом используются шаблоны равносильных преобразований, наличие вспомогательных структур БД и статистика объектов хранения. Сверх этого Oracle дает такие средства влияния на схему вычисления, как подсказки оптимизатору, особые параметры СУБД и особые конфигурации структур хранения объектов.
Приводимые ниже формулировки запросов в некоторых случаях следуют сразу нескольким рекомендациям. Рекомендации в целом носят качественный характер, в то время как количественная оценка выигрыша есть предмет отдельного и конкретного изучения.
Полное указание имен объектов в запросе пусть незначительно, но сокращает время разбора. Например, есть два запроса:
SELECT emp.ename FROM scott.emp; SELECT ename FROM emp;
Второй имеет предпосылки обрабатываться дольше, так как при разборе запроса требует дополнительной работы по уточнению принадлежности таблицы схеме и столбца таблице. Заметьте к тому же, что, строго говоря, эти предложения не равносильны: при разборе второго может оказаться, что таблица EMP не принадлежит пользователю SCOTT (если запрос выдавался другим пользователем, имеющим одноименную таблицу, или если переменная сеанса CURRENT_SCHEMA имела значение, отличное от SCOTT).
В некоторых случаях формулировка запроса позволяет отказаться от повторного вычисления выражений. Примерами могут служить логические выражения BETWEEN и IN. Например, формулировка условного выражения
( sal + comm ) BETWEEN 1000 AND 2000
способна обрабатываться эффективнее равносильной формулировки
( sal + comm >= 1000 ) AND ( sal + comm <= 2000 )
Ответ на вопрос, будет ли первая формулировка действительно обрабатываться быстрее, зависит от сложности выражения. В данном случае подвыражение ( SAL + COMM ) достаточно просто, чтобы при построении плана, на этапе анализа формулировки, оптимизатор заметил, что во втором случае подвыражение повторяется, и фактически вычислял бы его однократно. Однако если бы вместо этого подвыражения стояла более сложная конструкция или если бы подвыражение содержало бы обращение к функции пользователя (все аспекты вычисления которой оптимизатору неизвестны), формулировку условного выражения через BETWEEN следовало бы признать более выгодной с вычислительной точки зрения. (Другие выгоды от BETWEEN — в надежности кода, из-за однократности записи выражения в запросе и в возможности задействовать в плане вычисления индекс, когда таковой имеется).
Аналогичные рассуждения обосновывают выгоду от использования для построения условных выражений операторов IN или = ANY перед употреблением цепочек сравнения, составленных с помощью OR, и выгоду от ссылки на имя столбца, данное во фразе SELECT, перед воспроизведением выражения в другой фразе. Так, возвращаясь к одному из примеров выше, предпочтение следует отдать формулировке
SELECT job, AVG ( sal ) avgsal FROM emp GROUP BY job ORDER BY avgsal ;
перед формулировкой
SELECT job, AVG ( sal ) FROM emp GROUP BY job ORDER BY AVG ( sal ) ;
Опять-таки, что касается вычислений, то для этого простого случая обе формулировки скорее всего приведут к одному общему сценарию обработки (это не совсем просто проверить), но когда вместо AVG ( SAL ) будет стоять более сложное выражение или оно будет содержать обращение к функции пользователя, бесспорно предпочтительней окажется формулировка первого типа.
Логические выражения, составленные цепочками с помощью связок OR или AND, при отсутствии у Oracle информации о сложности вычисления подвыражений вычисляются во вполне определенном направлении. Это обстоятельство можно использовать для размещения элементов цепочек, наиболее вероятно дающих TRUE или же FALSE в соответствующих концах цепочки.
При просмотре сотрудников выражение
( sal > 1000 ) OR ( mgr IS NULL )
будет вычисляться скорее, чем
( mgr IS NULL ) OR ( sal > 1000 )
Это объясняется тем, что цепочка, построенная с помощью связок OR, вычисляется слева направо, а сотрудников с зарплатой более 1000 — больше, чем "президентов". Мизерная в данном случае разница может оказаться заметной на вычислительно более сложных выражениях.
Перестановка фильтров строк способна сократить объем обработки. Так, предложение SELECT с фразой HAVING иногда можно переформулировать в содержательно равносильное, но вычислительно более эффективное за счет перенесения отбора строк из фразы HAVING во фразу WHERE. Вот пример запроса на количество разных сотрудников по специальностям, помимо клерков:
SELECT job, COUNT ( * ) FROM emp GROUP BY job HAVING job <> 'CLERK' ;
Из сказанного ранее следует, что логическая последовательность действий по вычислению результата на этот запрос будет следующей:
WHERE;GROUP BY;HAVING.Памятуя логический порядок вычислений, объем вычислений затратной группировки можно сократить, переписав запрос в виде
SELECT job, COUNT ( * ) FROM emp WHERE job <> 'CLERK' GROUP BY job ;
Некоторые специалисты вовсе дают рекомендацию избегать отсева групп фразой HAVING.
Подобный перенос фильтра на более раннюю фазу обработки иногда возможен и в иных случаях, например, в операциях соединения. Сравните:
SELECT e.ename, d.dname FROM emp e INNER JOIN dept d USING ( deptno ) WHERE e.job <> 'SALESMAN' ; SELECT e.ename, d.dname FROM ( SELECT * FROM emp WHERE job <> 'SALESMAN' ) e INNER JOIN dept d USING ( deptno ) ;
В то же время не следует недооценивать оптимизатор Oracle: для двух последних запросов (пусть и не самых сложных) он дает одинаковый
Хотя доступ к строкам таблицы по индексу не гарантирует высокую скорость (а иногда приводит даже к замедлению), часто он оказывается оправдан. Но решению СУБД употребить существующий индекс может, помимо прочего, препятствовать формулировка условного выражения. Например, в случае указания поля, соответствующего индексированному обычным индексом (древовидным и без функционального преобразования ключа) столбцу, в качестве параметра для функции СУБД откажется от использования индекса:
SELECT empno FROM emp WHERE TRUNC ( empno ) = 7369;
СУБД может воспользоваться индексом (если сочтет целесообразным), только если индексированное поле присутствует в одной из частей сравнения без каких-либо преобразований:
SELECT empno FROM emp WHERE empno >= 7369;
но не в этом случае:
SELECT empno FROM emp WHERE empno + 0 >= 7369;
Oracle не будет также отказываться от использования индекса в сравнениях с помощью других операторов:
empno BETWEEN 7000 AND 8000 empno IN ( 7369, 7865, 8888 ) empno = ANY ( 7369, 7865, 8888 ) empno = ALL ( 7369, 7865, 8888 )
В сравнении оператором LIKE СУБД сможет привлекать индекс только если в проверочной маске первый слева символ не является специальным:
job LIKE 'SAL%'
но не:
job LIKE '_SAL%'
Транзакции и блокировки есть механизм регулирования доступа к БД из приложений.
Транзакция в SQL есть логическая последовательность операций DML по внесению изменений в БД, принимаемая или же отвергаемая СУБД в конечном итоге ("по завершению транзакции") в целом. В английском языке слово transaction обозначает единицу общения двух агентов (в нашем случае — программы и СУБД), завершающуюся оказанием взаимного воздействия друг на друга.
Понятие транзакции в реляционной теории отсутствует и составляет самостоятельный по отношению к ней предмет изучения. В случае баз данных транзакции позволяют:
Широко известны общие требования к механизму транзакций ("свойства ACID"):
Некоторые эксперты полагают, что требование согласованности для транзакций в БД, то есть соблюдение ограничений целостности, должно обеспечиваться на уровне не транзакции (как то допускают и стандарт SQL, и Oracle), а отдельного оператора DML. Получается, что выполнение этого требования механизмом тразнакций свидетельствует о недостаточности языка SQL для моделирования событий в предметной области. К сожалению это не единственная возможная претензия к языку.
Стандарт SQL предлагает определенный перечень средств для управления транзакциями. Oracle не поддерживает их в полном объеме и в полной мере, однако средства для управления транзакциями в Oracle обладают свойствами ACID и достаточны для нужд большинства приложений.
Для работы с транзакциями Oracle поддерживает следующие операторы SQL:
COMMIT [ WORK ] ROLLBACK [ WORK ] [ TO SAVEPOINT имя_точки_сохранения ] SAVEPOINT имя_точки_сохранения SET TRANSACTION тип_транзакции
Слово WORK в COMMIT и ROLLBACK носит косметический характер и употребляется по желанию.
В Oracle отсутствует команда для создания новой транзакции (в отличие от стандарта SQL), но есть две команды завершения: фиксацией результатов выполнявшихся в последней транзакции команд DML (COMMIT) и отказом от них (ROLLBACK). Соединение с СУБД автоматически приводит к началу новой транзакции, и то же случается по завершению отработки любой команды COMMIT или ROLLBACK. Таким образом, все операции с данными (таблиц, индексов, внутренних объектов LOB) волей-неволей всегда выполняются в Oracle в рамках какой-нибудь транзакции, а сеанс связи программы с СУБД выглядит последовательностью сменяющих друг друга транзакций. Команды завершения транзакции затрагивают только операции DML, но в некоторых случаях СУБД порождает такие команды самостоятельно. Так, всякая команда DDL завершается неявной (скрытой) выдачей COMMIT; аварийный разрыв сеанса или возникновение исключительной ситуации на уровне программы сопровождается неявной выдачей ROLLBACK.
Пример:
CONNECT scott/tiger -- открыта новая транзакция INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'BUSH' ); UPDATE emp SET ename = 'LADEN' WHERE empno = 1111; ROLLBACK; -- старая транзакция завершена отменой UPDATE и INSERT; открыта новая транзакция
Иногда приводят два методических правила по употреблению COMMIT:
COMMIT, как только представится возможным, иCOMMIT раньше необходимого.Операция COMMIT затратна для СУБД и при особо частой выдаче может заметно тормозить работу СУБД. В версии 10 введена возможность ускоренного выполнения COMMIT. Для этого в команде можно указать ключевые слова BATCH ("групповая фиксация": запись о выдаче COMMIT заносится в буфер журнала в СУБД, но не провоцирует перенос журнальных записей в файл) или (СУБД начинает обрабатывать следующую команду сеанса, не дожидаясь фактического завершения отработки COMMIT):
COMMIT WRITE [BATCH | IMMEDIATE] [WAIT | NOWAIT]
Пример такого поведения можно наблюдать, прогнав в SQL*Plus с помощью сценарного файла следующий текст:
CONNECT / AS SYSDBA SELECT dname, ora_rowscn FROM scott.dscn; UPDATE scott.dscn SET dname = dname WHERE ROWNUM <= 2; COMMIT; STARTUP FORCE SELECT dname, ora_rowscn FROM scott.dscn; UPDATE scott.dscn SET dname = dname WHERE ROWNUM <= 2; COMMIT WRITE BATCH NOWAIT; STARTUP FORCE SELECT dname, ora_rowscn FROM scott.dscn;
Здесь разрыв транзакции достигается форсированной перезагрузкой СУБД: STARTUP FORCE.
Любое из указаний BATCH или в команде COMMIT WRITE отменяет гарантию со стороны СУБД попадания последних изменений в БД в случае сбоя, невзирая на выдачу программой пользователя COMMIT.
Умолчательный способ отработки COMMIT при наличии указанных вариантов можно установить для всей СУБД (ALTER SYSTEM …) и для отдельных сеансов (ALTER SESSION …) параметрами СУБД:
COMMIT_WRITE[10] COMMIT_LOGGING[11-) COMMIT_WAIT[11-)
[10] В версии 10.
[11-)] С версии 11.
Примеры:
ALTER SESSION SET COMMIT_WRITE = 'batch, nowait';
С версии 11:
ALTER SESSION SET COMMIT_LOGGING = 'batch'; ALTER SESSION SET COMMIT_WAIT = 'force_wait';
Команда SAVEPOINT позволяет поставить в последовательности команд DML поименованную "точку сохранения", к которой можно вернуться в рамках текущей транзакции с тем, чтобы дать ей с этого места новое продолжение:
CONNECT scott/tiger -- новая транзакция ... INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'BUSH' ); UPDATE emp SET job = 'PRESIDENT' WHERE ename = 'BUSH'; SAVEPOINT try_new_employee; -- поставили точку сохранения TRY_NEW_EMPLOYEE UPDATE emp SET ename = 'LADEN' WHERE ename = 'BUSH'; DELETE FROM emp WHERE ename = 'LADEN'; SELECT ename FROM emp WHERE empno = 1111; -- сотрудник 'LADEN' ROLLBACK TO SAVEPOINT try_new_employee; -- вернулись к точке сохранения, отказавшись от последних DELELE и UPDATE SELECT ename FROM emp WHERE empno = 1111; -- сотрудник 'BUSH' UPDATE emp SET job = 'CLERK' WHERE ename = 'BUSH'; ALTER TABLE emp ADD UNIQUE ( job ); -- хотя ошибка, но операции DDL, а потому неявная выдача COMMIT и новая транзакция ... ROLLBACK; -- новая транзакция, и далее без ошибок SELECT ename FROM emp WHERE empno = 1111; DELETE FROM emp WHERE ename = 'BUSH'; COMMIT;
Точки сохранения могут иметься во множестве, и не возбраняется использовать одно и то же имя несколько раз. Однако если имена в пределах транзакции совпали, то вернуться можно будет только к той, что выдана последней. Это следует учитывать при выборе имени очередной точки сохранения.
В некоторых типах СУБД нет точек сохранения, зато есть более развитый аппарат
Команда SET TRANSACTION позволяет в начале транзакции (точнее, до выдачи первого изменяющего данные оператора DML) назначить тип транзакции.
Транзакция любого типа в Oracle не сможет увидеть изменения незавершенных других транзакций (нет так называемых "грязных" транзакций).
Тип транзакции READ WRITE умолчательный и не требует явного указания командой SET TRANSACTION. Транзакция этого типа позволяет программе выдавать команды DML изменения данных и наблюдать их результат, как будто бы он непосредственно совершается в БД.
Задание типа READ ONLY дает начало "читающей" транзакции, в течение которой программа изолируется от изменений в БД (выполняемых другими транзакциями) и видит состояние БД на момент выдачи SET TRANSACTION READ ONLY.
"Читающие" транзакции полезны при составлении отчетов, когда программе нужны согласованные данные из нескольких таблиц базы. Однако попытка выполнить в них INSERT, UPDATE или DELETE приведет к ошибке.
Тип транзакции аналогичен READ ONLY, но не запрещает выполнять собственные операции INSERT, UPDATE или DELETE. Последнее дает программисту свободу действия по сравнению с READ ONLY, однако чревато риском для транзакции оказаться заблокированной.
Пример последовательности выдачи команд в SQL*Plus:
CONNECT scott/tiger -- новая транзакция, по умолчанию — READ WRITE ... INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'BUSH' ); HOST sqlplus scott/tiger SELECT ename FROM emp WHERE empno = 1111; -- сотрудник не виден EXIT COMMIT; -- новая транзакция ... SELECT ename FROM emp WHERE empno = 1111; -- сотрудник 1111 находится в БД и виден SET TRANSACTION READ ONLY; -- установили тип READ ONLY ... DELETE FROM emp WHERE empno = 1111; -- ошибка: транзакция READ ONLY ! HOST sqlplus scott/tiger DELETE FROM emp WHERE empno = 1111; COMMIT; EXIT SELECT ename FROM emp WHERE empno = 1111; -- сотрудник все еще виден ROLLBACK; SELECT ename FROM emp WHERE empno = 1111; -- ... а по завершению транзакции уже нет SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- установили тип ISOLATION LEVEL SERIALIZABLE ... INSERT INTO emp ( empno, ename ) VALUES ( 1111, 'OBAMA' ); HOST sqlplus scott/tiger INSERT INTO emp ( empno, ename ) VALUES ( 2222, 'LADEN' ); COMMIT; EXIT SELECT ename FROM emp WHERE empno IN ( 1111, 2222 ); -- виден только 'OBAMA' ROLLBACK; SELECT ename FROM emp WHERE empno IN ( 1111, 2222 ); -- виден только 'LADEN' DELETE FROM emp WHERE empno = 2222; COMMIT; -- "почистили" данные
Для удобства можно поменять умолчательный тип дальнейших транзакций в пределах текущего сеанса на желаемый, например:
ALTER SESSION SET ISOLATION LEVEL SERIALIZABLE;
Oracle не запрещает разным транзакциям одновременно править разные строки одной и той же таблицы. Однако попытка транзакции изменять строку, уже изменяемую другой незавершенной транзакцией, приведет к блокировке претендующей транзакции. Ниже на двух экранах показана работа двух транзакций, сначала с разными строками EMP, а затем с общей строкой (текущее время отображается подсказкой для ввода строки в SQL*Plus):
Обратите внимание на "долгое" выполнение последнего оператора UPDATE на нижнем экране (42,51 секунды). В данном случае оно означает не то, что уменьшение зарплаты Миллеру на 100 единиц происходило так медленно, а то, что транзакция на нижнем экране после обращения к СУБД с заявкой на UPDATE была переведена в состояние ожидания (сеанс "завис"). Ее работу "заблокировала" транзакция на верхнем экране. Как только блокирующая транзакция завершилась, блокированная продолжила работу и на деле выполнила UPDATE. Об этой синхронизованности событий указывает (в подсказке SQL*Plus) окончательно одно и то же текущее время двух сеансов — для одной транзакции после ее последней команды ROLLBACK, а для другой — после ее последней команды UPDATE.
Упражнение. Проверьте возможность правки разными транзакциями (сеансами) одной строки таблицы, но разных полей.
Легко убедиться, что приведенная схема блокировок не препятствует появлению взаимных блокировок двух разных транзакций (deadlocks). Хорошая новость в том, что Oracle автоматически распознает появление взаимных блокировок и отменяет действие операции, виновной в этом. Одна из транзакций сможет продолжать работу и по своему окончанию освободит вторую.
Упражнение. Спровоцируйте взаимную блокировку двух транзакций и проверьте реакцию на это Oracle.
Показанное на примере блокирование действий транзакции, изменяющей данные, препятствует разрушению целостности данных в случае попыток одновременной правки со стороны разных программ. Технически этот механизм защиты данных строится на основе использования "замков". Строго говоря, Oracle не употребляет замки индивидуально для каждой строки, но для понимания логики происходящего можно пойти на такое допущение.
Замки (locks, иначе — "блокировки") в Oracle являются средством предотвращения нежелательного одновременного доступа к данным и внутренним структурам СУБД путем либо выстраивания процессов СУБД в очередь, либо прерывания операции с возвращением в программу ошибки доступа.
Замки имеют тип (type) и режим наложения (mode).
Oracle использует несколько десятков различных типов замков, однако для регулирования изменений данных в таблицах первоочередную важность имеют замки всего двух типов, необходимость наличия которых на объекте доступа и обусловливает возможность выполнения изменяющей операции DML:
Режимов наложения замка на объект шесть:
| Режим блокировки | Код — краткое название | (Иногда) иное название | Старое название |
|---|---|---|---|
ROW SHARE | 2 — RS | SUB SHARED (SS) | SHARE UPDATE |
Режимы с характеристикой EXCLUSIVE предполагают монопольное овладевание объектом, а режимы с характеристикой SHARE — долевое, допускающее одновременное нахождение на объекте нескольких однотипных замков (для режима SRX первенствует поведение EXCLUSIVE). Эти две характеристики ("виды блокировок") существуют в общем подходе к устройству транзакций. Слово ROW в названиях сообщает о действии замка на группу строк, а отсутствие этого слова — о действии на всю таблицу.
Замки типа TX могут налагаться СУБД в режимах RS и RX; типа TM — во всех.
Если транзакция пытается наложить на объект замок в режиме, несовместимом с режимом ранее наложенного на тот же объект замка, она либо (а) будет поставлена в очередь, либо (б) получит от СУБД сообщение об ошибке. Правила совместимости режимов наложения замков:
| Режим претендующей блокировки | ||||||
|---|---|---|---|---|---|---|
| Режим имеющейся блокировки | NL | RS | RX | S | SRX | X |
| NL | OK[1] | OK | OK | OK | OK | OK |
| RS | OK | (OK)[2] | (OK) | (OK) | (OK) | Несовм.[3] |
| RX | OK | (OK) | (OK) | Несовм. | Несовм. | Несовм. |
| S | OK | OK | Несовм. | OK | Несовм. | Несовм. |
| SRX | OK | OK | Несовм. | Несовм. | Несовм. | Несовм. |
| X | OK | Несовм. | Несовм. | Несовм. | Несовм. | Несовм. |
[1] режим нового замка совместим с режимом ранее наложенного, и замок будет применен
[2] режим нового замка совместим с режимом ранее наложенного, если замок устанавливается командой LOCK TABLE, и "условно совместим", если замок устанавливается командами UPDATE, DELETE и SELECT … FOR UPDATE; в последнем случае транзакция встанет в очередь ожидания, если другие транзакции не заблокировали требуемые строки (TX)
[3] режим нового замка несовместим с режимом ранее наложенного и попытка его наложить на объект приведет к ошибке
Замки могут накладываться СУБД автоматически (неявно) и программой пользователя явочным порядком. Все замки, наложенные в течение транзакции как явно, так и неявно, автоматически снимаются по ее завершению. При возврате к точке сохранения (ROLLBACK TO SAVEPOINT …) автоматически снимаются замки, наложенные в течение транзакции с момента выдачи команды создания точки сохранения.
При поступлении из программы любой из команд INSERT, UPDATE или DELETE, СУБД сначала автоматически попытается наложить на объект определенные замки и только в случае успеха приступит к самому изменению данных. По меньшей мере, замков два:
типа TM в режиме ROW EXCLUSIVE;
типа TX в режиме EXCLUSIVE.
Наблюдать блокировки можно запросами SQL к таблицам словаря-справочника, но нагляднее их представляет Oracle Enterprize Manager (OEM — программа для администрирования Oracle). Возвращаясь к примеру выше, после первой выдачи UPDATE в рамках первой транзакции (она выполнялась сеансом 128) программа OEM показала следующие замки:
После попытки выполнить UPDATE для той же строки другой транзакцией (сеанс 139) эта транзакция "подвисла", а программа OEM показала следующие замки:
Заметьте, что замки типа TM в режиме наложения ROW EXCLUSIVE не помешали друг другу, а попытка второй транзакции наложить замок типа TX в режиме EXCLUSIVE привела к постановке этой транзакции в очередь ожидания. Мешающий этому действию замок будет снят только по концу первой транзакции.
Наличие внешних ключей в схеме обычно приводит к дополнительным замкам на объектах БД и к дополнительным шансам блокирования работы транзакций.
Если таблицы связаны внешним ключом, выполнение INSERT, UPDATE внешнего ключа одной таблицы или ключа (первичного или уникального) другой и DELETE для подчиненной таблицы автоматически добавит третий и, возможно, четвертый замок:
ROW SHARE на партнерскую таблицу;EXCLUSIVE на партнерскую таблицу.В некоторых версиях Oracle в подобном поведении возможны непринципиальные варианты.
При выполнении команды SELECT … FOR UPDATE применительно к родительской таблице СУБД автоматически добавит второй замок:
ROW SHARE на эту таблицу.Вот как OEM показывает замки, возникшие после выдачи UPDATE на изменение значения DEPTNO сотрудника из таблицы EMP:
Обратите внимание на наложение двух "лишних" замков, уже на таблицу DEPT. Пример показывает, что полезные с точки зрения моделирования предметной области внешние ключи приводят к увеличению количества замков на объектах, а значит — повышают вероятность блокирования одними транзакциями других.
Используемая в Oracle техника замков позволяет предотвратить искажение данных изменяющими их транзакциями. Однако с точки зрения конкретной программы она может приводить к неожиданным "подвисаниям", смысл которых не всегда удается осознать конечному пользователю (для него программа просто "перестает работать"). Чтобы не терять контроль над работой программы, разработчик может побеспокоиться заранее и до действий по изменению строк явочным порядком наложить на объект требуемый замок. После этого транзакция сможет свободно изменять необходимые данные вплоть до своего завершения, не опасаясь быть заблокированной.
Предложение LOCK TABLE в Oracle позволяет наложить замок в требуемом режиме на таблицу:
LOCK TABLE имя_таблицы IN режим_блокировки MODE [NOWAIT | WAIT [ n ]]
Само по себе это предложение не полностью решает задачу сохранения контроля над работой программы, так как сама команда LOCK TABLE может столкнуться с несовместимым замком, повешенным ранее другой транзакцией. Для окончательного закрытия проблемы применяется указание .
Указание заменит в случае несовместимости замков ожидание на немедленный возврат в программу сообщения об ошибке. Теперь уже дело программиста обработать такую ошибку (исключительную ситуацию, exception) и довести ее в понятном виде до конечного пользователя.
Указание WAIT соответствует умолчательному поведению, когда при занятости хотя бы одной строки таблицы другими транзакциями выдающая LOCK TABLE будет ждать их завершения. Указание же WAIT n тоже приведет к ожиданию, но не более n секунд. Если за это время мешающий замок будет с таблицы снят, претендующая транзакция повесит на нее свой и продолжит работу. Если же по истечении n секунд таблица не освободится, программа получит сообщение об ошибке. Указание WAIT 0 равносильно .
Вариант WAIT n разрешен с версии 11.
Примеры:
LOCK TABLE dept IN SHARE MODE; LOCK TABLE emp IN EXCLUSIVE MODE NOWAIT;
Блокировку таблицы в режимах SHARE и EXCLUSIVE можно специально запретить или же наоборот, разрешить.
Пример:
ALTER TABLE emp DISABLE TABLE LOCK;
Недостатком блокирования данных таблицы командой LOCK TABLE может оказаться чересчур широкий охват строк, способный порождать по существу ненужные ожидания среди прочих транзакций. Зарезервировать для собственных нужд группы строк, а не все строки таблицы целиком, можно оператором SELECT, завершив его специальной фразой FOR UPDATE.
Предложение
SELECT ... FOR UPDATE [список_имен_столбцов] [NOWAIT | WAIT [ n ]]
не только вернет в программу запрашиваемые данные, но и автоматически наложит два замка, связывающие с действиями в этой транзакции группы строк, участвующих в формировании результата запроса:
ROW SHARE на эту таблицу;EXCLUSIVE.Примеры:
SELECT * FROM emp WHERE deptno = 10 FOR UPDATE;
Здесь программа получает сведения о сотрудниках 10-го отдела и тут же резервирует за собой право свободно их изменять до конца транзакции.
Примечательно, что ограничений на однотабличность предложения SELECT не накладывается. Это позволяет одним предложением зарезервировать для последующих изменений группы строк сразу из нескольких таблиц, что важно, когда данные разных таблиц содержательно связаны:
SELECT empno, sal, comm
FROM emp INNER JOIN dept
USING ( deptno )
WHERE job = 'CLERK'
AND loc = 'NEW YORK'
FOR UPDATE
;
Здесь программа получает сведения о сотрудниках-клерках из Нью-Йорка и тут же блокирует строки об этих сотрудниках в таблице EMP и строкe об отделе этих сотрудников из таблицы DEPT. В подобных случаях снова может возникать проблема чересчур широкого блокирования. Сузить охват блокирования искусственно позволяет уточнение OF …:
SELECT empno, sal, comm
FROM emp INNER JOIN dept
USING ( deptno )
WHERE job = 'CLERK'
AND loc = 'NEW YORK'
FOR UPDATE OF emp.sal
;
Здесь программа получает те же сведения, что и в предыдущем запросе, но резервироваться до конца транзакции будут только строки из таблицы EMP с сотрудниками-клерками из Нью-Йорка. Строки из таблицы DEPT не блокируются.
Казалось бы, в уточнении OF … достаточно сослаться на имена таблиц, строки которых мы хотим зарезервировать, в то время как Oracle требует указывать здесь список столбцов таблиц. Логика такого синтаксического оформления следующая: сообщить после слова OF перечень тех полей строк используемых в запросе таблиц, которые мы намерены править до конца транзакции. Таким образом, фраза FOR UPDATE OF приобретает документирующий характер, способствующий лучшему восприятию текста программистом.
Указания даются и действуют аналогично таким же в команде LOCK TABLE, однако вариант WAIT n стал доступен много раньше, с версии 8.
С версии 8.1 существует разновидность групповой блокировки с обходом уже кем-то блокированных в данный момент строк. Запрос ниже выдаст в программу и одновременно пометит для текущей транзакции замками только те строки о сотрудниках отдела 10, которые свободны в настоящее время от несовместимых замков, принадлежащих другим транзакциям:
SELECT * FROM emp WHERE deptno = 10 FOR UPDATE SKIP LOCKED;
Строки с существующими несовместимыми замками "наша" транзакция попросту проигнорирует, обойдет.
Такая возможность позволяет сократить и упростить программный код. Долгое время она была недокументирована, но с версии 11 стала официальной.
На время выполнения предложений DDL ALTER TABLE, DROP TABLE и LOCK TABLE … IN EXCLUSIVE MODE СУБД пытается "блокировать" таблицу замком типа TM в режиме EXCLUSIVE. Если там уже имеется замок от предшествовавшей операции DML, попытка выполнить операцию DDL может завершиться неудачей.
Упражнение. Проверьте работу блокировок при выполнении операций DDL. Выполните в одном сеансе (т. е. в одной транзакции):
UPDATE emp SET sal = sal WHERE ename = 'SCOTT';
Выполните в другом сеансе (т. е. в другой транзакции):
DROP TABLE emp;
В основе "статических таблиц" словаря-справочника Oracle лежит относительно небольшое число исходных (основных) таблиц, таких как OBJ$, TAB$, COM$, и им подобных. О наличии этих таблиц в схеме SYS документация по Oracle не сообщает. Но на их основе построено большое число выводимых (виртуальных) таблиц, "представлений" данных, доставляющих справочную информацию об объектах в более удобном виде, собственно и предназначенных разработчиками Oracle для обычных потребителей (технически при помощи вдобавок одноименных таблицам публичных синонимов), например, таблицы:
USER(ALL, DBA)_TABLES USER(ALL, DBA)_TAB_COLUMNS USER(ALL, DBA)_INDEXES USER(ALL, DBA)_CONSTRAINTS USER(ALL, DBA)_CONS_COLUMNS USER(ALL, DBA)_IND_COLUMNS USER(ALL, DBA)_SEQUENCES USER(ALL, DBA)_TRIGGERS USER(ALL, DBA)_TRIGGER_COLS ...
Префикс USER обозначает перечисление объектов пользователя, префикс ALL — объектов, доступных для работы из схемы, префикс — всех объектов БД. Таблицы с префиксом обычному пользователю, как правило, не видны. В то же время некоторое количество таблиц-представлений словаря-справочника не имеют префиксов USER, ALL или и именованы по-своему.
Так, таблица DICTIONARY является "справочной по справочнику"; она содержит список названий практически всех (виртуальных) таблиц словаря-справочника (за редкими исключениями) с краткими пояснениями — в том числе название самой себя. Она предназначена для поиска нужной справочной таблицы путем составления запроса на SQL, например:
SELECT table_name FROM dictionary WHERE table_name LIKE 'USER%DICT%';
Для простоты употребления заведено несколько дополнительных публичных (PUBLIC) синонимов:
TABS (синоним для USER_TABLES) — список всех таблиц схемы пользователя;COLS (синоним для USER_TAB_COLUMNS) — список всех столбцов всех таблиц схемы пользователя;SEQ (синоним для USER_SEQUENCES) — список всех генераторов последовательностей чисел в схеме пользователя;SYN (синоним для USER_SYNONYMS) — список всех синонимов схемы пользователя;CAT (синоним для USER_CATALOG) — список основных таблиц и представлений данных, синонимов и генераторов последовательностей чисел в схеме пользователя;OBJ (синоним для USER_OBJECTS) — список всех объектов схемы пользователя;IND (синоним для USER_INDEXES) — список всех индексов схемы пользователя;DICT (синоним для DICTIONARY).Введя в словарь-справочник представления данных, разработчики Oracle достигли сразу две цели:
Когда пользователь работает в графической среде разработки типа SQL Developer, он редко испытывает нужду в прямом обращении к таблицам словаря-справочника: за него это скрытно делает программа. Однако даже в этом случае запросы к справочным таблицам на SQL способны иногда дать ответ, недоступный вовсе или получаемый крайне неудобно с помощью графической среды; например: "в каких таблицах имеется столбец с таким-то именем?"
Включающие языки для Oracle: С/C++, Ada, COBOL, PL/1, FORTRAN, Java (SQLJ), SQL*Plus.
Соответствующие компоненты программного обеспечения Oracle: Pro*C, Pro*Ada, Pro*PL/1, Pro*FORTRAN, SQLJ, ... .
Пример оформления запроса SQL для включающего языка C/C++ (переменные привязки name, salary, d_no):
exec sql select ename, sal
into :name, :salary
from emp
where deptno = :d_no;
Пример для включающего языка Java (переменная привязки cnt):
#sql {
SELECT COUNT ( * )
INTO :cnt
FROM emp
}
Пример из PL/SQL в окружении SQL*Plus (name, salary, d_no — переменные в SQL*Plus):
BEGIN SELECT ename, sal INTO :name, :salary FROM emp WHERE deptno = :d_no ; END;
Порядок обработки исходного текста программы в общем случае:
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.