Вставка методом прямой загрузки
Эта возможность образования взаимного блокирования встречается в Oracle довольно редко в виду специфики режима команд, применяемых при её возникновении. Если два или большее число сеансов пытаются осуществить прямые вставки в таблицы, то они могут создать такую ситуацию, при которой они будут бесконечно ожидать друг друга. Отличие этого сценария взаимного блокирования от сценариев с транзакционной блокировкой заключается в особом режиме применения команды вставки INSERT. При указании подсказки /*+ APPEND */ и использовании подзапроса команда начинает вставлять строки непосредственно в файлы данных, минуя кеш. При этом выполняется монопольная блокировка таблицы, которая иногда и приводит к взаимной блокировке сеансов.
Попробуем смоделировать этот сценарий взаимного блокирования. Для начала создадим две таблицы t8 и t9:
ZH@XE> CREATE TABLE t8 (c1 NUMBER PRIMARY KEY, c2 VARCHAR2(50)); Таблица создана
ZH@XE> CREATE TABLE t9 (c1 NUMBER PRIMARY KEY, c2 VARCHAR2(50)); Таблица создана
Образуем два сеанса и выполним в них методом прямой загрузки вставку строк в эти таблицы, используя имитацию подзапроса.
Первый сеанс:
ZH@XE(24)> ALTER SESSION SET EVENTS '10046 trace name context forever, level 12'; Session altered
ZH@XE(24)> INSERT /*+ APPEND */ INTO t8 SELECT 1, 'Строка1' FROM dual; Вставлено: 1 строка
Второй сеанс:
ZH@XE(23)> INSERT /*+ APPEND */ INTO t9 SELECT 1, 'Строка1' FROM dual; Вставлено: 1 строка
Как было сказано выше, выполнение команд прямой вставки должно приводить к монопольному блокированию таблицы. Следовательно, сейчас у нас должно быть в каждом из сеансов выставлено по одной TM-блокировке в монопольном режиме. Проверим, так ли это на самом деле, обратившись к представлению V$lock:
SYSTEM@XE> SELECT * FROM v$lock WHERE sid IN (24, 23) AND type = 'TM' ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- --- ---- ----- --- ----- ------- ----- ----- 296DAA90 296DAAA8 24 TM 14016 0 6 0 81 0 296DAB54 296DAB6C 23 TM 14018 0 6 0 42 0 Выбрано: 2 строки
Действительно, в представлении мы видим две блокировки TM в монопольном режиме (LMODE = 6 - X). Таблицы t8 и t9 оказались полностью заблокированы для изменений структуры и данных другими сеансами. Теперь выполнение любой команды, изменяющей данные в этих таблицах, должно привести к ожиданию. Попробуем снова осуществить прямую загрузку в первом сеансе, только уже для таблицы t9:
ZH@XE(24)> INSERT /*+ APPEND */ INTO t9 SELECT 2, 'Строка2' FROM dual;
Так и есть, возникло ожидание. Заглянем в трассировочный файл:
WAIT #2: nam='enq: TM - contention' ela= 2999757 name|mode=1414332422 object #=14018 table/partition=0 obj#=14017 tim=11169951762
В первом сеансе постоянно возникает ожидание «Конкуренция TM блокировки». Сеанс ждёт освобождения блокировки. Чтобы убедиться в этом, заглянем в представление v$lock:
SYSTEM@XE(35)> SELECT * FROM v$lock WHERE sid IN (24, 23) AND type = 'TM' ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- --- ---- ----- --- ----- ------- ----- ----- 296DAA90 296DAAA8 24 TM 14016 0 6 0 132 0 296DAB54 296DAB6C 23 TM 14018 0 6 0 93 1 296DAC18 296DAC30 24 TM 14018 0 0 6 15 0 Выбрано: 3 строки
Из содержимого представления видно, что первый сеанс пытается установить TM-блокировку в монопольном режиме (REQUEST = 6) на таблицу t9. Но так как данная таблица была уже заблокирована ранее вторым сеансом, это привело к ожиданию. Если теперь второй сеанс попытается осуществить прямую загрузку в таблицу t8, то возникнет бесконечное ожидание, так как таблица t8 уже заблокирована ожидающим первым сеансом:
ZH@XE(23)> INSERT /*+ APPEND */ INTO t8 SELECT 2, 'Строка2' FROM dual;
Так и есть, возникло взаимное блокирование, и в первом сеансе происходит исключение:
ZH@XE(24)> INSERT /*+ APPEND */ INTO t9 SELECT 2, 'Строка2' FROM dual; INSERT /*+ APPEND */ INTO t9 SELECT 2, 'Строка2' FROM dual; * Ошибка в строке 1: ORA-00060: deadlock detected while waiting for resource
Посмотрим, что нам покажет трассировочный файл взаимной блокировки. В первой секции отменённый оператор вставки первого сеанса:
Current SQL statement for this session: INSERT /*+ APPEND */ INTO t9 SELECT 2, 'Строка2' FROM dual
Граф взаимной блокировки:
Deadlock graph: ---------Blocker(s)-------- ---------Waiter(s)--------- Resource Name process session holds waits process session holds waits TM-000036c0-00000000 26 24 X 27 23 X TM-000036c2-00000000 27 23 X 26 24 X
В качестве ресурсов графа выступают TM-блокировки, выставленные на таблицы t8 и t9. Столбцы holds и waits содержат символьное значение X. Этот символ соответствует монопольному режиму установленной или ожидающей TM-блокировки. В остальном граф читается стандартным образом, поэтому не будем заострять на этом внимание и сразу обратимся к секции ожидающих строк. В отличие от предыдущего сценария с TM-блокировками, эта секция не пуста:
Rows waited on: Session 23: obj - rowid = 000036C2 - AAADbMAAEAAAAJBAAA (dictionary objn - 14018, file - 4, block - 577, slot - 0) Session 24: obj - rowid = 000036C1 - AAADbKAAEAAAAI8AAA (dictionary objn - 14017, file - 4, block - 572, slot - 0)
Впрочем, практического значения содержимое этой секции в большинстве случаев не имеет. Не стоит забывать, что данный вид взаимного блокирования представляет собой блокирование на уровне объектов, а никак не строк.
Как не допустить возникновения взаимных блокировок при прямой загрузке? Во-первых, в виду того, что данный режим вставки накладывает на таблицу самую жесткую из известных блокировок, надо по возможности ограничить его использование. Во-вторых, транзакция, в которой присутствует прямая загрузка, должна быть по возможности короткой, это уменьшит время, в течении которого может возникнуть взаимное блокирование. И наконец, последнее – можно попытаться организовать одинаковую последовательность обработки ресурсов в сеансах, что просто превратит взаимную блокировку в обыкновенную очередь.
Содержание раздела