Наложение фрагментов индекса на основе битовых карт
Случай взаимной блокировки с разделяемым режимом может возникать и в тех случаях, когда несколько сеансов пытаются обновить или удалить строки в таблицах с битовыми индексами. Правда, для этого необходимо наличие одного дополнительного условия: изменения в индексируемом столбце должны приводить к наложению строк в этом битовом индексе. Попробуем продемонстрировать это на примере. Для этого нам придётся взять экземпляр редакции Oracle Enterprise Edition, так как в используемой нами до этого редакции Express Edition отсутствует опция битовых индексов. Для начала в первом сеансе создадим таблицу t3 и заполним её данными:
Подключение к: Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production With the Partitioning, OLAP and Data Mining options ZH@XE(146)> CREATE TABLE t3 (c1 NUMBER PRIMARY KEY, c2 VARCHAR2(1)); Таблица создана
ZH@XE(146)> INSERT INTO t3 (c1, c2) VALUES(1, 'А'); Вставлено: 1 строка
ZH@XE(146)> INSERT INTO t3 (c1, c2) VALUES(2, 'Б'); Вставлено: 1 строка
ZH@XE(146)> INSERT INTO t3 (c1, c2) VALUES(3, 'В'); Вставлено: 1 строка
ZH@XE(146)> INSERT INTO t3 (c1, c2) VALUES(4, 'Г'); Вставлено: 1 строка
ZH@XE(146)> INSERT INTO t3 (c1, c2) VALUES(5, 'Д'); Вставлено: 1 строка
ZH@XE(146)> COMMIT; Commit complete
Создадим битовый индекс по столбцу c2:
ZH@ALFA(146)> CREATE BITMAP INDEX t3_bmp_idx ON t3(c2); Индекс создан
Включим трассировку для первого сеанса на уровне ожидания событий:
ZH@XE(146)> ALTER SESSION SET EVENTS '10046 trace name context forever, level 12'; Session altered
Далее попробуем изменить значение столбца «с2» второй строки на «В»:
ZH@ALFA(146)> UPDATE t3 SET c2 = 'В' where c1 = 2; Изменено: 1 строка
Во втором сеансе в это время изменяем значение столбца «с2» четвёртой строки значение «Д»:
ZH@ALFA(144)> UPDATE t3 SET c2 = 'Д' where c1 = 4; Изменено: 1 строка
Если теперь в первом сеансе изменить значение столбца «с2» пятой строки на «Е», то возникнет ожидание:
ZH@ALFA(146)> UPDATE t3 SET c2 = 'Е' where c1 = 5;
Ожидание…
Отчего возникло ожидание? Ведь мы изменяли разные строки таблицы, никак не связанные друг с другом. Может быть, дело здесь вовсе не в таблице, а в битовом индексе? На самом деле, данное ожидание происходит из-за того, что при изменении значения индексируемого столбца таблицы, блокируется не только строка битового индекса, соответствующая текущему значению столбца, но и строка индекса, соответствующая его новому значению. Проще говоря, в нашем случае второй сеанс при обновлении четвёртой строки таблицы заблокировал строки битового индекса, значения которых соответствовали значениям «Г» и «Д». Поэтому первый сеанс, пытающийся обновить столбец c2 пятой строки таблицы с находящимся в нём значением «Д», будет ожидать освобождения необходимой ему строки битового индекса. Проверим это. Для начала заглянем в трассировочный файл первого сеанса:
WAIT #4: nam='enq: TX - row lock contention' ela= 2999504 name|mode=1415053316 usn<<16 | slot=262154 sequence=2992 obj#=59268 tim=954083404
В файле наблюдаем привычное ожидание “конкуренция блокировки строки”. Похожая ситуация отображена и в системном представлении v$lock:
SYSTEM@ALFA> SELECT * FROM v$lock WHERE type = 'TX'; ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- --- ---- ------ ---- ----- ------- ----- ----- 338343A8 338343BC 146 TX 589842 3019 0 4 9 0 32E01ABC 32E01AE0 146 TX 393235 2995 6 0 36 0 32E37268 32E3728C 144 TX 589842 3019 6 0 24 1 Выбрано: 3 строки
Первый сеанс (146) сделал запрос на установку TX-блокировки в разделяемом режиме (REQUEST = 4) и ожидает освобождения строки индекса от блокировки транзакций исключительного режима во втором сеансе (144).
Продолжим изменения и попробуем во втором сеансе изменить значение столбца «с2» третьей строки на «Г»:
ZH@ALFA(144)> UPDATE t3 SET c2 = 'Г' where c1 = 3;
Ожидание …
По идее, мы сейчас пытаемся обновить столбец c2 в строке, где предыдущее его значение было равно «В». Следовательно, в битовом индексе на строку соответствующую этому значению должна быть выставлена блокировка. Но строка уже была заблокирована первым сеансом при обновлении значения столбца c2 второй строки на значение «В». Поэтому второй сеанс будет ждать её освобождения. В то же время первый сеанс также ждёт освобождения строки индекса, соответствующей значению «Д», которая была захвачена вторым сеансом. Происходит бесконечное ожидание, и у нас возникает ситуация взаимной блокировки, о которой Oracle сигнализирует в первом сеансе:
ZH@ALFA(146)> UPDATE t3 SET c2 = 'Е' where c1 = 5; UPDATE t3 SET c2 = 'Е' where c1 = 5 * Ошибка в строке 1: ORA-00060: deadlock detected while waiting for resource
Изучим содержимое образовавшегося трассировочного файла взаимной блокировки. Первые две секции содержат обычную для таких случаев информацию:
Current SQL statement for this session: UPDATE t3 SET c2 = 'Е' where c1 = 5
Deadlock graph: ---------Blocker(s)-------- ---------Waiter(s)--------- Resource Name process session holds waits process session holds waits TX-00060013-00000bb3 19 146 X 18 144 S TX-00090012-00000bcb 18 144 X 19 146 S
А вот в секции сеансов, ожидающих строки, появились нужные нам данные: Session 144: obj - rowid = 0000E784 - AAAOeEAAAAAAAAAAAA (dictionary objn - 59268, file - 0, block - 0, slot - 0) Session 146: obj - rowid = 0000E784 - AAAOeEAAAAAAAAAAAA (dictionary objn - 59268, file - 0, block - 0, slot - 0)
По номеру objn мы легко находим объект, строки которого блокируются. В нашем случае это битовый индекс нашей таблицы:
SYSTEM@ALFA> SELECT owner, object_name FROM dba_objects WHERE object_id = 59268; OWNER OBJECT_NAME ----- ----------- ZH T3_BMP_IDX Выбрано: 1 строка
Наложение фрагментов индекса на основе битовых карт это, пожалуй, единственный случай из сценариев взаимной блокировки транзакций с разделяемым режимом, который содержит полную секцию Rows waited on, поэтому определить его по содержимому файлу трассировки не составляет труда.
Теперь поговорим немного о том, как избежать возникновения взаимных блокировок в битовых индексах. Прежде всего, конечно, надо по возможности ограничить обновления значений столбца, по которому построен битовый индекс. Это убережёт от лишних блокировок строк индекса. Во-вторых, можно подобрать обновления строк таким образом, чтобы они, к примеру, происходили последовательно в соответствии с упорядоченными значениями столбца, по которому построен битовый индекс. В этом случае будут просто возникать очереди ожиданий.
Содержание раздела