Ошибка ora 00060

ORA-00060

ORA-00060: вовремя ожидания ресурса обнаруживается тупиковая ситуация

Причина:

Вы и другой пользователь, каждый из вас ждет ресурса, а он заблокирован одним из вас. Такое событие известно под названием тупиковая ситуация «deadlock». Для разрешения данной ситуации ваш оператор посылается обратно, чтобы другой пользователь мог продолжить работу.

Действие:

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

May 10, 2021

I got ” ORA-00060: deadlock detected while waiting for resource ”  error in Oracle database.

ORA-00060: deadlock detected while waiting for resource

Details of error are as follows.

ORA-00060: deadlock detected while waiting for resource

Cause: Transactions deadlocked one another while waiting for resources.

Action: Look at the trace file to see the transactions and resources involved. Retry if necessary. 

deadlock detected while waiting for resource

This ORA-00060 error is related with the Transactions deadlocked one another while waiting for resources

Look at the trace file to see the transactions and resources involved. Retry if necessary.

What is a Deadlock?

A deadlock occurs when a session (A) wants a resource held by another session (B) , but that session also wants a resource held by the first session (A). There can be more than 2 sessions involved but the idea is the same.

Example of Deadlock

The following example demonstrates a deadlock scenario.

Setup

create table eg_60 ( num number, txt varchar2(10) );
insert into eg_60 values ( 1, 'First' );
insert into eg_60 values ( 2, 'Second' );

commit;
select rowid, num, txt from eg_60;

ROWID                     NUM TXT
------------------ ---------- ----------
AAASuCAAEAAAAinAAA          1 First
AAASuCAAEAAAAinAAB          2 Second


Session #1:

update eg_60 set txt='ses1' where num=1;

Session #2:

update eg_60 set txt='ses2' where num=2;
update eg_60 set txt='ses2' where num=1;

Session #2 is now waiting for the TX lock held by Session #1

Session #1:

update eg_60 set txt='ses1' where num=2;

Session #1 is now waiting  on the TX lock for this row.

The lock is held by Session #2.

However Session #2  is already waiting on Session #1

This causes a deadlock scenario so deadlock detection kicks in and one of the sessions signals an ORA-60.

Session #2:

*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource

Session #1 is still blocked until Session #2 commits or rolls back as ORA-60  only rolls back the current statement and not the entire transaction.

Diagnostic information produced by an ORA-60

ORA-60 error normally writes the error message in the alert.log together with the name of the trace file created. The exact format of this varies between Oracle releases. The trace file will be written to the directory indicated by the USER_DUMP_DEST or BACKGROUND_DUMP_DEST, depending on the type of process that creates the trace file.

The trace file will contain a deadlock graph and additional information similar to that shown below. This is the trace output from the above example which signaled an ORA-60 to Session #2:

 
DEADLOCK DETECTED ( ORA-00060 ) [Transaction Deadlock]
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:

Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TX-00050018-000004fa 22 132 X 19 191 X
TX-00070008-00000461 19 191 X 22 132 X

session 132: DID 0001-0016-00000005 session 191: DID 0001-0013-0000000C
session 191: DID 0001-0013-0000000C session 132: DID 0001-0016-00000005
Rows waited on:
Session 132: obj - rowid = 00012B82 - AAASuCAAEAAAAinAAA
(dictionary objn - 76674, file - 4, block - 2215, slot - 0)
Session 191: obj - rowid = 00012B82 - AAASuCAAEAAAAinAAB
(dictionary objn - 76674, file - 4, block - 2215, slot - 1)
----- Information for the OTHER waiting sessions -----
Session 191:
sid: 191 ser: 5 audsid: 340002 user: 88/USER1 flags: 0x45
pid: 19 O/S info: user: USER1, term: UNKNOWN, ospid: 3163
image: oracle@<NAME>.xx (TNS V1-V3)
client details:
O/S info: user: USER1, term: pts/3, ospid: 3097
machine: <Name>.xx program: sqlplus@<Name>.xx (TNS V1-V3)
application name: SQL*Plus, hash value=3669949024
current SQL:
update eg_60 set txt='ses1' where num=2
Information for THIS session:
----- Current SQL Statement for this session (sql_id=13b96yk6y5zny) -----
update eg_60 set txt='ses2' where num=1
===================================================
PROCESS STATE
-------------
.....

What does the trace information mean ?

You can use the following document to help diagnose common causes of deadlocks:

Document 1550091.2 Troubleshooting Assistant: Oracle Database ORA-00060 Errors on Single Instance (Non-RAC) Diagnosing Using Deadlock Graphs in ORA-00060 Trace Files

The following article provides information on how to detect and identify different deadlock types:

Document 1507093.1 How to Identify ORA-00060 Deadlock Types Using Deadlock Graphs in Trace

Section 1: Deadlock Graph

  
Deadlock graph:

                      ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TX-00050018-000004fa        22     132     X             19     191           X
