Оракул тупик в одном обновлении заявления? - PullRequest
1 голос
/ 27 июня 2019

Первая версия моего оракула:

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0      Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

Я создаю таблицу и вставляю две строки:

create table test_table
(
    objectId VARCHAR2(40) not null,
    dependId VARCHAR2(40) not null
);

insert into test_table values(1, 10000);
insert into test_table values(2, 20000);
commit;

Затем откройте две сессии и выполните следующие команды по порядку.

Вариант 1:

сессии1:

update test_table set dependId=100000 where objectid in (2);

сессии2:

update test_table set dependId=200000 where objectid in (1,2);

seesion1:

update test_table set dependId=100000 where objectid in (1);

и сессия2 показывает ORA-00060: deadlock detected while waiting for resource

* 1026 Вариант 2 *

сессии1:

update test_table set dependId=100000 where objectid in (1);

сессии2:

update test_table set dependId=200000 where objectid in (2,1);

seesion1:

update test_table set dependId=100000 where objectid in (2);

и тупик не возникает.

Пожалуйста, объясните причину. Как update ... where objectid in (1,2) удерживает блокировку?

1 Ответ

2 голосов
/ 27 июня 2019

Это сводится к порядку, в котором база данных пытается получить блокировки строк.

В вашем примере objectid = 1 является «первым» в таблице.Вы можете проверить это, отсортировав данные по rowid:

create table test_table
(
    objectId VARCHAR2(40) not null,
    dependId VARCHAR2(40) not null
);

insert into test_table values(1, 99);
insert into test_table values(2, 0);
commit;

select rowid, t.* from test_table t
order  by rowid;

ROWID                 OBJECTID    DEPENDID   
AAAT9kAAMAAAdMVAAA    1           99          
AAAT9kAAMAAAdMVAAB    2           0     

Если в сеансе 1 вы сейчас выполняете:

update test_table set dependId=100000 where objectid in (2);

Вы обновляете «вторую» строку в таблице.Когда сеанс 2 выполняется:

update test_table set dependId=200000 where objectid in (2,1);

Считывает блок данных.Затем пытается получить блокировки на них в порядке их хранения.Так он смотрит на первый ряд (objectid = 1), спрашивает "заблокирован ли?"Находит ответ нет.И блокирует строку.

Затем повторяется этот процесс для второго ряда.Который заблокирован сеансом 1. При запросе v$lock вы должны увидеть две записи, запрашивающие блокировки 'TX' в lmode = 6. Одна для каждой сессии:

select sid from v$lock
where  type = 'TX'
and    lmode = 6;

SID   
    75 
    60 

Так что вна этом этапе обе сессии заблокированы одной строкой.И сессия 2 ожидает сессию 1.

В сессию 1 вы теперь запускаете:

update test_table set dependId=100000 where objectid in (1);

БУМ!Deadlock!

ОК, но как мы можем быть уверены, что это связано с сохранением строк заказа?

Используя кластеризацию атрибутов (функция 12c), мы можем изменить строки заказа, сохраненныев блоках objectid = 2 является «первым»:

alter table test_table 
  add clustering 
  by linear order ( dependId );

alter table test_table move;

select rowid, t.* from test_table t
order  by rowid;

ROWID                 OBJECTID    DEPENDID   
AAAT9lAAMAAAdM7AAA    2           0           
AAAT9lAAMAAAdM7AAB    1           99   

Повторите тест.В сеансе 1:

update test_table set dependId=100000 where objectid in (2);

Так что это заблокировало «первую» строку.В сеансе 2:

update test_table set dependId=200000 where objectid in (2,1);

Попытка заблокировать «первую» строку.Но не могу, потому что сессия 1 заблокировала его.Таким образом, на данный момент только сеанс 1 удерживает любые блокировки.

Проверьте v$lock, чтобы быть уверенным:

select sid from v$lock
where  type = 'TX'
and    lmode = 6;

SID   
    60 

И, конечно же, когда вы запускаете второе обновление в сеансе 1, он завершает:

update test_table set dependId=100000 where objectid in (1);

ПРИМЕЧАНИЕ

Это не означает, что update гарантированно блокирует строки в порядке, в котором они хранятся в таблицеблоки.Добавление или удаление индексов может повлиять на это поведение.Изменения между версиями Oracle Database могут меняться.

Ключевым моментом является то, что update должен блокировать строки в некотором порядке.Он не может мгновенно получить блокировки во всех строках, которые он изменит.

Так что, если у вас есть два или более сеансов с несколькими обновлениями, возможна взаимоблокировка.Поэтому вам следует начать транзакцию, заблокировав все строки, которые вы хотите изменить, с помощью select ... for update.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...