Tuesday, May 29, 2012

Interested Transaction List ( ITL ) in Oracle

Few days back, I received an e-mail from a reader , who is having doubt on locking mechanism   i.e, related to  ITL  . This post will give some good concept about the ITL .  Let's start from the beginning i.e, from the transaction .

Transaction :  A transaction starts when an update to data such as insert, update or delete occurs (or the intention to do so, e.g. SELECT FOR UPDATE) and ends when the session issues a commit or rollback. Like everything else, a specific transaction should have a name or an identifier to differentiate it from another one of the same type. Each transaction is given a transaction ID.

Locks :  In multi-user systems, many users may update the same information at the same time. Locking allows only one user to update a particular data at a time ,  another person cannot modify the same data. When any user start the transaction then oracle automatically locks the data . Oracle uses a different locking method then most other databases, Oracle locking policy consists of the following :
  • Oracle locks data at the row level on modification only. There is no lock escalation to a block or table level, ever.
  • Oracle never locks data just to read it. There are no locks placed on rows of data by simple reads.
  • A writer of data does not block a reader of data .
  • A writer of data is blocked only when another writer of data has already locked the row it was going after.

 What is Interested Transaction List (ITL) ? 
It is a simple data structure called  "Interested Transaction List" (ITL), a list that maintains information on transaction. The transaction identifier will be stored as an entry in the ITL in the header of the data block. The ITL contains several placeholders (or slots) for transactions. When a row in the block is locked for the first time, the transaction places a lock in one of the slots. In other words, the transaction makes it known that it is interested in some rows (hence the term "Interested Transaction List"). 

When a different transaction locks another set of rows in the same block, that information is stored in another slot and so on. When a transaction ends after a commit or a rollback, the locks are released and the slot which was used to mark the row locks in the block is now considered free .

ITL slots are required for every transaction, it contains the transaction ID (XID) which is a pointer to an entry in the transaction table of a rollback segment. We can still read the data but other processes wanting to change the data must wait until the lock is released (commit or rollback). The ITL entry contains a XID, undo byes address (UBA) information, flags indicating the transaction status (Flag) and lock count (Lck) showing the number of rows locked by this transaction within the block and SCN at which the transaction is updated.

How many slots are typically available in ITL ?
During the table creation, the INITRANS parameter defines how many slots are initially created in the ITL. INITRANS is a block level storage parameter which can be specified while creating an object (table). INITRANS and MAXTRANS parameters are used to control the concurrent access to the same block  . When the transactions exhaust all the available slots and a new transaction comes in to lock a row, the ITL grows to create another slot. The ITL can grow up to the number defined by the MAXTRANS parameter of the table, provided there is space in the block. Nevertheless, if there is no more room in the block, even if the MAXTRANS is high enough, the ITL cannot grow. the maximum value for MAXTRANS parameter is 255.

Each ITL entry in the block transaction variable header takes 24 bytes. Though a block can have a maximum of 255 different ITL entries, the ITL allocation is limited by block size.  The database block size plays an important role when allocating the number of inital ITL entries for the block.  The rule is "the total size allocated for initial ITLs SHOULD be LESS THAN 50% of the database block size" .
i.e,   sizeof(INITIAL ITLs) < ( 50% of the DATABASE BLOCK SIZE )

BLOCK SIZE(B)                           NO OF ITL entries allocated in block header
============                      =============================
2048                                                               41
4096                                                               83
8192                                                              169

What happens when a transaction does not find a free slot to place its lock information? 
This can occur because either
1.)  the block is so packed that the ITL cannot grow to create a free slot, or 
2.)  the MAXTRANS has already been reached. In this case, the transaction that needs to lock a row has to wait until a slot becomes available. This wait is termed as ITL waits and can be seen from the view v$session_wait, in which the session is waiting on an event named "enqueue"  Since the INITRANS is one, there is only one slot for the ITL. The rest of the block is empty. 

How to Diagnose the ITL Wait   How do we know that a segment is experiencing ITL waits? 
To check for ITL waits, set up the STATISTICS_LEVEL to TYPICAL in init.ora or via ALTER SYSTEM, then examine the segment statistics for the waits.
SQL>select owner ,object_name  from v$segment_statistics where statistic_name='ITL waits' and value> 0 ;

When we suspect that a database is suffering from these waits, query the view v$session_wait. If the event on which the system is waiting is "enqueue," then the session might be experiencing ITL waits. However, enqueue is a very broad event that encompasses any type of locks, so it does not accurately specify the ITL waits. When the wait event is experienced, issue the following complex query a table tab1 (say) 