TX-00070008-00000461        19     191     X             22     132           X

session 132: DID 0001-0016-00000005     session 191: DID 0001-0013-0000000C
session 191: DID 0001-0013-0000000C     session 132: DID 0001-0016-00000005

This shows which process was holding each lock, and which process was waiting for each lock.
For each resource there are 2 parts each giving information on the relevant process:

  • Blocker(s)
  • Waiters(s)

The columns in the graph indicate:

  • Resource Name: Lock name being held / waited for.
    Resource Name consists of  3 parts: Lock Type-ID1-ID2 where the information contained in id1 and id2 are different depending on the lock type
    Using the example above : TX-00050018-000004fa:
    Lock Type: TX
    ID1 (00050018) and ID2 (000004fa) point to the rollback segment and transaction table entries for that transaction.
  • process              V$PROCESS.PID of the Blocking / Waiting session
  • session               V$SESSION.SID of the Blocking / Waiting session
  • holds                  Mode the lock is held in
  • waits                  Mode the lock is requested in (waiting for)

So in this example:
SID 132 (Process 22) is holding TX-00050018-000004fa in eXclusive mode and is requesting TX-00070008-00000461 in eXclusive mode.

SID 191 (Process 19) is holding TX-00070008-00000461  in eXclusive mode and is requesting  TX-00050018-000004fa in eXclusive mode.

The important things to note here are the LOCK TYPE, the MODE HELD and the MODE REQUESTED for each resource as these give a clue as to the reason for the deadlock.

Section 2: Rows waited on

Rows waited on: 
Session 132: obj - rowid = 00012B82 - AAASuCAAEAAAAinAAA (dictionary objn - 76674, file - 4, block - 2215, slot - 0) 
Session 191: obj - rowid = 00012B82 - AAASuCAAEAAAAinAAB (dictionary objn - 76674, file - 4, block - 2215, slot - 1)

If the deadlock is due to row-level locks being obtained in different orders then this section of the trace file indicates the exact rows that each session is waiting to lock for themselves. Ie: If the lock requests are TX mode X waits then the ‘Rows waited on’ may show useful information.
For any other lock type / mode the ‘Rows waited on’ is not relevant and usually shows as “no row”.

In the above example:

SID 132 was waiting for ROWID ‘AAASuCAAEAAAAinAAA’ of object 76674
SID 191 was waiting for ROWID ‘AAASuCAAEAAAAinAAB’ of object 76674

This can be decoded to show the exact row/s.
Eg: SID 132 can be shown to be waiting thus:

SELECT owner, object_name, object_type FROM dba_objects WHERE object_id = 76674;

OWNER      OBJECT_NAM OBJECT_TYP
---------- ---------- ----------
USER1       EG_60      TABLE

SELECT * FROM user1.eg_60 WHERE ROWID='AAASuCAAEAAAAinAAA';

      NUM TXT
---------- ----------
        1 ses1

Section 3: Information on OTHER waiting session(s)

----- Information for the OTHER waiting sessions ----- 
Session 191: sid: 191 ser: 5 audsid: 340002 user: 88/USER1 flags: 0x45 
pid: 19 O/S info: user: USER1, term: UNKNOWN, ospid: 3163 
image: [email protected] (TNS V1-V3) 
client details: 
O/S info: user: USER1, term: pts/3, ospid: 3097 
machine: xxxxx.xx program: [email protected] (TNS V1-V3) 
application name: SQL*Plus, hash value=3669949024 
current SQL: 
update eg_60 set txt='ses1' where num=2

This section displays information regarding the other sessions (apart from the session that produced the ORA-60 deadlock trace) that are involved in the deadlock. The information includes:

  • session details
  • client details
  • Current SQL
    In this case: update eg_60 set txt=’ses1′ where num=2

Section 4: Information for this session

Information for THIS session: 
----- Current SQL Statement for this session (sql_id=13b96yk6y5zny) ----- 
update eg_60 set txt='ses2' where num=1 
=================================================== 
PROCESS STATE 
------------- 
.....

Displays the current sql for the session that creates the ORA-60 trace as well as a complete PROCESS STATE for the session.

Avoiding Deadlock

The above deadlock example occurs because the application which issues the update statements has no strict ordering of the rows it updates.Applications can avoid row-level lock deadlocks by enforcing some ordering of row updates. This is purely an application design issue.
Eg: If the above statements had been forced to update rows in ascending ‘num’ order then:

Session #1:          update eg_60 set txt='ses1' where num=1;

Session #2:          update eg_60 set txt='ses2' where num=1;

> Session #2 is now waiting for the
TX lock held by Session #1

Session #1:          update eg_60 set txt='ses1' where num=2;

> Succeeds as no-one is locking this row
commit;

> Session #2 is released as it is no longer waiting for this TX

Session #2:          update eg_60 set txt='ses2' where num=2;
commit;

