Deadlocks in Oracle - oracle

I want to make a script where the oracle sessions which go into a deadlock are killed automatically.Is it possible to find out the session id for the sessions which go into a deadlock.As of now I have to bounce the database to remove the deadlock.Is any solution to this problem possible?

I want to make a script where the oracle sessions which go into a deadlock are killed automatically
EDIT Explained in a better way, corrected few sentences, and added a test case to demonstrate deadlock scenario.
Why do you want to re-invent the wheel? Oracle detects a deadlock automatically, throws ORA-00060: deadlock detected while waiting for resource, and rolls back one of the transactions involved in the deadlock which Oracle decided as the victim. The previous successful transactions are not rolled back. Even after the deadlock error, if a commit is issued, the previous successful transaction will be committed. At this time, the other session's transaction will also succeed and you could issue a commit. There is nothing that you need to explicitly do here. Deadlocks are automatically cleared -- you never need to clear them.
Usually, Oracle takes a second or two to detect a deadlock and throws the error.
You can try with a simple test case as demonstrated here : Understanding Oracle Deadlock
Let's look at a test case -
SQL> CREATE TABLE t_test(col_1 NUMBER, col_2 NUMBER);
Table created
SQL> INSERT INTO t_test VALUES(1,2);
1 row inserted
SQL> INSERT INTO t_test VALUES(3,4);
1 row inserted
SQL> COMMIT;
Commit complete
SQL> SELECT * FROM t_test;
COL_1 COL_2
---------- ----------
1 2
3 4
Note the time of each transaction, I have set time on timing on for a better understanding.
SESSION : 1
12:16:06 SQL> UPDATE t_test SET col_1 = 5 WHERE col_2=2;
1 row updated.
Elapsed: 00:00:00.00
SESSION : 2
12:16:04 SQL> UPDATE t_test SET col_1 = 6 WHERE col_2=4;
1 row updated.
Elapsed: 00:00:00.00
12:16:31 SQL> UPDATE t_test SET col_1 = 7 WHERE col_2=2;
At this point, SESSION 2 keeps waiting.
SESSION : 1
12:16:15 SQL> UPDATE t_test SET col_1 = 8 WHERE col_2=4;
At this point, SESSION 2 is the victim of deadlock, SESSION 1 is still waiting.
Let's look at the session details from SESSION 2 -
12:22:15 SQL> select sid,status,program,sql_id, state, wait_class, blocking_session_status, event from v$session where schemaname='LALIT' and program='sqlplus.exe';
SID STATUS PROGRAM SQL_ID STATE WAIT_CLASS BLOCKING_SE EVENT
---------- -------- --------------- ------------- ------------------- --------------- ----------- ----------------------------------------------------------------
14 ACTIVE sqlplus.exe 60qmqpmbmyhxn WAITED SHORT TIME Network NOT IN WAIT SQL*Net message to client
134 ACTIVE sqlplus.exe 5x0zg4qwus29v WAITING Application VALID enq: TX - row lock contention
Elapsed: 00:00:00.00
12:22:18 SQL>
So, v$session details when viewed in SESSION 2, i.e. SID 14, says the status is ACTIVE.
Let's look at the session details from another session, lets call it SESSION 3 for the sake. Remember, SESSION 1 is still waiting.
SQL> set time on timing on
12:24:41 SQL> select sid,status,program,sql_id, state, wait_class, blocking_session_status, event from v$session where schemaname='LALIT' and program='sqlplus.exe'
SID STATUS PROGRAM SQL_ID STATE WAIT_CLASS BLOCKING_SE EVENT
---------- -------- --------------- ------------- ------------------- ---------- ----------- ------------------------------
13 ACTIVE sqlplus.exe 60qmqpmbmyhxn WAITED SHORT TIME Network NOT IN WAIT SQL*Net message to client
14 INACTIVE sqlplus.exe WAITING Idle NO HOLDER SQL*Net message from client
134 ACTIVE sqlplus.exe 5x0zg4qwus29v WAITING Applicatio VALID enq: TX - row lock contention
n
Elapsed: 00:00:00.01
12:24:44 SQL>
So, for other sessions, SESSION 2, i.e. SID 14, is INACTIVE. SESSION 1 is still WAITING with event enq: TX - row lock contention.
Let's commit SESSION 2 -
12:22:18 SQL> commit;
Commit complete.
Elapsed: 00:00:00.01
12:25:43 SQL>
At this point, the lock is released for SESSION 1, let's commit session 1 as well -
12:16:15 SQL> UPDATE t_test SET col_1 = 8 WHERE col_2=4;
1 row updated.
Elapsed: 00:08:27.29
12:25:43 SQL> commit;
Commit complete.
Elapsed: 00:00:00.00
12:26:26 SQL>
Elapsed: 00:08:27.29 shows SESSION 1 was waiting that long till SESSION 2 was committed.
To summarize, here is the entire story of session 1 -
12:16:06 SQL> UPDATE t_test SET col_1 = 5 WHERE col_2=2;
1 row updated.
Elapsed: 00:00:00.00
12:16:15 SQL> UPDATE t_test SET col_1 = 8 WHERE col_2=4;
1 row updated.
Elapsed: 00:08:27.29
12:25:43 SQL> commit;
Commit complete.
Elapsed: 00:00:00.00
12:26:26 SQL>
To summarize, here is the entire story of session 2 -
12:16:04 SQL> UPDATE t_test SET col_1 = 6 WHERE col_2=4;
1 row updated.
Elapsed: 00:00:00.00
12:16:31 SQL> UPDATE t_test SET col_1 = 7 WHERE col_2=2;
UPDATE t_test SET col_1 = 7 WHERE col_2=2
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
Elapsed: 00:00:24.47
12:22:15 SQL> select sid,status,program,sql_id, state, wait_class, blocking_session_status, event from v$session where schemaname='LALIT' and program='sqlplus.exe';
SID STATUS PROGRAM SQL_ID STATE WAIT_CLASS BLOCKING_SE EVENT
---------- -------- --------------- ------------- ------------------- --------------- ----------- ----------------------------------------------------------------
14 ACTIVE sqlplus.exe 60qmqpmbmyhxn WAITED SHORT TIME Network NOT IN WAIT SQL*Net message to client
134 ACTIVE sqlplus.exe 5x0zg4qwus29v WAITING Application VALID enq: TX - row lock contention
Elapsed: 00:00:00.00
12:22:18 SQL> commit;
Commit complete.
Elapsed: 00:00:00.01
12:25:43 SQL>
Now, let's see which transaction actually got rolled back and which got committed -
12:25:43 SQL> select * from t_test;
COL_1 COL_2
---------- ----------
5 2
8 4
Elapsed: 00:00:00.00
12:30:36 SQL>
Conclusion
In my opinion, the best way to know the session details of a deadlock is to log the details as verbose as possible. Else, it is a nightmare for a DBA to investigate without proper information logged. For that matter, even a Developer would find it to be an herculean task to rectify and fix the actual design flaw if the deadlock error details are not logged verbosely. And to conclude with a one liner statement, A deadlock is due to design flaw, Oracle is just the victim and the application being the culprit. Deadlocks are scary, but they point out the design flaws that must be rectified sooner or later.