SQL> Select s.sid SID, s.serial# Serial#, l.type type, ' ' object_name, lmode held, request request
from  v$lock l,  v$session s,  v$process p
where s.sid = l.sid and   s.username <> ' ' and   s.paddr = p.addr and l.type <> 'TM' and
(l.type <> 'TX' or l.type = 'TX' and l.lmode <> 6)  
select s.sid SID, s.serial# Serial#,  l.type type, object_name object_name, lmode held, request request
from v$lock l, v$session s, v$process p, sys.dba_objects o
where s.sid = l.sid and o.object_id = l.id1 and l.type = 'TM' and  s.username <> ' ' and  s.paddr = p.addr
select s.sid SID, s.serial# Serial#,l.type type, '(Rollback='||rtrim(r.name)||')' object_name, lmode held,
request request
from v$lock l, v$session s, v$process p, v$rollname r
where s.sid = l.sid and l.type = 'TX' and l.lmode = 6 and  trunc(l.id1/65536) = r.usn and s.username <> ' ' and  s.paddr = p.addr order by 5, 6 ; 
The output of the query will look something like this:
SID          SERIAL#     TY        OBJECT_NAME          HELD     REQUEST
-----        ----------     -------    -------------------         -------     -------------
36            8428         TX                 0                         4
36            8428         TM                 TAB1                   3              0
52           29592        TM                 TAB1                   3              0
52           29592        TX         (Rollback=RBS1_6)      6              0 

Note how the sessions 36 and 52 both have a TM (DML) lock on the table TAB1 of type 3 (Row Exclusive), but session 52 also holds a TX (Transaction) lock on the rollback segment of mode 6 (Exclusive) and Session 36 is waiting for a mode 4 (Share) lock. If this combination of locking occurs, we can be sure that session 36 is waiting for ITL on the table TAB1. Beware of a similar but different diagnosis when two sessions try to insert the same key value (a real locking – primary key violation). In that case, we would also see an additional TX lock on a rollback segment from the session that is waiting; for ITL waits, this TX lock on the rollback segment would not be seen. To know more about working of ITL click here

Examining ITL allocation :
1.)  Create a table with INITRANS 4.
SQL> create table test1 (I  number)  INITRANS 4 ; 
2.) Insert few  record for testing purpose. A blockdump can be taken later. 
SQL> insert into test1 values (10) ; 
SQL> commit ;
now updated the table from sessions too these transaction are in hang state .

3.) Find out the block number and the file id for dumping the block :
SQL> select dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) from test;
or use the below command 
SQL> select header_file,header_block from dba_segments where owner='HR' and segment_name='TEST' ; 

4.) Dump the data block:
SQL> alter system dump datafile 4 block min 636  block max 636 ;

5.) Open the dump trace file located in USER_DUMP_DEST directory and check the following:
Block dump from disk:
buffer tsn: 4 rdba: 0x0100027c (4/636)
scn: 0x0000.002a1814 seq: 0x02 flg: 0x04 tail: 0x18140602
frmt: 0x02 chkval: 0x1561 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Block header dump:  0x0100027c
Object id on Block? Y
seg/obj: 0x1277b  csc: 0x00.2a1372  itc: 10  flg: E  typ: 1 - DATA
 brn: 0  bdba: 0x1000278 ver: 0x01 opc: 0
     inc: 0  exflg: 0
 Itl           Xid                                     Uba                   Flag     Lck          Scn/Fsc   
0x01   0x0001.001.00000380  0x00c01117.0204.07   --U-     2         fsc 0x0000.002a1376
0x02   0x0000.000.00000000  0x00000000.0000.00   ----     0          fsc 0x0000.00000000
0x03   0x0000.000.00000000  0x00000000.0000.00   ----     0          fsc 0x0000.00000000
0x04   0x0000.000.00000000  0x00000000.0000.00   ----     0           fsc 0x0000.00000000
bdba: 0x0100027c
data_block_dump,data header at 0xe511924

itl             = interested transaction list (list of transactions on a block that have changes on that block) 
xid           = transaction id (it is a set of three numbers – undo segment#, undo slot# and undo record# -                           separated by periods.)
uba          = undo block address (pointer to a transactions list of undo blocks) 
scn/fsc    = system change/commit number of the current transaction
tsn          = Tablespace Number
rdba       = Relative data block address.Its value is "0100027c" in hexa decimal & 16777852 in decimal
seg/obj   = is the object_id. 0x 1277b   = 75643 in 75643  in decimal.
itc: 10    =  Number of interested transaction list(ITL) entry in the block.
typ: 1    =  data –> Indicates this is an data block
Flag      =  State for current transaction (C – Commited) U- indicates that the particular ITL was used.
Lck       =  Number of Lock held by the transacion
csc:  0x00.2a1372  =  This is the comit SCN number of the block

If  we use locally managed tablespaces with automatic segment space management , we can more or less forget about these parameters as  Oracle takes care of it for us . In Oracle 10g and above,  this is now more to concern .   In the past they were much more important that they are now .  

Note :

  • Lck” column shows a value. It shows “2”, meaning two rows in the blocks is locked by a transaction. But, which transaction? To get that answer, note the value under the “Xid” column. It shows the transaction ID -  0x0001.001.00000380 .  These numbers are in hexadecimal (as indicated by the 0x at the beginning of the number).
  • A transaction is identified by a transaction ID (XID) which is a set of three numbers – undo segment#, undo slot# and undo record# - separated by periods.
  • We can also check all the active transactions in the view v$transaction, where the columns XIDUSN, XIDSLOT and XIDSQN denote the undo segment#, undo slot# and undo rec# - the values that make up the transaction ID.
  • The v$transaction view also contains the session address under SES_ADDR column, which can be used to join with the SADDR column of v$session view to get the session details

 References:- http://www.rampant-books.com/

Enjoy     :-) 

1 comment:

Anonymous said...

simple and sweet expln