The strict ordering of the updates ensures that a deadly embrace cannot occur. This is the simplest deadlock scenario to identify and resolve. Note that the deadlock need not be between rows of the same table – it could be between rows in different tables. Hence it is important to place  rules on the order in which tables are updated as well as the order of the rows within each table.

Other deadlock scenarios are discussed below.

Different Lock Types and Modes

The most common lock types seen in deadlock graphs are TX and TM locks. These may appear held / requested in a number of modes. It is the lock type and modes which help determine what situation has caused the deadlock.

Lock Mode Mode    Requested Probable Cause
TX X (mode 6) Application row level conflict.
Avoid by recoding the application to ensure  rows are always locked in
a particular order.
TX S (mode 4) There are a number of reasons that a TX lock may be requested in
S mode. See Document 62354.1 for a list of when TX locks are requested in mode 4.
TM SSX (mode 5)
or
S (mode 4)
This is usually related to the existence of foreign key constraints where the columns are not indexed on the child table. See Document 33453.1
for how to locate such constraints. See below for locating the OBJECT being waited on

Although other deadlock scenarios can happen the above are the most common.
The following article provides common cause information for the various types of deadlocks most frequently encountered based upon deadlock graphs found in trace:

Document 1507093.1 How to Identify ORA-00060 Deadlock Types Using Deadlock Graphs in Trace

TM locks – which object ?

ID1 of a TM lock indicates which object is being locked. This makes it very simple to isolate the object involved in a deadlock when a TM lock is involved.

The TM lock id is in the form TM-00012B85-00000000  where 00012B85 is the object number in hexadecimal format.

  1. Convert 00012B85 from hexadecimal to a decimal number
    Hexadecimal 00012B85 is  Decimal 76677
  2. Locate the object using DBA_OBJECTS
    SELECT owner,object_name,object_type 
    FROM dba_objects 
    WHERE object_id= 76677; 
    
    OWNER      OBJECT_NAM OBJECT_TYP 
    ---------- ---------- ---------- 
    USER1      EMP        TABLE
    

This is the object id that the TM lock covers.
Note that with TM locks it is possible that the lock is already held in  some mode in which case the REQUEST is to escalate the lock mode.

How to obtain Additional Information

If you are still having problems identifying the cause of a deadlock Oracle Support may be able to help. Additional information can be collected by adding the following to the init.ora parameters:

event=”60 trace name errorstack level 3;name systemstate level 266″

or by setting events using alter system in which case the event will be set for the life of the Oracle instance and only for new sessions:

ALTER SYSTEM SET events ’60 trace name errorstack level 3;name systemstate level 266′;

NOTE: This can generate a very large trace file which may get truncated unless MAX_DUMP_FILE_SIZE is large enough to accommodate the output.

When this is set any session encountering an ORA-60 error will write information about all processes on the database at the time of the error.This may help show the cause of the deadlock as it can show information about both users involved in the deadlock. Oracle Support will need all the information you have collected in addition to the new trace file to help identify where in the application you should look for problems.

It may be necessary to run the offending jobs with SQL_TRACE  or 10046 event enabled to show the order in which each session issues its commands in order to get into a deadlock scenario.

Do you want to learn Oracle Database for Beginners, then read the following articles.

Oracle Tutorial | Oracle Database Tutorials for Beginners ( Junior Oracle DBA )

About Mehmet Salih Deveci

I am Founder of SysDBASoft IT and IT Tutorial and Certified Expert about Oracle & SQL Server database, Goldengate, Exadata Machine, Oracle Database Appliance administrator with 10+years experience.I have OCA, OCP, OCE RAC Expert Certificates I have worked 100+ Banking, Insurance, Finance, Telco and etc. clients as a Consultant, Insource or Outsource.I have done 200+ Operations in this clients such as Exadata Installation & PoC & Migration & Upgrade, Oracle & SQL Server Database Upgrade, Oracle RAC Installation, SQL Server AlwaysOn Installation, Database Migration, Disaster Recovery, Backup Restore, Performance Tuning, Periodic Healthchecks.I have done 2000+ Table replication with Goldengate or SQL Server Replication tool for DWH Databases in many clients.If you need Oracle DBA, SQL Server DBA, APPS DBA,  Exadata, Goldengate, EBS Consultancy and Training you can send my email adress [email protected].-                                                                                                                                                                                                                                                 -Oracle DBA, SQL Server DBA, APPS DBA,  Exadata, Goldengate, EBS ve linux Danışmanlık ve Eğitim için  [email protected] a mail atabilirsiniz.

Введение

