Introduction Prior to Oracle database 12c, dropping an index was always an EXCLUSIVE (offline) operation, which required locking the base table in exclusive mode. This sometimes cause the resource busy wait while the base table is already locked for DML operations and the transactions are not yet committed. Further, when the table is locked in exclusive mode for the index drop operation, no DML is allowed on the base table until the index drop operation is completed. This may not be a problem for small indexes. However, when we are dropping a huge index, it will eventually block all DML on the base table for a higher duration which is sometimes not desired. Oracle 12c has overcome this limitation of dropping index. Dropping an index no longer requires an exclusive lock (if specified) on the base table. With Oracle database 12c, we have the option of dropping an index ONLINE which allows DML on the base table while the drop index operation is running. Lets go through a quick demonstration to validate this new feature. Drop Index in Oracle 11g (Offline) Lets create a table in a Oracle 11g database for the demonstration ----// ----// query database version //---- ----// SQL> select version from v$instance; VERSION ----------------- 11.2.0.1.0 ----// ----// create table T_DROP_IDX_11G for demonstration //---- ----// SQL> create table T_DROP_IDX_11G 2 ( 3 id number, 4 name varchar(15), 5 join_date date 6 ); Table created. ----// ----// populate table T_DROP_IDX_11G with dummy data //---- ----// SQL> insert /*+ APPEND */ into T_DROP_IDX_11G 2 select rownum, rpad('X',15,'X'), sysdate 3 from dual connect by rownum commit; Commit complete. Lets create an index on this table ----// ----// create index IDX_T_DROP_IDX_11G on table T_DROP_IDX_11G //---- ----// SQL> create index IDX_T_DROP_IDX_11G on T_DROP_IDX_11G (id, name); Index created. Now, lets perform DML (update a record) in this table without committing the transaction ----// ----// update a record in table T_DROP_IDX_11G //---- ----// SQL> SELECT sys_context('USERENV', 'SID') SID FROM DUAL; SID ---------- 20 SQL> update T_DROP_IDX_11G set name='ABBAS' where id=100; 1 row updated. ----// ----// leave the transaction uncommitted in this session //---- ----// If we query the v$locked_object view, we can see the base table is locked in row exclusive (mode=3) mode by the previous update operation which we haven't yet committed. ----// ----// query v$locked_object to check the locked object //---- ----// SQL> select object_id,session_id,locked_mode from v$locked_object; OBJECT_ID SESSION_ID LOCKED_MODE ---------- ---------- ----------- 73451 20 3 SQL> select object_name,object_type from dba_objects where object_id=73451; OBJECT_NAME OBJECT_TYPE ------------------------- ------------------- T_DROP_IDX_11G TABLE Now, from another session; lets try to drop the index (IDX_T_DROP_IDX_11G) that we had created on this table (T_DROP_IDX_11G). - ---// ----// try to drop the index IDX_T_DROP_IDX_11G from another session //---- ----// 01:17:07 SQL> drop index IDX_T_DROP_IDX_11G; drop index IDX_T_DROP_IDX_11G * ERROR at line 1: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired 01:17:10 SQL> drop index IDX_T_DROP_IDX_11G; drop index IDX_T_DROP_IDX_11G * ERROR at line 1: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired 01:17:12 SQL> drop index IDX_T_DROP_IDX_11G; drop index IDX_T_DROP_IDX_11G * ERROR at line 1: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired Index drop operation is failing with resource busy wait. This is because when we try to drop an index, Oracle tries to acquire an exclusive lock on the base table and if it fails to acquire that exclusive lock, it throws this resource busy wait. If we check the 10704 trace for this drop index operation, we can see Oracle tried to acquire a exclusive lock (mode=6) on table T_DROP_IDX_11G and failed (ksqgtl: RETURNS 51) with resource busy wait error (err=54) #----// #----// lock trace for the drop index operation //---- #----// PARSING IN CURSOR #3 len=69 dep=1 uid=85 oct=26 lid=85 tim=1443857735568850 hv=114407125 ad='8ab5b250' sqlid='04zx1kw3d3dqp' LOCK TABLE FOR INDEX "IDX_T_DROP_IDX_11G" IN EXCLUSIVE MODE NOWAIT END OF STMT PARSE #3:c=5999,e=5962,p=0,cr=59,cu=0,mis=1,r=0,dep=1,og=1,plh=0,tim=1443857735568850 ksqgtl *** TM-00011eeb-00000000 mode=6 flags=0x401 timeout=0 *** ksqgtl: xcb=0x8f92aa68, ktcdix=2147483647, topxcb=0x8f92aa68 ktcipt(topxcb)=0x0 ksucti: init session DID from txn DID: ksqgtl: ksqlkdid: 0001-0017-000000FD *** ksudidTrace: ksqgtl ktcmydid(): 0001-0017-000000FD ksusesdi: 0000-0000-00000000 ksusetxn: 0001-0017-000000FD ksqcmi: TM,11eeb,0 mode=6 timeout=0 ksqcmi: returns 51 ksqgtl: RETURNS 51 ksqrcl: returns 0 EXEC #3:c=0,e=67,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=0,tim=1443857735568937 ERROR #3:err=54 tim=1443857735568943 CLOSE #3:c=0,e=3,dep=1,type=0,tim=1443857735568967 In this trace file, the object ID is represented in hexadecimal (00011eeb), which can be mapped to object ID as follows. ----// ----// finding object details based on hexadecimal object ID //---- ----// SQL> select object_id,to_char(object_id,'0XXXXXXX') object_hex,object_name,object_type 2 from dba_objects where object_name='T_DROP_IDX_11G'; OBJECT_ID OBJECT_HE OBJECT_NAME OBJECT_TYPE ---------- --------- ------------------------- ------------------- 73451 00011EEB T_DROP_IDX_11G TABLE We will not be allowed to drop the index unless Oracle acquires a exclusive lock (mode=6) on the base table. We can commit/rollback the transaction in the first session (sid=20) which will release the row exclusive lock (mode=3) from the table and will allow Oracle to acquire a exclusive lock on the base table and in turn process the DROP INDEX operation. Drop Index in Oracle 12c (Online) Now lets see, how the drop index operation behaves in Oracle 12c. Lets quickly create a table for our demonstration ----// ----// query database version //---- ----// SQL> select version from v$instance; VERSION ----------------- 12.1.0.2.0 ----// ----// create table T_DROP_IDX_12C for demonstration //---- ----// SQL> create table T_DROP_IDX_12C 2 ( 3 id number, 4 name varchar(15), 5 join_date date 6 ); Table created. ----// ----// populate table T_DROP_IDX_11G with dummy data //---- ----// SQL> insert /*+ APPEND */ into T_DROP_IDX_12C 2 select rownum, rpad('X',15,'X'), sysdate 3 from dual connect by rownum commit; Commit complete. Lets create an index on this table. ----// ----// create index IDX_T_DROP_IDX_12C on table T_DROP_IDX_12C //---- ----// SQL> create index IDX_T_DROP_IDX_12C on T_DROP_IDX_12C (id, name); Index created. Now lets perform DML on this table and leave them uncommitted. ----// ----// perform few DML on the table T_DROP_IDX_12C //--- ----// SQL> SELECT sys_context('USERENV', 'SID') SID FROM DUAL; SID ---------- 20 SQL> insert into T_DROP_IDX_12C values (1000001,'Abbas',sysdate); 1 row created. SQL> update T_DROP_IDX_12C set name='Fazal' where id=100; 1 row updated. ----// ----// leave the transactions uncommitted in this session //---- ----// If we query the v$locked_object view, we can see the base table is locked in row exclusive (mode=3) mode by the previous update operation which we haven't yet committed. ----// ----// query v$locked_object to check the locked object //---- ----// SQL> select object_id,session_id,locked_mode from v$locked_object; OBJECT_ID SESSION_ID LOCKED_MODE ---------- ---------- ----------- 20254 20 3 SQL> select object_name,object_type from dba_objects where object_id=20254; OBJECT_NAME OBJECT_TYPE ------------------------- ----------------------- T_DROP_IDX_12C TABLE Now, from another session; lets try to drop the index (IDX_T_DROP_IDX_12C) that we had created on this table (T_DROP_IDX_12C). ----// ----// try to drop the index IDX_T_DROP_IDX_11G from another session //---- ----// SQL> SELECT sys_context('USERENV', 'SID') SID FROM DUAL; SID ---------- 127 SQL> drop index IDX_T_DROP_IDX_12C; drop index IDX_T_DROP_IDX_12C * ERROR at line 1: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired We are still getting the same resource busy wait that we had received in Oracle database 11g. This is because the normal DROP INDEX operation still tries to acquire a exclusive (mode=6) lock on the base table while dropping an index. Here comes the new feature, the ONLINE option of DROP INDEX. With Oracle 12c, we can add the clause ONLINE while using DROP INDEX command. Lets try to drop the index ONLINE (we haven't yet committed the DMLs on the other session ). ----// ----// try to drop (ONLINE) the index IDX_T_DROP_IDX_11G from another session //---- ----// SQL> SELECT sys_context('USERENV', 'SID') SID FROM DUAL; SID ---------- 127 SQL> drop index IDX_T_DROP_IDX_12C online; ----// ----// drop index hangs here //---- ----// We no longer get the resource busy error (ORA-0054) here. However, the drop index operation just hangs as it is waiting for the DML operations to commit and release the lock (enqueue) acquired at row level. If we review the 10704 lock trace, we can see Oracle has acquired a shared lock (mode=2) on the base table and is waiting to acquire a shared transactional lock (TX: enqueue) which is currently blocked by the row exclusive lock mode from the first session (sid=20) #----// #----// lock trace for the drop index online operation //---- #----// PARSING IN CURSOR #47298660689800 len=69 dep=1 uid=63 oct=26 lid=63 tim=1443861257910293 hv=412402270 ad='7f878298' sqlid='6kxaujwc99hky' LOCK TABLE FOR INDEX "IDX_T_DROP_IDX_12C" IN ROW SHARE MODE NOWAIT END OF STMT PARSE #47298660689800:c=4999,e=6590,p=1,cr=8,cu=0,mis=1,r=0,dep=1,og=1,plh=0,tim=1443861257910293 ksqgtl *** TM-00004F1E-00000000-00000003-00000000 mode=2 flags=0x400 timeout=0 *** ksqgtl: xcb=0x88034da8, ktcdix=2147483647, topxcb=0x88034da8 ktcipt(topxcb)=0x0 ksucti: init session DID from txn DID: 0001-0029-00000094 ksqgtl: ksqlkdid: 0001-0029-00000094 *** ksudidTrace: ksqgtl ktcmydid(): 0001-0029-00000094 ksusesdi: 0000-0000-00000000 ksusetxn: 0001-0029-00000094 ksqgtl: RETURNS 0 EXEC #47298660689800:c=0,e=46,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=0,tim=1443861257910358 CLOSE #47298660689800:c=0,e=1,dep=1,type=0,tim=1443861257910374 .. .. ( output trimmed) .. #----// #----// waiting to acquire a shared transactional lock //---- #----// ksqgtl *** TX-0001001E-00000439-00000000-00000000 mode=4 flags=0x10001 timeout=21474836 *** ksqgtl: xcb=0x88034da8, ktcdix=2147483647, topxcb=0x88034da8 ktcipt(topxcb)=0x0 ksucti: init session DID from txn DID: 0001-0029-00000094 ksqgtl: ksqlkdid: 0001-0029-00000094 *** ksudidTrace: ksqgtl ktcmydid(): 0001-0029-00000094 ksusesdi: 0000-0000-00000000 ksusetxn: 0001-0029-00000094 ksqcmi: TX-0001001E-00000439-00000000-00000000 mode=4 timeout=21474836 We can also verify from dba_waiters and v$lock views that the DROP INDEX ONLINE operation is waiting to acquire a transactional shared lock (TX:enqueue) which is blocked by the row exclusive (mode=3) lock and in turn by exclusive transactional lock (TX:mode=6) from the first session (sid=20) ----// ----// query dba_waiters to check who is holding the transactional lock on base table //---- ----// SQL> select waiting_session,holding_session,lock_type,mode_held,mode_requested from dba_waiters; WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUE --------------- --------------- ------------ ------------ ---------- 127 20 Transaction Exclusive Share ----// ----// query v$lock to find out the lock mode held by the holding session //---- ----// SQL> select * from v$lock where sid=20; ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK CON_ID ---------------- ---------------- ---------- -- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 000000008A67B980 000000008A67B9F8 20 AE 133 0 4 0 8500 0 3 00000000885703C8 0000000088570448 20 TX 327706 994 6 0 123 1 0 00002B78ACA00EA8 00002B78ACA00F10 20 TM 20254 0 3 0 123 0 3 ----// ----// query v$lock to find out the lock mode requested by waiting session //--- ----// SQL> select * from v$lock where sid=127; ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK CON_ID ---------------- ---------------- ---------- -- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 000000008A67F840 000000008A67F8B8 127 AE 133 0 4 0 845 0 3 000000008857EE28 000000008857EEA8 127 TX 524294 982 6 0 4 0 0 000000008A67F628 000000008A67F6A0 127 TX 327706 994 0 4 4 0 0 00002B78AD08AA08 00002B78AD08AA70 127 TM 20254 0 2 0 4 0 3 000000008A67F228 000000008A67F2A0 127 OD 20275 0 6 0 4 0 3 000000008A67E5F8 000000008A67E670 127 OD 20254 0 4 0 4 0 3 Although the drop index (online) operation hangs (waiting for DMLs to release row exclusive locks and exclusive TX lock), it will not block any new DMLs which get executed against the base table. We can confirm this by running a new DML from a new session as show below. ----// ----// perform new DML when drop index (ONLINE) is running ----// SQL> SELECT sys_context('USERENV', 'SID') SID FROM DUAL; SID ---------- 23 SQL> delete from T_DROP_IDX_12C where id=300; 1 row deleted. ----// ----// new DML are able to acquire RX (mode=3) lock on the table //---- ----// SQL> select object_id,session_id,locked_mode from v$locked_object; OBJECT_ID SESSION_ID LOCKED_MODE ---------- ---------- ----------- 20254 20 3 --> lock held by first session where we performed DML and left uncommitted 20254 23 3 --> lock held by this session to perform delete operation 20254 127 2 --> session from where we have executed drop index (hangs and waiting for DMLs to commit) SQL> commit; Commit complete. ----// ----// lock released by current session upon commit //---- ----// SQL> select object_id,session_id,locked_mode from v$locked_object; OBJECT_ID SESSION_ID LOCKED_MODE ---------- ---------- ----------- 20254 20 3 20254 127 2 As we can see, even through DROP INDEX ONLINE hangs waiting for DMLs to commit; it doesn't block any new DML on the base table. The DROP INDEX ONLINE operation will eventually get completed once the pending transactions are committed. Lets commit the uncommitted transactions from our first session (sid=20). ----// ----// commit pending transactions from first session //---- ----// SQL> SELECT sys_context('USERENV', 'SID') SID FROM DUAL; SID ---------- 20 SQL> commit; Commit complete. Lets check the status of DROP INDEX ONLINE operation (which was hung on other session) ----// ----// check the status of hanged drop index operation //---- ----// SQL> drop index IDX_T_DROP_IDX_12C online; Index dropped. SQL> SELECT sys_context('USERENV', 'SID') SID FROM DUAL; SID ---------- 127 The moment, pending transactions are committed the DROP INDEX ONLINE operation was resumed and completed automatically as the Row Exclusive (RX:mode=3) locks and TX locks (TX:mode=6) were released from the table (rows) and the DROP INDEX ONLINE was able to acquire a shared transactional lock (mode=4) on the table rows. We can also verify from lock trace that DROP INDEX ONLINE operation was able to acquire (ksqgtl: RETURNS 0) a shared transactional lock (TX:mode=4) once the DMLs were committed in first session (sid=20) #----// #----// drop index acquired shared transactional lock upon commit of pending DML on base table //---- #----// ksqgtl *** TX-0001001E-00000439-00000000-00000000 mode=4 flags=0x10001 timeout=21474836 *** ksqgtl: xcb=0x88034da8, ktcdix=2147483647, topxcb=0x88034da8 ktcipt(topxcb)=0x0 ksucti: init session DID from txn DID: 0001-0029-00000094 ksqgtl: ksqlkdid: 0001-0029-00000094 *** ksudidTrace: ksqgtl ktcmydid(): 0001-0029-00000094 ksusesdi: 0000-0000-00000000 ksusetxn: 0001-0029-00000094 ksqcmi: TX-0001001E-00000439-00000000-00000000 mode=4 timeout=21474836 ksqcmi: returns 0 *** 2015-10-03 14:30:01.164 ksqgtl: RETURNS 0 Conclusion Oracle has made significant improvements in the locking mechanism involved with the DROP INDEX operation by introducing the ONLINE feature, which now just needs a shared lock to be acquired on the base table to start with drop operation; allowing DMLs to be executed against the base table during the index drop operation. Online index drop operation can start without causing any exclusive lock. However, the drop operation will not complete unless all the uncommitted transactions are committed in the base table and the drop operation is able to acquire a shared transactional (TX:mode=4) lock. Reference I have used the term lock mode and used tracing to identify the locks at different places through out this article. You can refer the following article by Franck Pachot to get a fair idea about the lock modes and what those values mean and how to trace the locks. http://blog.dbi-services.com/investigating-oracle-lock-issues-with-event-10704/
↧