ORA-08177 с запертым столом
После
create table l (id int primary key, val int);
insert into l values (0, 0);
и инициализации двух сеансов оракула
alter session set isolation_level=serializable;
следующая последовательность приводит к ORA-08177: can't serialize access for this transaction
.
session 1 session 2
-----------------------------------+-----------------------------------
lock table l in exclusive mode
-----------------------------------+-----------------------------------
lock table l in exclusive mode
-----------------------------------+-----------------------------------
update l set val = 1 where id = 0 [blocked]
-----------------------------------+-----------------------------------
commit [blocked]
-----------------------------------+-----------------------------------
update l set val = 2 where id = 0
-----------------------------------+-----------------------------------
[ORA-08177]
Почему? Это выглядит совершенно серийно для меня.
Ответы
Ответ 1
Oracle, в отличие, скажем, SQL Server, работает оптимистично в режиме SERIALIZABLE
, то есть он не принимает блокировок. Если есть параллельный доступ во время транзакции, Oracle просто потерпит неудачу, а не повторит попытку.
Кроме того, Oracle не только выкинет это исключение на фактический одновременный доступ, но и если что-то, что затронуто во время транзакции, также было изменено другим потоком с начала транзакции, независимо когда в ходе транзакции были получены данные.
Ответ 2
Когда сериализуемая транзакция пытается изменить данные, которые другая транзакция (сериализуемая или нет) уже была изменена (или даже данные, которые находятся рядом с данными, другая транзакция уже изменилась в том же блоке данных) после того, как эта сериализуемая транзакция началась, вы 'получит сообщение об ошибке ORA-08177
. Когда вы устанавливаете уровень изоляции транзакции на serializable
, необходимо иметь в виду две важные вещи:
- Последовательная транзакция может изменить запись только в том случае, если изменения к этой записи, сделанные другой транзакцией (транзакциями), были уже совершены к моменту начала сериализуемой транзакции (а не инструкции).
- Простая согласованность расширяется до уровня транзакции, а не уровня оператора, так как уровень изоляции транзакции равен
read committed
(уровень изоляции транзакции по умолчанию).
В принципе, когда начинается сериализуемая транзакция, он получает свой собственный снимок данных, данных и работает только с этим моментальным снимком и видит только свои собственные данные - не может видеть, совершенные другими данными транзакции, пока не получит новый снимок данные. И новый снимок данных будет получен после завершения (фиксации или откат) одной сериализуемой транзакции и запуска другой.
Простой пример, который приводит к ошибке ORA-08177
:
-- /* test table */
SQL> create table t1(col) as
select 3 from dual;
Table created.
-- sqlplus session #1 sqlplus #session 2
----------------------------------------------------------------------------
SQL> alter session
set isolation_level=serializable;
SQL> alter session set
isolation_level=serializable;
/*
You start serializable transaction by locking the t1 table in
exclusive mode.
*/
-- serializable transaction #1
SQL> lock table t1 in exclusive mode; -- serializable transaction # 2
-- snapshot of t1 has been acquired
SQL> update t1 set col = 5 SQL> lock table t1 in exclusive mode;
where col = 3;
-- this update does not see changes
1 row updated. -- transaction #1 has already made
-- to t1 row where col = 3.
SQL> update t1 set col = 7
SQL> commit; where col = 3;
ERROR at line 1:
Commit complete. ORA-08177: can't serialize access
for this transaction
В приведенной выше ситуации Oracle, чтобы поддерживать вашу базу данных в согласованном режиме, бросает на вас ORA-08177
, поскольку она знает о том, что сериализуемая транзакция №1 внесла изменения в строки (строки), где col = 3
и сериализуемая транзакция № 2 не приобрела новый моментальный снимок данных, но работает на старом.
Кажется очень и очень чрезмерным использовать явную исключительную блокировку таблиц и сериализуемую транзакцию вместе: a) огромный удар по concurrency; b) Чтобы избежать ORA-08177
, вам необходимо совершить транзакцию или откат транзакции, которая вызывает ошибку ORA-08177
и повторите попытку снова, и как только вы произведете фиксацию или откат, исключительная блокировка таблицы будет немедленно выпущена.