Взаимная блокировка (deadlock) —это ситуация в СУБД, при которой двое или более сеансов находятся в состоянии бесконечного ожидания ресурсов, захваченных самими этими же сеансами. При обычном состоянии, когда один из сеансов захватывает, какой либо ресурс, другие сеансы будут ожидать его освобождения, выстраиваясь в очередь, друг за другом. Но если удерживающий сеанс не может освободить ресурс, вследствие того что он ожидает освобождения ресурса, захваченного одним из ожидающих сеансов, возникает парадоксальная ситуация, при которой ни один из захваченных сеансами ресурсов не может быть освобождён. В этом случае СУБД сама должна вмешаться в процесс бесконечного ожидания и принудительно освободить запрос в сеансе к одному из ресурсов. При этом никак не должна пострадать уже начатая транзакция сеанса. СУБД должна только отменить последнее действие в сеансе, которое привело к сложившейся ситуации взаимного блокирования, и предоставить сеансу решить, зафиксировать ли изменения в данных или отменить их.

Надо сказать, что возникновение взаимного блокирования это исключительный случай для СУБД. У Oracle даже для этого случая есть специальная ошибка ORA-00060. Если взаимные блокировки возникают очень редко, то можно просто игнорировать эту ошибку, обрабатывая в приложении данное исключение. Но когда подобная ошибка начинает возникать очень часто, требуется детальный анализ возникающей ситуации. Большую помощь в этом нам может оказать файл трассировки, который создаёт Oracle в каждом случае возникновения взаимных блокировок. Образуется этот файл в каталоге, который определяется параметром инициализации user_dump_dest. Кроме создания файла, Oracle делает так же запись о возникшей ошибке в системный журнал alert.log. В этом журнале наряду с самим фактом происшедшей ошибки записывается ссылка к образованному файлу трассировки. Так как данный журнал и файл доступны только администратору базы данных, одним из пунктов его обязанностей, должно являться обнаружение подобных записей, и предоставление разработчику приложения всей доступной информации для исправления повторяющихся взаимных блокировок.

Граф ожидания транзакций

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

В Oracle, впрочем, как и в других современных СУБД, поиск ситуаций взаимного блокирования происходит постоянно. Вначале строится так называемый граф ожидания транзакций. Граф состоит из вершин и соединяющих их рёбер. Существуют два типа вершин – это вершины соответствующие транзакциям или сеансам, и вершины, представляющие из себя ресурсы или объекты. Ребра в данном случае представляют собой блокировки. Если блокировка захвачена, то ребро направлено от вершины соответствующей сеансу к вершине определяющей ресурс. Если же блокировка ожидает установки, то, наоборот, ребро направлено от вершины ресурса к вершине соответствующей сеансу. Если в этом сплетении рёбер и вершин обнаруживается цикл, то это означает, что возникла ситуация взаимного блокирования. При этом Oracle должен выбрать и отменить одно из ожидающих рёбер, что приведёт к разрыву цикла и нормализации ситуации.

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

Сценарии возникновения

Сценариев возникновения взаимных блокировок в Oracle на самом деле не так уж много. Их отличие друг от друга заключается в видах удерживающих и ожидающих блокировок, а так же в режимах в которых эти блокировки находятся. Ниже, мы попробуем смоделировать большинство ситуаций, при которых возникают взаимные блокировки, а также попытаемся детально разобраться в механизме самого взаимного блокирования. Научимся разбирать, анализировать содержимое трассировочного файла, и определять, к какому сценарию взаимного блокирования относиться данная взаимная блокировка.

Блокировки транзакций. Захват и ожидание в исключительном режиме.

Рассмотрим первый сценарий взаимного блокирования. Необходимым условием для его возникновения является наличие в двух разных сеансах установленной и ожидающей блокировок транзакций (TX) в исключительном режиме. Моделировать ситуацию будем с использованием редакции Oracle Express Edition и инструмента администратора ZhiSQL for Oracle.

Механизм взаимоблокировки

Вначале создадим тестового пользователя zh и выдадим ему все необходимые привилегии:

Подключение к: 
Oracle Database 10g Express Edition Release 10.2.0.1.0 – Production

SYSTEM@XE> CREATE USER zh IDENTIFIED BY test DEFAULT TABLESPACE users;

Пользователь создан

SYSTEM@XE> GRANT connect, resource, alter session TO zh;
 
Grant succeeded

Далее, создадим простую таблицу и вставим в неё две строки:

ZH@XE> CREATE TABLE t1 (c1 NUMBER PRIMARY KEY, c2 VARCHAR2(50));

Таблица создана

ZH@XE> INSERT INTO t1 (c1) VALUES(1);

Вставлено: 1 строка

ZH@XE> INSERT INTO t1 (c1) VALUES(2);

Вставлено: 1 строка

ZH@XE> COMMIT;
 
Commit complete

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

Первый сеанс:

ZH@XE(28)> ALTER SESSION SET EVENTS '10046 trace name context forever, level 
12';
 
Session altered

ZH@XE(28)> UPDATE t1 SET c2 = 'Строка1' WHERE c1 = 1;

Изменено: 1 строка

Второй сеанс:

ZH@XE(24)> UPDATE t1 SET c2 = 'Строка2' WHERE c1 = 2;

Изменено: 1 строка

