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.
Related
I have 5 rows in table. And some of the rows are locked in some sessions .
I don't want to generate any error, Just want to wait until any row will become free for further processing
I tired with nowait and skip locked:-
nowait , But there is a problem with nowait. query has written in cursor , when I used "nowait" under cursor , query will return null and control will go out with an error by saying- resource busy
I tried with skip locked with for update-But if table contain 5 rows and all
5 rows are locked then it is giving error.
CURSOR cur_name_test IS
SELECT def.id , def.name
FROM def_map def
WHERE def.id = In_id
FOR UPDATE skip locked;
Why don't use select for update only ? The below is being test locally in plsql developer.
in first sesssion I do the below
SELECT id , name
FROM ex_employee
FOR UPDATE;
in second session i run the below however it hang.
SET serveroutput ON size 2000
/
begin
declare curSOR cur_name_test IS
SELECT id , name
FROM ex_employee
WHERE id = 1
FOR UPDATE ;
begin
for i in cur_name_test loop
dbms_output.put_line('inside cursor');
end loop;
end;
end;
/
commit
/
when I commit in the first session , the lock will be released and the second session will do its work. i guess that you want , infinite wait.
However such locking mechanism (pessimistic locking) can lead to deadlocks if its not managed correctly and carefully ( first session waiting second session , and second session waiting first session).
As for the nowait its normal to have error resource busy, because you are saying for the query don't wait if there are locking. you can instead wait 30, which will wait 30 second then output error but thats not what you want(I guess).
As for skip locked, the select will skip the locked data for example you have 5 rows , and one of them is locked then the select will not read this row . thats why when all the data is locked its throwing error because nothing can be skipped. and I guess is not what you want in your scenario.
This sounds like you need to think about transaction control.
If you are doing work in a transaction then the implication is that that unit of work needs to complete in order to be valid.
What you are saying is that some of the work in my update transaction doesn't need to complete in order for the transaction to be committed.
Not only that but you have two transactions running at the same time performing operations against the same object. In itself that may be valid but
if it is then you really need to go back to the first sentence and think hard about transaction control and process flow and see if there's a way you can have the second transaction only attempt to update rows that aren't being updated in the first transaction.
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.
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.
In which case do we need to use for update nowait in cursors.
Using for update nowait will cause the rows to be busy and acquires a lock until a commit or rollback is executed.
Any other session that tries to acquire a lock will get an Oracle error message like ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired instead of waiting the lock to release.
Session1:
CURSOR abc_cur
IS
select * from dept where deptno =10 for update nowait;
Here the rows are locked until the cursor is closed or a commit/rollback gets executed. If, meanwhile, another user from session 2 tries to access the same records then this will throw an error as shown below:
Session2:
select * from dept where deptno =10 for update nowait;
This user cannot even update or delete the same records that have been locked by the first session.
ERROR at line 1:
`ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired`
Usage:
Now if you want to do some manipulation on certain sets of records and you don't want another user from another session to override your data then you must first lock the records(using for update nowait) and then do your manipulation. After you're done with your manipulation, close the cursor and commit.
EDIT
Suppose there are 10 rows in temp and I execute the following script in my session 1 :
declare
cursor abc is select * from temp for update nowait;
temp abc%rowtype;
begin
open abc;
-- do slow stuff here
close abc;
commit;
end;
In session 2, I execute the following while the script in session 1 is still running
select * from temp;
10 rows found
If I execute the same script, in session 2, while the script in session 1 is still running
declare
cursor abc is select * from temp for update nowait;
temp abc%rowtype;
begin
open abc;
-- do slow stuff here
close abc;
commit;
end;
Then I get ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired instead of waiting the lock to release.
Using for update without the nowait clause. will do the following:
check if the record in question (or records in question) are locked
if they are locked then wait until they are no longer locked
lock the records with a new lock that will keep records locked until a commit or rollback is executed.
Using for update with the nowait clause. will do the following:
check if the record in question (or records in question) are locked
if they are locked then do not wait, but exit with an error message
if the records are not locked then lock the records with a new lock that will keep records locked until a commit or rollback is executed.
The examples in the previous answer would also work if session 1 gets a lock for update without the nowait clause, while session 2 uses the nowait clause.
Hope that helps.
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.