Базы данных Oracle - статьи

         

Вставка методом прямой загрузки


Эта возможность образования взаимного блокирования встречается в 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)

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

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


Содержание раздела