В результате выполненных нами действий в существующих сеансах были открыты две транзакции. В первом сеансе была выставлена блокировка транзакции (TX) на первую строку в исключительном режиме. Такая же блокировка выставлена и во втором сеансе, но на вторую строку. Убедиться в этом, мы можем, сделав небольшой запрос к системному представлению v$lock:

SYSTEM@XE> SELECT * FROM v$lock WHERE sid IN (24, 28) AND type = 'TX';
 
ADDR     KADDR    SID TYPE ID1    ID2 LMODE REQUEST CTIME BLOCK
-------- -------- --- ---- ------ --- ----- ------- ----- -----
296EE5D8 296EE6F4 28  TX   262166 169 6     0       1110  0    
296FA680 296FA79C 24  TX   327718 163 6     0       879   0    

Рассмотрим более подробно содержимое этого запроса. Столбец SID здесь содержит идентификаторы первого и второго сеансов (28 и 24). Содержимое столбца TYPE указывает на тип блокировки, в нашем случае блокировки транзакции (TX). Столбец LMODE хранит значение 6, что соответствует установившемуся исключительному режиму блокировки. В результате мы видим, что в каждом из сеансов имеется по одной TX блокировке в установившемся исключительном режиме. Определить к какой транзакции относиться каждая из этих TX блокировок можно с помощью содержимого дополнительных столбцов ID1 и ID2. В них находятся составные части идентификатора транзакций, декодировать которые можно с помощью следующего запроса:

SYSTEM@XE> SELECT sid, TRUNC(id1/POWER(2,16)) rbs, BITAND(id1, POWER(2,16)-1)+ 
0 slot, id2 seq FROM v$lock WHERE sid IN (24, 28) AND type = 'TX';
 
SID RBS SLOT SEQ
--- --- ---- ---
28  4   22   169
24  5   38   163

Выбрано: 2 строки

Расшифровав значения столбцов ID1 и ID2, мы получили номер сегмента отката, слот и номер изменения транзакции. Эти значения полностью совпадают со значениями из представления v$transaction и все вместе, в шестнадцатеричном виде, представляют собой идентификатор транзакции:

SYSTEM@XE> SELECT s.sid, t.xidusn, xidslot, xidsqn FROM v$transaction t, 
v$session s  WHERE t.addr = s.taddr;
 
SID XIDUSN XIDSLOT XIDSQN
--- ------ ------- ------
24  5      38      163   
28  4      22      169   

Выбрано: 2 строки

Данный идентификатор нам встретиться ещё в дальнейшем, когда мы будем разбирать содержимое трассировочного файла взаимной блокировки, а пока продолжим последовательность начатых нами действий и изменим в первом сеансе содержимое второй строки:

ZH@XE(28)> UPDATE t1 SET c2 = 'Строка2' WHERE c1 = 2;

Ожидание …

Сеанс находиться в ожидании. Оно возникло от того, что первый сеанс пытается установить TX блокировку в исключительном режиме на вторую строку, которая уже захвачена TX блокировкой второго сеанса. Если в это время заглянуть в файл трассировки первого сеанса, то мы увидим там следующие строки:

WAIT #1: nam='enq: TX - row lock contention' ela= 3000022 name|mode=1415053318 
usn<<16 slot="327718" sequence="163" obj="" 13766="" tim="14923101611"

В сеансе постоянно возникает ожидание “конкуренция блокировки строки”. В параметрах этого ожидания мы видим уже знакомые нам значения идентификатора транзакции второго сеанса (slot, sequence). Именно эта транзакция, установила ранее TX блокировку в исключительном режиме на вторую строку и привела к ожиданию. Более детально это можно просмотреть в содержимом представления v$lock:

SYSTEM@XE> SELECT * FROM v$lock WHERE sid IN (24, 28) AND type = 'TX';
 
ADDR     KADDR    SID TYPE ID1    ID2 LMODE REQUEST CTIME BLOCK
-------- -------- --- ---- ------ --- ----- ------- ----- -----
2A201720 2A201734 28  TX   327718 163 0     6       1074  0    
296EE5D8 296EE6F4 28  TX   262166 169 6     0       7048  0    
296FA680 296FA79C 24  TX   327718 163 6     0       6817  1    

Выбрано: 3 строки

Как мы видим, в представлении появилась новая строка о TX блокировке со значением 6 в поле REQUEST. Данное значение означает, что сеанс 28 запросил установку TX блокировки на строку в исключительном режиме. При этом значения столбцов ID1 и ID2 этого запроса содержат идентификатор транзакции сеанса 24. Это свидетельствует о том, что первый сеанс ожидает освобождения строки захваченной транзакцией именно второго сеанса.

И так, на данный момент, мы имеем классическую картину ожидания. Но что произойдет, если мы изменим во втором сеансе первую строку? Ведь она уже захвачена первым сеансом:

