Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Alberto.

Asked: July 31, 2004 - 2:43 pm UTC

Last updated: May 08, 2025 - 12:24 pm UTC

Version: 9.2.0.5

Viewed 1000+ times

You Asked

Hi Tom,

Oracle puts a TM lock on a table even if no rows are affected by a DML statement:

TX_A>create table t (x int);

Table created.

-- just to show there are no locks on the table:

TX_A>alter table t move;

Table altered.

-- in another session (marked as TX_B) we now issue a statement which affects no rows:

TX_B>delete from t;

0 rows deleted.

-- now there's a TM lock:

TX_A>alter table t move;
alter table t move
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified

TX_B>select type, lmode,
2 decode(lmode,2,'row-share',3,'row-exclusive'),
3 decode(type,'TM',(select object_name from dba_objects where object_id=id1)) name
4 from v$lock
5 where sid = (select sid from v$mystat where rownum=1);

TYPE LMODE DECODE(LMODE,2,'ROW-SHARE',3,'ROW-EXCLU NAME
------ ---------- --------------------------------------- --------------------
TM 3 row-exclusive T

TX_B>commit;

Commit complete.

TX_A>alter table t move;

Table altered.

It seems to me a bit counter-intuitive (which doesn't mean that it's "bad", of course) to retain an unnecessary lock (even a "weak" row-exclusive one) on a not-modified table ... so there's probably a reason that I can't see.

Do you happen to know a strong reason for this behaviour - or shall I classify it as "just a quirk" ?

--

For sure it's something to remember while coding - ie think about a code such as this:

delete from t where ...
if (sql%rowcount > 0) then
log deletions in logging table
commit;
end if;

This would leave the tm lock on the table preventing DDL possibly forever - the commit has to be moved after the if block, most definitely.

Thanks
Alberto

and Tom said...

the tm lock is taken out before the delete even starts to happen -- in order for you to be able to modify the table -- DDL and such must be

a) verified to not be happening, the gaining of the TM lock would ascertain that
b) prevented from starting, the gaining of the TM lock does that.


It seems intuitive to me that the "delete from t where" must be committed regardless of the number of rows affected -- you've begun a transaction, the delete started a transaction for you no matter what.


Imagine the multi-statement transaction:

delete from t;
insert into t select ...; <<== what do you mean t was dropped? how could T go away here...




Rating

  (7 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

thanks, and a related question on tx starting

Alberto Dell'Era, July 31, 2004 - 6:41 pm UTC

Thanks for the interesting insights - i didn't think about the multi-statement tx; yes, that's for sure a strong reason to keep the tm lock.

About the tx start, i take this as "started logically", since physically it seems that it doesn't start:

TX_A>create table t (x int);

Table created.

TX_B>delete from t;

0 rows deleted.

TX_A>select XIDUSN from v$transaction;

no rows selected

TX_B>insert into t(x) values (1);

1 row created.

TX_A>select XIDUSN from v$transaction;

XIDUSN
----------
1

So: is it correct that the transaction didn't start physically, or it's the other way around: the tx started and got recorded in the kernel, and it is just a quirk of v$transaction that doesn't "see" the started tx ?

[I know that v$transaction is mostly a view on the rbs headers, and since the nothing-deleting delete doesn't create undo, perhaps v$transaction is blind here].

Thanks
Alberto

Tom Kyte
July 31, 2004 - 7:01 pm UTC

the transaction has for all intents and purposes begun with the delete statement. That there wasn't a physical transaction id assigned is an optimization (just like issuing a commit when nothing need be done is optimized away as a "no-op")

ok, thanks

Alberto Dell'Era, July 31, 2004 - 7:07 pm UTC


about tx lock

gaoyf, September 26, 2005 - 5:12 am UTC

hello,Tom
I have two session.
session 1:delete from t where ...
session 2:
LOOP
BEGIN
select ... from t for update nowait
EXIT;
EXCEPTION
WHEN busy THEN
NULL;
dbms_lock.sleep(5);
END;
END LOOP;


when session 1 and 2 run at the sames time,session 1 is blocked and never unlocked because of TX lock hold by session 2;session 2 is running never end because of TX lock hold by session 1.
The oracle database unlock a TX lock only when the blocker tracsaction end ?
In the sample,session 2 get resurce busy and relases the TM lock in table t ,but session 1 always blocked by session2,because the transcation of session 2 is not end.
Why oracle not detect a TM relase for locked object of blocker process to unlock the blocked process ?
This may improve concurrent performance?

Thanks a bunch!
This is a useful site in the world!

Tom Kyte
September 26, 2005 - 9:18 am UTC

Oracle blocks at the transaction level, when the delete gets blocked, it gets blocked on the TX of session 2, until session 2 commits or rolls back - it will be blocked.

This is the way it works.

TX lock

A reader, September 26, 2005 - 11:08 pm UTC

oracle unlock a session at transaction level,not at locked objects level?Can you tell me why ?
oracle do it this way,just 'like it' or some technical reason or need to promote ?
In the above sample,session 2 release TM lock on table t because of resource busy,the table t only locked by session 1,but it blocked too.This is not a deadlock in oracle,but a deadlock in deed.
In session2 ,it locks manay many rows in many tables to process,Now I must lock one by one by 'select ... from ... for update nowait' ,if any busy,must rollback and lock again from beginning.Can you give me a better method?
Thanks!

Tom Kyte
September 27, 2005 - 10:43 am UTC

because we do not have a list of row locks anywhere, that would not be a very scalable solution - the row locks are attributes of the data. When you commit, we do not revisit every single row you updated to "unlock it", we rather remove the transaction entry.

Hence, when you get blocked - you get blocked on a transaction (not a session, not a row - a transaction). when that transaction is finished, you become unblocked. If you just hung out and waited for the status on the row to change, you might wait for a long long long time...

It is not a deadlock indeed, if it were, you would get an ora-60. One of your sessions is alive, well, and processing.

Seems your session trying to lock many many rows in many many tables needs to either

a) lock the tables - rolling back if it cannot
b) try to lock many many many rows in many many tables - rolling back if it cannot.



