Это сводится к порядку, в котором база данных пытается получить блокировки строк.
В вашем примере 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
.