ZH@XE(24)> UPDATE t1 SET c2 = 'Строка1' WHERE c1 = 1;

Ожидание…

Возникает ожидание, и мы становимся свидетелями типичного случая взаимного блокирования. Второй сеанс ожидает освобождения первой строки, заблокированной в результате начатой транзакции первого сеанса. В то же время первый сеанс ожидает освобождение второй строки, заблокированной транзакцией второго сеанса. Такое ожидание могло бы продолжаться вечно, но как мы рассматривали ранее, Oracle сам определяет подобные возникающие тупиковые ситуации в течение примерно трёх секунд. В нашем случае он просто отменит выполнение последнего оператора UPDATE в первом сеансе и сгенерирует следующую ошибку:

ZH@XE(28)> UPDATE t1 SET c2 = 'Строка2' WHERE c1 = 2;

UPDATE t SET c2 = 'Строка2' WHERE c1 = 2
       *
Ошибка в строке 1:
ORA-00060: deadlock detected while waiting for resource

Убедимся в том, что транзакции отменены не были. Для этого выполним следующий запрос:

SYSTEM@XE> SELECT s.sid, t.status, t.start_time, t.xid  FROM v$transaction t, 
v$session s  WHERE t.addr = s.taddr;
 
SID STATUS START_TIME        XID             
--- ------ ----------------- ----------------
24  ACTIVE 01/21/10 23:14:40 05002600A3000000
28  ACTIVE 01/21/10 23:10:49 04001600A9000000

Выбрано: 2 строки

Как видно из результатов запроса, транзакции по-прежнему активны. Отменять их полностью у Oracle нет необходимости, достаточно лишь просто вернуться в одном из сеансов к неявной точке сохранения, которая делается перед каждым DML оператором.

Теперь мы должны решить, что нам делать с этой незавершённой транзакцией в первом сеансе. Повторять отменённый оператор не имеет смысла. Ситуация взаимного блокирования повториться, и ошибка возникнет уже в другом сеансе. Поэтому нам остаётся либо произвести отмену, либо зафиксировать транзакцию. После этого второй сеанс продолжит выполнение оператора UPDATE, так как необходимая строка для этого будет освобождена.

Что же касается самой Oracle, то в результате всех перечисленных выше действий приведших к возникновению взаимной блокировки, в файл журнала alert_xe.log будет занесена запись следующего вида:

Fri Jan 22 01:09:58 2010
ORA-00060: Deadlock detected. More info in file 
c:\oraclexe\app\oracle\admin\xe\udump\xe_ora_2480.trc.

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

Трассировочный файл взаимной блокировки

Сам файл трассировки содержит множество данных, в том числе здесь находится и полный дамп состояния процессов Oracle на момент возникновения взаимоблокировки. Но нам важны только несколько секций файла. Первая из них – это текущий SQL оператор сеанса, который столкнулся с ошибкой взаимной блокировки и был отменён. Для этого находим в файле строку DEADLOCK DETECTED. Чуть ниже её, после ключевых слов «Current SQL statement for this session» будет находиться необходимая нам секция:

Current SQL statement for this session:
UPDATE t1 SET c2 = 'Строка2' WHERE c1 = 2

Вторая секция, которая нас заинтересует – это граф взаимной блокировки. Он находиться после ключевой строки Deadlock graph, и отображает цепочку захватов и ожиданий блокировок между сеансами:

Deadlock graph:
                       ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TX-00040016-000000a9        20      28     X             21      24           X
TX-00050026-000000a3        21      24     X             20      28           X

Вспомним, как Oracle обнаруживает блокировки. Для этого он постоянно строит граф ожидания транзакций. Если обнаружен цикл в этом графе, то это означает, что возникла взаимная блокировка. Так вот именно этот цикл и отображается в секции Deadlock graph, правда в очень специфическом виде.

Визуально граф представлен в виде таблицы, которая разделена на три логических блока. Первый блок обозначает ресурсы, участвующие во взаимной блокировке. В нашем случае таким ресурсом является блокировка транзакции. Ресурс обозначается символьным кодом TX, после которого идет идентификатор транзакции в шестнадцатеричном виде. Именно этот идентификатор, но только в десятичном виде мы получили, когда расшифровывали значения столбцов ID1 и ID2 представления v$lock. Второй блок состоит из столбцов, содержащих информацию о сеансах удерживающих данную блокировку, а также режим, в котором эта блокировка установлена. И наконец, третий блок аналогичен второму, но противоположен по содержанию. Он хранит информацию о сеансах, которые сделали запрос на установление блокировки, но вынуждены ожидать освобождения блокировки из второго блока.

Расшифровка графа не представляет сложности. Для этого нам надо проанализировать содержимое таблицы построчно, слева направо. К примеру, для нашего случая это будет выглядеть следующим образом. Транзакционная блокировка TX-00040016-000000a9 на строку удерживается сеансом 28 (поле session) в исключительном режиме (символ X в поле holds). Сеанс 24 одновременно ждёт освобождение этого ресурса, чтобы установить свою TX блокировку в исключительном режиме (символ X в поле waits).