user 1
update table_c set id = 200 where id = 13;
BEGIN
DBMS_LOCK.sleep(14);
END;
/
update table_c set id = 200 where id = 15;
user 2
update table_c set id = 2000 where id = 15;
BEGIN
DBMS_LOCK.sleep(14);
END;
/
update table_c set id = 1000 where id = 13;

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.
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.
Resolving Deadlock
Oracle is smart and it is able to find deadlock situation within 3 seconds. Below are the options to avoid deadlocks inside the database
Ask the session getting deadlock error ORA-00060 to issue either COMMIT or ROLLBACK
Ask the waiting session to kill the SQL / transaction
Look inside alert log / trace file for the sessions involved in deadlock and inform application team to improve the code
As a DBA, whenever you get a Deadlock alert, immediately contact application team and inform them.

Related

How to check the history of a particular session in oracle

I am using oracle database 19c. I need to identify the historical blocking session details which had blocked almost 50 sessions. In ASH report, i can find the sid 1258 under blocking_session column under dba_hist_active_sess_history but not under sid column which is pretty unusual. I could not find any information related to activity of blocking session from hang analyze report as well. Is there any way to drill down the depth of a session other than ASH?
dba_hist_active_sess_history output
SAMPLE_ID SAMPLE TIME SID STATE EVENT SQL_ID BLK SID START SQL_EXEC_ID
---------- ----------- ----- ------------- ------------------------- --------------- ---------- ----- -----------
135345711 21 11:25:11 217 WAITING enq: TX - row lock conten shd23fhjdgjyhu 1258 <<== blocking sid 21:04 19783669
Hang analyze output:
{
p1: 'driver id'=0x54435000
p2: '#bytes'=0x1
time in wait: 2 min 16 sec
timeout after: never
wait id: 1445
blocking: 1 session
current sql_id: 3598363420
Thanks in advance !

Automatically get notified when a database record is updated

I have an oracle client database with 500k client records. Every month i run a batch process to produce some monthly analytical using the client data. But sometimes, the database owner tells me that they have updated the data and i need to run the batch again.
I would like to build a monitoring /notification service that will immediately tell me when a particular client record got updated and what was the update. That way i know if the update can be ignored or not.
I can of course run an hourly sql query that compares each client record with its previous snapshot but is there a better solution?
Does something like Kafka work in this scenario? How exactly?
You can use Continuous Query Notification (previously known as Database Change Notifications).
Read more about it:
DBMS_CQ_NOTIFICATION with examples
Continuous Query Notification for JDBC
#JustinCave also suggests a pretty good option to create simple trigger and enable it only when you really need it, but probably it would be easier just to create materialized view log and check it periodically for new changes. You can get changed rows from it.
Simple example:
SQL> create table t(id int primary key, a int, b int);
Table created.
SQL> create materialized view log on t with primary key, rowid;
Materialized view log created.
SQL> select log_table from user_mview_logs where master='T';
LOG_TABLE
--------------------------------
MLOG$_T
SQL> desc mlog$_t
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
M_ROW$$ VARCHAR2(255)
SNAPTIME$$ DATE
DMLTYPE$$ VARCHAR2(1)
OLD_NEW$$ VARCHAR2(1)
CHANGE_VECTOR$$ RAW(255)
XID$$ NUMBER
SQL> column M_ROW$$ format a20;
SQL> column CHANGE_VECTOR$$ format a10;
SQL> select * from mlog$_t;
no rows selected
SQL> insert into t(id, a, b) values(1,1,1);
1 row created.
SQL> commit;
Commit complete.
SQL> select * from mlog$_t;
ID M_ROW$$ SNAPTIME$$ D O CHANGE_VEC XID$$
---------- -------------------- ------------------- - - ---------- ----------
1 AAASWNAAMAAAAEXAAA 4000-01-01 00:00:00 I N FE 2.8148E+15

Root cause of deadlock?

I see below details for one of the deadlock detected in oracle 12g trace files but i am not getting why deadlock is happening here ?
Deadlock happens when thread 1 acquires lock on table1 or table rows but wait for table 2 rows and at the same time thread 2 acquires lock on table 2 rows byt wait for table1 rows
But i do not see the details which session is acquired the lock on which table and waiting for which resource . Any help what are the object which got locked here and
cause of it ?
Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TX-00290010-00015F75-00000000-00000000 295 1200 X 288 10 X
TX-00570012-00005D9B-00000000-00000000 288 10 X 295 1200 X
session 1200: DID 0001-0127-00014421 session 10: DID 0001-0120-00016BD1
session 10: DID 0001-0120-00016BD1 session 1200: DID 0001-0127-00014421
Rows waited on:
Session 1200: obj - rowid = 00051348 - BABRNIAARAAKfNLAAl
...
Session 10: obj - rowid = 000514F2 - BABRTyAAJAAKWbIAAY
....
----- Information for the OTHER waiting sessions -----
....
current SQL:
update employee set name=:1
----- End of information for the OTHER waiting sessions -----
Information for THIS session:
----- Current SQL Statement for this session (sql_id=5dfr2prw60rh1) -----
update department set address =:1 where id=:1
===================================================
Your output says the current session is trying to update a locked record in the department table (the "information for THIS session" output). The other session is trying to update every employee record (the "information for the OTHER waiting sessions" output). The current session must have updated a record in the employee table, blocking the other session, while the other session updated the record the current session is trying to update.
I assume this is some sort of exercise to cause a deadlock, since you're setting every employee record to the same name.

Oracle blocking table while updating

I have a database, which is used by some people and a service for updating personal data at the same time.
So there is the possibility, that a person updates a row and forgets the commit. later the update-service always wants to update this row and the service hangs, until the commit will be made or the session will be closed. This may take some time, while the update-service could not do its work.
Is it possible that i can determine this situation?
Probably the update-service can send a statement like this:
UPDATE person
SET email_address = 'new.email#company.com' WHERE person_id='1234567'
ON LOCKED ERROR;
This statement should get an error, if there is a lock on this row.
Or can I configure the oracle server to send an error code after a defined time, if the lock on a row does not finish?
a person updates a row and forgets the commit. later the update-service always wants to update this row and the service hangs, until the commit will be made or the session will be closed
Ideally, readers do not block writers, and writers do not block readers.
What you are describing is not a DEADLOCK scenario. When a session executes an update, it acquires a row-level exclusive lock and other session trying to update these rows, need to wait until the lock is released by a COMMIT/ROLLBACK.
Deadlock happens when two or more sessions are waiting on each other for the lock.
Or can i configure the oracle server to send an error code after a defined time, if the lock on a row does not finish?
To check the blocking session and wait class, you can query the v$session view:
select sid,
status,
program,
sql_id,
state,
wait_class,
blocking_session_status,
event
from v$session;
When it comes to deadlocks, Oracle detects a deadlock automatically, throws ORA-00060: deadlock detected while waiting for resource, and rolls back one of the transactions involved in the deadlock which Oracle decided as the victim. The previous successful transactions are not rolled back. Even after the deadlock error, if a commit is issued, the previous successful transaction will be committed. At this time, the other session's transaction will also succeed and you could issue a commit. There is nothing that you need to explicitly do here. Deadlocks are automatically cleared -- you never need to clear them.
See a similar question which I answered here https://stackoverflow.com/a/28455397/3989608
For a detailed demonstration and examples of deadlocks, see Understanding Oracle Deadlock
IF you use FOR UPDATE NOWAIT, then Oracle would not let you update those rows and throw the following error:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
For example,
Session 1:
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno = 10
4 FOR UPDATE NOWAIT;
EMPNO DEPTNO
---------- ----------
7782 10
7839 10
7934 10
SQL>
Session 2:
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno in (10, 20)
4 FOR UPDATE NOWAIT;
FROM emp WHERE
*
ERROR at line 2:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
Going a step further, if you want to avoid updating the rows which are already locked, you could use the FOR UPDATE SKIP LOCKED clause to avoid other sessions to fetch the rows for update which are already locked.
For example,
Session 1:
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno = 10
4 FOR UPDATE NOWAIT;
EMPNO DEPTNO
---------- ----------
7782 10
7839 10
7934 10
SQL>
Session 2:
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno in (10, 20)
4 FOR UPDATE NOWAIT;
FROM emp WHERE
*
ERROR at line 2:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
Now let's skip the rows which are locked by session 1.
SQL> SELECT empno, deptno
2 FROM emp WHERE
3 deptno IN (10, 20)
4 FOR UPDATE SKIP LOCKED;
EMPNO DEPTNO
---------- ----------
7369 20
7566 20
7788 20
7876 20
7902 20
SQL>
So, department = 10 were locked by session 1 and then department = 20 are locked by session 2.
See this similar question about avoiding the updates on already locked rows Oracle deadlock keeps repeating on the same record
You could do it in two stages; query the table with FOR UPDATE NOWAIT, which will throw an exception if the row is already locked, and then do the update (and commit) if it doesn't error:
SELECT * FROM person WHERE person_id = 1234567 FOR UPDATE NOWAIT;
UPDATE person SET email_address = 'new.email#company.com' WHERE person_id='1234567';
If you do that from two sessions, without committing, then the second one to run the select will see:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
You could also have a timeout with WAIT <time>:
SELECT * FROM person WHERE person_id = 1234567 FOR UPDATE WAIT 3;
UPDATE person SET email_address = 'new.email#company.com' WHERE person_id='1234567';
In that case, the second caller will get an error if the first hasn't committed/rolled back in 3 seconds:
ORA-30006: resource busy; acquire with WAIT timeout expired
If you're calling this through JDBC or OCI etc., you may need to have separate back-to-back calls, on the same session, and with the first call handling the exception if it's thrown.

Know if a record is updated within Oracle?

Is there a option to see if existing table/record from a Oracle database is updated?
From a monitoring perspective (not intended to find previous changes), you have several options including but not limited to triggers, streams, and a column with a default value of sysdate. A trigger will allow you to execute a bit of programming logic (stored directly in the trigger or in an external database object) whenever the record changes (insert, update, delete). Streams can be used to track changes by monitoring the redo logs. One of the easiest may be to add a date column with a default value of sysdate.
Are you talking about within a transaction or outside of it?
Within our program we can use things like SQL%ROWCOUNT to see whether our DML succeeded...
SQL> set serveroutput on size unlimited
SQL> begin
2 update emp
3 set job = 'SALESMAN', COMM=10
4 where empno = 8083;
5 dbms_output.put_line('Number of records updated = '||sql%rowcount);
6 end;
7 /
Number of records updated = 1
PL/SQL procedure successfully completed.
SQL>
Alternatively we might test for SQL%FOUND (or SQL%NOTFOUND).
From outside the transaction we can monitor ORA_ROWSCN to see whether a record has changed.
SQL> select ora_rowscn from emp
2 where empno = 8083
3 /
ORA_ROWSCN
----------
83828715
SQL> update emp
2 set comm = 25
3 where empno = 8083
4 /
1 row updated.
SQL> commit
2 /
Commit complete.
SQL> select ora_rowscn from emp
2 where empno = 8083
3 /
ORA_ROWSCN
----------
83828780
SQL>
By default ORA_ROWSCN is set at the block level. If you want to track it at the lower level your need to create the table with the ROWDEPENCIES keyword.
These are ad hoc solutions. If you want to proactive monitoring then you need to implementing some form of logging. Using triggers to write log records is a common solution. If you have Enterprise Edition you should consider using Fine Grained Auditing: Dan Morgan's library has a useful demo of how to use FGA to track changes.
You can see if a table definition has change by querying the last_ddl_time from the user_objects view.
Without using triggers or materialized logs (which would be a total hack) there is no way I know of to see when any particular row in a table has been updated.

Resources