about TX lock

gaoyf, September 27, 2005 - 9:47 pm UTC

I do not say row locks,but say:can oracle detect TM lock releasing of blocker session to unlock blocked session ?
In sample,session 2 release all row locks and TM lock on table t because of resource busy(you lost TM locks on table,you lost all row locks on table,oracle can detect TM lock releasing ,ok?),session 1 can run because of no blocked resource in deed ?
oracle use only TX lock releasing to unlock blocked session,i think thier must be some technical reason,or why not use TM lock releasing on critical table of blocker session?

In my sample,session 1 is blocked forever,session 2 is run run run and never end forever,this is just not a deadlock and ora-60 in oracle.deadlock or deadloop or dead????, a symbol ? I have not a right word to describe it,maybe deadlockloop ? Just for fun.
Thanks very much.

Tom Kyte
September 28, 2005 - 9:17 am UTC

Looking at your application design, this is something such that the odds of the select for update ever succeeding would be near zero - very near zero - over time.

We block on the transaction - not on the block, row, segment - but the transaction.

If you used "wait", you would have a real deadlock, but because you use nowait intentionally, you don't have a true deadlock, you have an application issue, but not a deadlock in this case.

Don't know what else to tell you other than "that application logic needs to be looked at"

Why do you need the select for updates in this case? What is the real logic behind the logic - the "goal"

To know the time when a table was created or modified

A reader, January 08, 2006 - 10:38 pm UTC

Hi Tom,

How to know the time when a table was created or modified in Oracle ? By modified I mean the time when an additional column was added to the table.

Tom Kyte
January 09, 2006 - 7:58 am UTC

there is a created and last_ddl_time in the dictionary.


ops$tkyte@ORA10GR2> create table t ( x int );

Table created.

ops$tkyte@ORA10GR2> exec dbms_lock.sleep(5)

PL/SQL procedure successfully completed.

ops$tkyte@ORA10GR2> alter table t add y int;

Table altered.

ops$tkyte@ORA10GR2> select created, last_ddl_time from user_objects where object_name = 'T';

CREATED              LAST_DDL_TIME
-------------------- --------------------
09-jan-2006 07:52:10 09-jan-2006 07:52:15
 

Avoiding TM lock

Zilvinas, May 08, 2025 - 7:30 am UTC

People do things like this to avoid TM lock

select count(1) into c from my_table where ...
if c>0 then
update my_table where ...
end if;

Does that make any sence???
How often DDLs are done in production environment ???
Is that TM lock so costly? It is only for DDL so no blcoking happens for DML.
Also this way you have two statements instead of one what is slower.
Besides there is possibility that the table is updated by another transaction between select and update statements.
Correct way would be to put "for update" in select statement, but then TM lock is aquired.
I think it is bad practice. Am I right?
Chris Saxon
May 08, 2025 - 12:24 pm UTC

You're right - counting the rows before doing update/delete is a bad idea for the reasons you've stated.

Using select for update to lock the rows first has a couple other advantages:

- You can use the (no)wait clause to time out if you can't get the lock
- It may be useful/necessary to avoid deadlock in transactions with many update/deletes to ensure rows and tables are locked in a specified order