Пока это нормальное ожидание необходимого ресурса. Поэтому далее мы обратимся ко второй строке графа. Здесь транзакционная блокировка TX-00050026-000000a3 на строку удерживается сеансом 24 в исключительном режиме, а сеанс 28 ждёт освобождение строки, чтобы установить свою TX блокировку в исключительном режиме. В то же время в первой строке графа сеанс 24 уже ожидает освобождения ресурса, в результате чего получается, что сеансы находятся в состоянии бесконечного ожидания. Единственным логичным действием в этом случае, явилось бы отмена ожидания установки блокировки в сеансе 28, что собственно и было сделано Oracle. В графе такое отменённое ожидание всегда отображается последним в блоке Waiter(s).

Итак, граф расшифрован. Он дал нам описание цепочки захватов и ожиданий TX блокировок в сеансах. Но по этой цепочке мы можем судить только об общей картине возникновения взаимной блокировки. Если же нам потребуется найти конкретные ресурсы, из-за которых возникают ожидания, сделать нам это будет затруднительно. К счастью Oracle сам позаботился об этом, записав в файл трассировки информацию о строках, освобождения которых от TX блокировок ожидают сеансы. Рассмотрим более подробно эту секцию. Найти её можно сразу после графа, по ключевой строке Rows waited on:

Rows waited on:
Session 24: obj - rowid = 000035C6 - AAADXGAAEAAAAFkAAA
  (dictionary objn - 13766, file - 4, block - 356, slot - 0)
Session 28: obj - rowid = 000035C6 - AAADXGAAEAAAAFkAAB
  (dictionary objn - 13766, file - 4, block - 356, slot - 1)

В этой секции для каждого ожидающего сеанса, который перечислен в графе, указана строка, на которую этот сеанс пытается получить TX блокировку. Строка идентифицируется номером объекта, которому она принадлежит, и идентификатором ROWID. Чуть ниже дана их полная расшифровка в десятичном виде. Это позволяет, с лёгкостью, обратившись, например, к системному представлению dba_objects, идентифицировать объект, к которому принадлежит данная строка:

SYS@XE> SELECT owner, object_name FROM dba_objects WHERE object_id = 13766;
 
OWNER OBJECT_NAME
----- -----------
ZH    T1  

Следующая секция трассировочного файла, которую мы рассмотрим, хотя и не столь важна, но позволяет дополнить картину взаимной блокировки. Она располагается сразу за секцией Rows waited on и находится по следующим ключевым словам:

Information on the OTHER waiting sessions:
Session 24:
  pid=21 serial=48 audsid=141 user: 39/ZH
  O/S info: user: ALFA\Сергей, term: ALFA, ospid: 1984:2524, machine: 
            program: DBASQL.exe
  client info: DBASQL
  application name: DBASQL.exe, hash value=0
  Current SQL Statement:
  UPDATE t1 SET c2 = 'Строка1' WHERE c1 = 1
End of information on OTHER waiting sessions.

Из этой секции можно получить информацию о пользователе, приложении и текущем SQL курсоре ожидающих сеансов, вовлеченных в процесс взаимной блокировки. Иногда это может быть полезно при поиске причин образования ситуации взаимного блокирования.

Как определить по содержимому трассировочного файла, что произошел первый сценарий взаимного блокирования? Для ответа на этот вопрос обратимся в первую очередь к графу взаимной блокировки. Для начала мы должны определить, с какого идентификатора начинаются имена ресурсов графа в столбце «Resource Name». В нашем случае это всегда будет идентификатор TX, то есть блокировка транзакции. Далее нам следует проверить значения режимов блокировок, отображаемые в столбцах holds и waits. Они должны иметь одинаковое значение равное символу X. Не следует так же забывать, что данный сценарий взаимного блокирования возникает на уровне строк, и, следовательно, в секции «Rows waited» всегда будут присутствовать данные об ожидающих строках. Отсюда следует непреложное правило о том, что в первой секции «Current SQL statement for this session» при данном сценарии вы никогда не встретите оператора INSERT, так как строки, вставленные в одном из сеансов, никогда не будут доступны для другого сеанса до фиксации транзакции.

Выводы

Пришло время обобщить полученную информацию, которую мы узнали, изучая взаимную блокировку, образующуюся по первому сценарию. Обычно, такая взаимная блокировка возникает из-за неодинаковой последовательности обработки строк, в пределах транзакций разных сеансов. Если бы мы, к примеру, в нашем случае проводили бы обновление строк таблицы в каждом из сеансов последовательно в соответствии со значением ключевого столбца «c1», то мы могли бы избежать взаимных блокировок. Образовалась бы простая очередь ожидания ресурса. Правда, бывают ситуации, когда одинаковую последовательность обработки строк в разных сеансах сделать трудно или даже невозможно. Как тогда выходить из ситуации? Самое простое, что приходит на ум, и о чём я уже упоминал выше, это можно обрабатывать ошибку в приложении, и в случае её возникновения в одном из сеансов откатывать транзакцию, повторяя все необходимые действия позже. Велика вероятность, что транзакция в другом сеансе за это время завершиться и не будет мешать производить изменения в данных. Вообще по возможности надо всегда уменьшать время длительности транзакций в приложениях. Если транзакция будет короткой, то это уменьшает время в течение, которого сеансы могут попасть в неприятную ситуацию с взаимным блокированием. Это справедливо и для длинной цепочки каскадного срабатывания триггеров, которым так грешат некоторые сложные системы. По возможности эту цепочку надо укорачивать.

I have a series of scripts running in parallel as a nohup on an AIX server hosting oracle 10g. These scripts are written by somebody else and are meant to be executed concurrently. All the scripts are performing updates on a table. I am getting the error,

ORA-00060: deadlock detected while
waiting for resource

As I googled for this, I found,
http://www.dba-oracle.com/t_deadly_perpetual_embrace_locks.htm

Even though the scripts are performing updation on the same table concurrently, they are performing updates on different records of the table determined by the WHERE clause with no overlaps of records between them.

So would this have caused the error?.

Will this error happen regardless of where the updates are performed on a table?.

Should I avoid concurrent updates on a table at all times?.

Strangely I also found on the nohup.out log,
PL/SQL successfully completed after the above quoted error.

Does this mean that oracle has recovered from the deadlock and completed the updates successfully or Should I rerun those scripts serially?

starball's user avatar

starball

21.2k7 gold badges47 silver badges256 bronze badges

asked Jun 19, 2010 at 8:00

wowrt's user avatar

I was recently struggling with a similar problem. It turned out that the database was missing indexes on foreign keys. That caused Oracle to lock many more records than required which quickly led to a deadlock during high concurrency.

Here is an excellent article with lots of good detail, suggestions, and details about how to fix a deadlock:
http://www.oratechinfo.co.uk/deadlocks.html#unindex_fk

answered Oct 25, 2010 at 14:55

benvolioT's user avatar

benvolioTbenvolioT

4,5072 gold badges36 silver badges30 bronze badges

You can get deadlocks on more than just row locks, e.g. see this. The scripts may be competing for other resources, such as index blocks.

I’ve gotten around this in the past by engineering the parallelism in such a way that different instances are working on portions of the workload that are less likely to affect blocks that are close to each other; for example, for an update of a large table, instead of setting up the parallel slaves using something like MOD(n,10), I’d use TRUNC(n/10) which mean that each slave worked on a contiguous set of data.

There are, of course, much better ways of splitting up a job for parallelism, e.g. DBMS_PARALLEL_EXECUTE.

Not sure why you’re getting «PL/SQL successfully completed», perhaps your scripts are handling the exception?

answered Jun 19, 2010 at 12:57

Jeffrey Kemp's user avatar

Jeffrey KempJeffrey Kemp

59.2k14 gold badges106 silver badges158 bronze badges

0

I ran into this issue as well. I don’t know the technical details of what was actually happening. However, in my situation, the root cause was that there was cascading deletes setup in the Oracle database and my JPA/Hibernate code was also trying to do the cascading delete calls. So my advice is to make sure that you know exactly what is happening.

answered Feb 3, 2011 at 2:54

DaShaun's user avatar

DaShaunDaShaun

3,7322 gold badges27 silver badges29 bronze badges

I was testing a function that had multiple UPDATE statements within IF-ELSE blocks.

I was testing all possible paths, so I reset the tables to their previous values with ‘manual’ UPDATE statements each time before running the function again.

I noticed that the issue would happen just after those UPDATE statements;

I added a COMMIT; after the UPDATE statement I used to reset the tables and that solved the problem.

So, caution, the problem was not the function itself…

answered Apr 7, 2019 at 7:23

10110's user avatar

1011010110

2,3631 gold badge21 silver badges37 bronze badges

1

totn Oracle Error Messages


Learn the cause and how to resolve the ORA-00060 error message in Oracle.

Description

When you encounter an ORA-00060 error, the following error message will appear:

  • ORA-00060: deadlock detected while waiting for resource

Cause

You tried to execute a statement, but your session was deadlocked because another session had the same resource locked. The statement(s) that you tried to execute have been rolled back.

Resolution

The option(s) to resolve this Oracle error are:

Option #1

You can wait a few minutes and try to re-execute the statement(s) that were rolled back.

Option #2

You can execute a ROLLBACK and re-execute all statements since the last COMMIT was executed.

Понравилась статья? Поделить с друзьями:
  • Ошибка jsonexception no value for type вконтакте
  • Ошибка ora 00028
  • Ошибка json что это
  • Ошибка json parse error
  • Ошибка opvl ippon smart winner 2000