4. Who is Kyle Hailey
1990 Oracle
–
–
–
–
–
90 support
92 Ported v6
93 France
95 Benchmarking
98 ST Real World Performance
2000 Dot.Com
2001 Quest
2002 Oracle OEM 10g
Success!
First successful OEM design
5. Who is Kyle Hailey
1990 Oracle
–
–
–
–
–
90 support
92 Ported v6
93 France
95 Benchmarking
98 ST Real World Performance
2000 Dot.Com
2001 Quest
2002 Oracle OEM 10g
2005 Embarcadero
– DB Optimizer
6. Who is Kyle Hailey
•
1990 Oracle
•
•
•
•
•
90 support
92 Ported v6
93 France
95 Benchmarking
98 ST Real World Performance
2000 Dot.Com
2001 Quest
2002 Oracle OEM 10g
2005 Embarcadero
DB Optimizer
Delphix
When not being a Geek
- Have a little 4 year old boy who takes up all my time
9. AWR Method
Who/When
Summary
Top 5 Timed Events
~~~~~~~~~~~~~~~~~~
% for
Load profile goodTotal
Event
Waits having a (s) Call Time
Time feel for the
---------------------- ------------ application and
----------- --------buffer busy waits
2,748
250
78.72
comparing two 10.16
CPU time
32
periods for changes
free buffer waits
1,588
15
4.63
Efficiency 8
ratios 2.51
write complete waits
10
log buffer space
306 misleading carry 1.51
5
---------------------------------------- 6
over from version
days
Waits
Big Picture
10. Row Locks 10g+
Top 5 Timed Events
Avg %Total
~~~~~~~~~~~~~~~~~~
wait
Call
Event
Waits
Time (s)
(ms)
Time
----------------------------------------- ------------ ----------- ------ -----enq: TX - row lock contention
59
160
2714
41.8
PL/SQL lock timer
4
117 29291
30.6
CPU time
28
7.2
buffer busy waits
1,217
18
15
4.7
log file parallel write
422
11
27
3.0
Who is waiting?
Who is blocking?
What is the SQL?
What is the row?
Not in AWR report
11. Power of ASH !
v$active_session_history
•
Waiter
– SESSION_ID
– SESSION_SERIAL#
– USER_ID
•
Object
– CURRENT_OBJ#
– CURRENT_FILE#
– CURRENT_BLOCK#
•
SQL Waiting
- SQL_ID
•
Blocker
- BLOCKING_SESSION
- BLOCKING_SESSION_STATUS
- BLOCKING_SESSION_SERIAL#
•
Lock Type and Mode
- Event = Type (name)
- P1
= Type | Mode
12. Content
Part I : Row Locks
– enq: TX - row lock contention
•
•
•
•
•
Data in ASH
OEM
V$lock & v$session
ASH
Find blocking SQL
Part II : Row Locks ??
– enq: TX - row lock contention, mode 4
Part III : Other TX locks
–
–
–
enq: TX - allocate ITL entry
enq: TX - index contention
enq: TX – contention
Part IV : TM locks
13. Lock Types
•
9i
– One Wait : “enqueue”
•
10g
TX
TM
Transaction Table Modification
– 208 enqueue waits
– Specific to each type of enqueue
– select name,wait_class from v$event_name where name like 'enq%'
enq:
enq:
enq:
enq:
enq:
enq:
enq:
enq:
enq:
enq:
HW
SQ
SS
ST
TM
TW
TX
TX
TX
TX
–
contention
contention
contention
contention
contention
contention
allocate ITL entry
index contention
row lock contention
contention
Configuration
Configuration
Configuration
Configuration
Application
Administrative
Configuration
Concurrency
Application
Application
14. Lock Modes
# Type
--- ------1 Null
2 SS
3 SX
4 S
5 SSX
6 X
Name
--------------------------Null
Sub share
Sub exclusive
Share
Share/sub exclusive
Exclusive
15. Lock Type and Mode
Select parameter1 from v$event_name where name like ‘enq%’;
Parameter1
---------Name|mode
Select p1, p1raw from v$session where event like 'enq%';
P1
P1RAW
---------- -------1415053318 54580006
Name: 5458
Hex
Decimal
54 =
84
58 =
88
ASCII
=
“T”
=
“X”
Mode: 0006
Lock = TX 6
17. enq: TX - row lock contention, mode 6
To Solve we need, and ASH gives :
1. Waiter
2. Blocker
3. Blocking Object
4. Lock Type
– type
– Mode
1. Blocking SQL : Missing
1.Log miner?
2.Undo ?
18. OEM 10g
if P1 = 1415053318
then mode = 6
Then it is a data block
row lock
19. enq: TX - row lock contention
Mode 6, row in data block
User 1
SQL> delete from toto
where id = 1;
User 2
SQL> delete from toto
where id =1;
Table
ID
Value
foo
1
21. Other Data Sources
In “real time” can also use
–
–
–
–
–
–
v$lock
v$session (v$session_wait)
dba_blockers
dba_waiters
?/rdbms/admin/utllockt.sql
http://www.evdbt.com/enqwaits.sql
22. Step one: get a lock
Session 1
Session_1> select pk_id from test_tab1
where rownum=1 ;
PK_ID
--------------
4051
select pk_id from test_tab1
where pk_id = 4051
for update;
23. Step 2: create a blocker
Session 2 Session_2> select pk_id from test_tab1
where pk_id = 4051
for update;
Hangs …
24. Step 3: release the lock and blocking
Session 1
Session_1> commit;
Commit complete.
Session 2 now continues
PK_ID
---------4051
37. Session B
update toto set
update toto set
name = ‘ADAMS’
name = ‘ADAMS’
where id = 1;
where id = 1;
TX Lock
Wait for Tx
To commit
Undo
Segment
Header
Redo
Table Toto
Data Block
Data Block
Header
Header
Transaction 1
Session a
Row 1
Undo
Segment
update toto set
update toto set
name = ‘SMITH’
name = ‘SMITH’
where id = 1;
where id = toto where id
Delete from 1;
Delete from toto where id
= 2;
= 2;
Delete from toto where id
Delete from toto where id
38. Logminer : setup
SQL> select group#, thread#, status from v$log;
GROUP#
THREAD# STATUS
---------- ---------- ---------------1
1 CURRENT
2
1 ACTIVE
3
1 ACTIVE
SQL> select group#, member from v$logfile;
GROUP#
---------1
2
3
MEMBER
-----------------------------------------------------------C:ORACLEPRODUCT10.2.0ORADATATEST1020REDO01.LOG
C:ORACLEPRODUCT10.2.0ORADATATEST1020REDO02.LOG
C:ORACLEPRODUCT10.2.0ORADATATEST1020REDO03.LOG
SQL> exec dbms_logmnr.add_logfile('&my_member');
Enter value for my_member: C:ORACLEPRODUCT10.2.0ORADATATEST1020REDO01.LOG
PL/SQL procedure successfully completed.
SQL> exec dbms_logmnr.start_logmnr(options => dbms_logmnr.dict_from_online_catalog);
PL/SQL procedure successfully completed.
@lgmr.sql
39. Logminer : setup locking
SQL> create table test_tab1
as select object_id pk_id, object_name from all_objects;
SQL> select rowid, object_name from test_tab1 where pk_id=5042;
ROWID
OBJECT_NAME
------------------ -----------------------------AAAN2NAAKAAAaBLAEX SYSTEM_PRIVILEGE_MAP
SESSION_1>
update test_tab1 set object_name='UKOUG 1' where pk_id=5042;
SESSION_2>
update test_tab1 set object_name='UKOUG 2' where pk_id=5042;
Future
Reference:
ROWID
Create
Lock
Wait
40.
41. Find Transaction
•
From v$transaction
– Xid of current active transaction
•
From v$active_session _history
– Calculate XID from p2, p3
– For currently waiting ‘enq: TX row lock contention’
•
Use XID to dump redo log for that TX
42. Logminer with XID
SQL> select
@tx.sql
xid, xidusn, xidslot, xidsqn
from v$transaction order by start_time;
XID
XIDUSN
XIDSLOT
XIDSQN
-------------------- ---------- ---------- ---------0A00240064180000
10
36
6244
Only one transaction active on system, ours
SQL>
select username, session# sid, serial# , sql_redo
from v$logmnr_contents
where XID = '&tx';
USERNAME
SID
SERIAL# SQL_REDO
----------------- ---------- ---------- ----------------UNKNOWN
0
0
set transaction read write;
UNKNOWN
0
0
update "SYS"."TEST_TAB1" set "OBJECT_NAME" =
UNKNOWN
0
0
'SESSION 1' where "OBJECT_NAME" =
'CLUSTER_NODES' and ROWID = 'AAAT5XAABAAAW7hAEr’;
commit;
@lgmrtx.sql
43. Finding XID from ASH
Normal use P2, P3 from wait:
WAIT TYPE = 'enq: TX - Row Lock Contention’
Then
P2=usn<<16||slot
P3=Sequence
=> XID
select
distinct
lpad(ltrim(to_char(p2,'XXXXXX')),6,'0')||'00'||
ltrim(to_char(mod(p3,256),'XX'))||
ltrim(to_char(trunc(p3/256),'XX'))||'0000'
block_xid,
to_char(p2,'XXXXXXXX') p2hex,
to_char(p3,'XXXXXXXX') p3hex,
trunc(p2/65536) usn,
mod(p2,65536) slot,
p3 sqn, xid wait_xid
from v$active_session_history
where event like 'enq: T%'
and sample_time > sysdate - &v_minutes/(60*24)
;
select username, session# sid,
serial# , sql_redo
from v$logmnr_contents
where XID = '&tx';
'&tx';
@ashtx.sql
@lgmrtx.sql
44. Logminer by USN and SLOT
SQL> select
xid, xidusn, xidslot, xidsqn
from v$transaction order by start_time;
XID
XIDUSN
XIDSLOT
XIDSQN
-------------------- ---------- ---------- ---------0A00240064180000
10
36
6244
SYS@TEST1020> select distinct
username, session# sid, serial# , sql_redo
from v$logmnr_contents
where timestamp > sysdate- &minutes/(60*24)
and xidusn=10
and xidslt=36;
Enter value for minutes: 10
USERNAME
SID
SERIAL# SQL_REDO
----------------- ---------- ---------- ----------------UNKNOWN
0
0
set transaction read write;
UNKNOWN
0
0
update "SYS"."TEST_TAB1" set "OBJECT_NAME" =
UNKNOWN
0
'SESSION 1' where "OBJECT_NAME" =
'CLUSTER_NODES' and ROWID = 'AAAT5XAABAAAW7hAEr’;
0 commit;
45. Dumping Redo Directly
SQL> select rowid, object_name from test_tab1 where pk_id=5041;
ROWID
OBJECT_NAME
------------------ -----------------------------AAAN2NAAKAAAaBLAEX SYSTEM_PRIVILEGE_MAP
SYS@TEST1020> select
dbms_rowid.rowid_block_number('&my_rowid') block_number,
dbms_rowid.rowid_to_absolute_fno('&my_rowid','SYS','TEST_TAB1') file_id
from dual;
Enter value for my_rowid: AAAN2NAAKAAAaBLAEX
FILE_ID
---------10
BLOCK_NUMBER
-----------106571
46. Data block row update
FILE_ID
BLOCK_NUMBER
----------
------------
10
106571
SYS@TEST1020> alter system dump logfile '&&my_member'
dba min 10 106571 dba max 10 106571;
C:ORACLEPRODUCT10.2.0ORADATATEST1020REDO01.LOG
System altered
DUMP OF REDO FROM FILE 'C:ORACLEPRODUCT10.2.0ORADATATEST1020REDO01.LOG' Opcodes
*.* DBAs: (file # 10, block # 106571) thru (file # 10, block # 106571) RBAs:
0x000000.00000000.0000 thru 0xffffffff.ffffffff.ffff SCNs: scn: 0x0000.00000000 thru
scn: 0xffff.ffffffff
…
REDO RECORD - Thread:1 RBA: 0x0000b3.00000009.0010 LEN: 0x0210 VLD: 0x0d SCN:
0x0000.008a42e6 SUBSCN: 1 04/20/2009 20:46:57 CHANGE #1 TYP:2 CLS: 1 AFN:10
DBA:0x0281a04b OBJ:56717 SCN:0x0000.008a4239 SEQ: 2 OP:11.19 KTB Redo op:
0x11 ver: 0x01
op: F xid: 0x000a.024.00001864
uba: 0x0080009d.031a.27
0A00240064180000
CLS: 1 - Data Class Block
OP:11.19 - Operation Update Multiple Rows.
52. P1 = name | mode
select distinct parameter1 from v$event_name
where name like 'enq:%'
PARAMETER1
name|mode
select
event,
mod(p1,16) as "mode"
from v$active_session_history
where event like 'enq:%‘;
EVENT
enq: TX - row lock contention
enq: TX - row lock contention
mode
6
4
57. 1. enq: TX - row lock contention
Mode 4 , unique index
User 2
User 1
create table p(n number);
create unique index p_i on p(n);
insert into p values(2);
insert into p values(2);
PK
Table
ID
2?
ID Value
2?
58. 2. enq: TX - row lock contention
Mode 4, foreign key
User 1
create table parent (
id number primary key);
create table child (
id number references parent,
name varchar2(20));
insert into parent values (2);
PK
ID
2?
Parent
ID Value
2?
User 2
insert into child values (2,88);
Child
ID Name
2
59. 3. enq: TX - row lock contention
Mode 4, bitmap
• Bitmaps are compressed
• Changes to the same bitmap cause locks
Value Start
Rowid
1
000.000.0000
End
Bitmap
Rowid
01010000111000011100001100
000.000.000
2
01010000111000011100001100
000.000.0000
000.000.000
60. 3. enq: TX - row lock contention
Mode 4, foreign key
Session 1
create table t1 ((
Different rows but
create table t1
value number(4),
value number(4),
same key value
row number(4));
row number(4));
insert into t1
insert into t1
select 1, rownum
select 1, rownum
from all_objects
from all_objects
where rownum <= 400;
where rownum <= 400;
commit;
commit;
create bitmap index i1 on t1(value);
create bitmap index i1 on t1(value);
update t1 set value = 2
update t1 set value = 2
Session 2
where row= 12;
where row= 12;
update t1 set value = 2
update t1 set value = 2
where row= 13;
where row= 13;
61. 3. enq: TX - row lock contention
Mode 4, foreign key
•
•
Bitmaps are compressed
Changes to the same bitmap chunk cause locks
Value Start End
Bitmap
Rowid Rowid
1
01010000111000011100001100
200.0
1
2
205.0
200.0
block
204.7
210.3
205.6
row
01010000111000011100001100
01010000111000011100001100
62. 3. enq: TX - row lock contention
Mode 4, foreign key
Value Start End
Bitmap
Rowid Rowid
1
01010000111000011100001100
200.0
2
3
205.0
200.0
204.7
210.3
205.6
01010000111000011100001100
01010000111000011100001100
Session 1
Update row=12
set value 2
Session 2
Update row=13
set value 2
64. Specific ‘enq: TX’ wait events
•
enq: TX - allocate ITL entry
– ITL space waits
•
enq: TX - index contention ?
– Index block split
•
enq: TX – contention Unusual
– Alter tablespace … read only;
– Free Lists slot waits
– Possible with two phase commit
65. enq: TX - allocate ITL entry
Data Block
Data Block
Header
Header
ITL
Data
Transaction 1 Info
Transaction 2 Info
66. enq: TX - allocate ITL entry
Data Block
Data Block
Header
Header
Transaction 1
Transaction 2
Row 3
Row 2
Data
Row 1
Transaction 3
67. enq: TX - allocate ITL entry
create table itl (
create table itl (
id number,
id number,
data varchar2(20)
data varchar2(20)
)
)
pctfree 0
pctfree 0
initrans 1
initrans 1
;
;
insert into itl select rownum,'a' from all_objects
insert into itl select rownum,'a' from all_objects
where rownum < 2000;
where rownum < 2000;
commit;
commit;
session 1: update itl set data=data where id=1;
session 1: update itl set data=data where id=1;
session 2: update itl set data=data where id=2;
session 2: update itl set data=data where id=2;
session 3: update itl set data=data where id=3;
session 3: update itl set data=data where id=3;
session 4: update itl set data=data where id=4;
session 4: update itl set data=data where id=4;
session 5: update itl set data=data where id=5;
session 5: update itl set data=data where id=5;
68. enq: TX - contention
1. Altering tablespace read only with open transaction
Example
– Session 1 – start transaction, don’t commit
– Session 2 – alter tablespace read only
1. Data File Extension – waiter waiting for another session to extend
file
69. enq: TM - contention
•
•
TX locks have a corresponding TM lock
TM locks the structure from change
Parameter1
= object id
LOCK
Parmeter1
LOCK
Parmeter1
------- --------------- --------enq: TM name|mode
enq: TM name|mode
Parmeter2(ID1) Parameter3(ID2)
Parmeter2(ID1) Parameter3(ID2)
----------------------------------------------------object #
table/partition
object #
table/partition
70. enq: TM - contention
Exclusive Row Level Lock
User 1
create table parent (
id number primary key);
create table child (
id number references parent,
name varchar2(20));
insert into parent values (1);
insert into parent values (2);
commit;
delete from parent where id=2;
User 2
insert into child values (1,’a’);
No commit
71. enq: TM - contention
PK
Parent
Child
ID
1
X
2
ID
1
X
2
ID Name
1
Session 1
Session 2
Value
Insert into Child ID=1
Delete from Parent where ID=2 :
Enqueue TM 4
Session 2 doesn’t know the value Session 1 inserted
Session 2 only knows there is an outstanding change
72. enq: TM – contention Solution
PK
Parent
ID
1
2
ID
Value
Index
ID
1
Child
ID Name
Foreign Key
Session 1: Insert into Child ID=1
Session 2: Delete from Parent ID=2
OK – can verify quickly in the child index
73. TM Further Investigation
select
event,
sql_id,
mod(p1,16) as "mode",
p2|| ' ' || o.name obj
from v$active_session_history ash,
obj$ o
where
event like 'enq: TM%'
and o.obj# (+)= ash.p2
order by sample_time;
EVENT
enq:
enq:
enq:
enq:
enq:
enq:
TM
TM
TM
TM
TM
TM
SQL_ID
-
contention
contention
contention
contention
contention
contention
8zw36yw3fq4yy
8zw36yw3fq4yy
8zw36yw3fq4yy
8zw36yw3fq4yy
8zw36yw3fq4yy
8zw36yw3fq4yy
mode OBJ
4
4
4
4
4
4
53372
53372
53372
53372
53372
53372
CHILD
CHILD
CHILD
CHILD
CHILD
CHILD
In Oracle Support I learned more faster than I think I could have anywhere.
Porting gave me my first appreciation for the shareable nature of Oracle code and also a bit of disbelief that it worked as well as it did.
Oracle France gave me an opportunity to concentrate on the Oracle kernel.
At Oracle France I had 3 amazing experiences. First was being sent to the Europecar site where I first met a couple of the people who would later become the founding members of the Oaktable, James Morle and Anjo Kolk. The Europecar site introduced me to a fellow name Roger Sanders who first showed me the wait interface before anyone knew what it was. Roger not only used it but read it directly from shared memory without using SQL.
Soon after Europecar I began to be sent often to benchmarks at Digital Europe. These benchmarks were some of my favorite work at Oracle. The benchmarks usually consisted of installing some unknown Oracle customer application and then having a few days to make it run as fast as possible. I first started using TCL/TK and direct shared memory access (DMA) at Digital Europe and got solid hands on tuning experience testing things like striping redo and proving it was faster long before people gave up arguing that this was bad from a theoretical point of view.
Finally in France, my boss, Jean Yves Caleca was by far the best boss I ever had, but on top of that he was wonderful at exploring the depths of Oracle and explaining it to others, teaching me much about the internals of block structure, UNDO, REDO and Freelsits.
I came back from France wanting to do performance work and especially graphical monitoring. The kernel performance group had limited scope in that domain, so I left for a dot com where I had my first run as the sole DBA for everything, backup, recovery, performance, installation and administration. I was called away by Quest who had my favorite performance tool Spotlight. It turns out thought that scope for expanding Spotlight was limited so I jumped at the chance in 2002 to restructure Oracle OEM. The work at OEM I’m proud of but still want to do much more to make performance tuning faster, easier and more graphical.
In Oracle Support I learned more faster than I think I could have anywhere.
Porting gave me my first appreciation for the shareable nature of Oracle code and also a bit of disbelief that it worked as well as it did.
Oracle France gave me an opportunity to concentrate on the Oracle kernel.
At Oracle France I had 3 amazing experiences. First was being sent to the Europecar site where I first met a couple of the people who would later become the founding members of the Oaktable, James Morle and Anjo Kolk. The Europecar site introduced me to a fellow name Roger Sanders who first showed me the wait interface before anyone knew what it was. Roger not only used it but read it directly from shared memory without using SQL.
Soon after Europecar I began to be sent often to benchmarks at Digital Europe. These benchmarks were some of my favorite work at Oracle. The benchmarks usually consisted of installing some unknown Oracle customer application and then having a few days to make it run as fast as possible. I first started using TCL/TK and direct shared memory access (DMA) at Digital Europe and got solid hands on tuning experience testing things like striping redo and proving it was faster long before people gave up arguing that this was bad from a theoretical point of view.
Finally in France, my boss, Jean Yves Caleca was by far the best boss I ever had, but on top of that he was wonderful at exploring the depths of Oracle and explaining it to others, teaching me much about the internals of block structure, UNDO, REDO and Freelsits.
I came back from France wanting to do performance work and especially graphical monitoring. The kernel performance group had limited scope in that domain, so I left for a dot com where I had my first run as the sole DBA for everything, backup, recovery, performance, installation and administration. I was called away by Quest who had my favorite performance tool Spotlight. It turns out thought that scope for expanding Spotlight was limited so I jumped at the chance in 2002 to restructure Oracle OEM. The work at OEM I’m proud of but still want to do much more to make performance tuning faster, easier and more graphical.
In Oracle Support I learned more faster than I think I could have anywhere.
Porting gave me my first appreciation for the shareable nature of Oracle code and also a bit of disbelief that it worked as well as it did.
Oracle France gave me an opportunity to concentrate on the Oracle kernel.
At Oracle France I had 3 amazing experiences. First was being sent to the Europecar site where I first met a couple of the people who would later become the founding members of the Oaktable, James Morle and Anjo Kolk. The Europecar site introduced me to a fellow name Roger Sanders who first showed me the wait interface before anyone knew what it was. Roger not only used it but read it directly from shared memory without using SQL.
Soon after Europecar I began to be sent often to benchmarks at Digital Europe. These benchmarks were some of my favorite work at Oracle. The benchmarks usually consisted of installing some unknown Oracle customer application and then having a few days to make it run as fast as possible. I first started using TCL/TK and direct shared memory access (DMA) at Digital Europe and got solid hands on tuning experience testing things like striping redo and proving it was faster long before people gave up arguing that this was bad from a theoretical point of view.
Finally in France, my boss, Jean Yves Caleca was by far the best boss I ever had, but on top of that he was wonderful at exploring the depths of Oracle and explaining it to others, teaching me much about the internals of block structure, UNDO, REDO and Freelsits.
I came back from France wanting to do performance work and especially graphical monitoring. The kernel performance group had limited scope in that domain, so I left for a dot com where I had my first run as the sole DBA for everything, backup, recovery, performance, installation and administration. I was called away by Quest who had my favorite performance tool Spotlight. It turns out thought that scope for expanding Spotlight was limited so I jumped at the chance in 2002 to restructure Oracle OEM. The work at OEM I’m proud of but still want to do much more to make performance tuning faster, easier and more graphical.
bitand(p1, 65536) "Mode"
Lock seems easy
Compared to latches, buffer busy waits etc
col event for a22
col block_type for a18
col objn for a18
col otype for a10
col fn for 99
col sid for 9999
col bsid for 9999
col lm for 99
col p3 for 99999
col blockn for 99999
select
to_char(sample_time,'HH:MI') st,
substr(event,0,20) event,
ash.session_id sid,
mod(ash.p1,16) lm,
ash.p2,
ash.p3,
nvl(o.object_name,ash.current_obj#) objn,
substr(o.object_type,0,10) otype,
CURRENT_FILE# fn,
CURRENT_BLOCK# blockn,
ash.SQL_ID,
BLOCKING_SESSION bsid
--,ash.xid
from v$active_session_history ash,
all_objects o
where event like 'enq: TX %'
and o.object_id (+)= ash.CURRENT_OBJ#
and sample_time > sysdate - &1/(60*24)
Order by sample_time
/
TX
if P1 = 1415053316
then mode = 4
if P1 = 1415053318
then mode = 6
create table p(n number);
create unique index p_i on p(n);
insert into p values(3);
Subject:
Bitmap Indexes and Deadlocks: Deadlocks on Insert Statements
Doc ID:
Note:171795.1
Type:
TROUBLESHOOTING
Last Revision Date:
18-MAY-2004
Status:
PUBLISHED
PURPOSE
-------
The purpose of this article is to explain the occurrence of deadlocks
when the only DML activity is insert statements against a table with
a bitmap index
SCOPE & APPLICATION
-------------------
Database administrators and Application developers involved in
application design.
BITMAP INDEXES: THE HIDDEN DEADLOCK THREAT
------------------------------------------
The "Oracle8i Designing and Tuning for Performance" guide explains
the limitations of bitmap indexes as this:
Extract of documentation:
"DML and DDL statements, such as UPDATE, DELETE, DROP TABLE, affect bitmap
indexes the same way they do traditional indexes: the consistency model is
the same. A compressed bitmap for a key value is made up of one or more bitmap
segments, each of which is at most half a block in size (but may be smaller).
The locking granularity is one such bitmap segment. This may affect performance
in environments where many transactions make simultaneous updates. If numerous
DML operations have caused increased index size and decreasing performance for
queries, then you can use the ALTER INDEX ... REBUILD statement to compact the
index and restore efficient performance.
A B*-tree index entry contains a single rowid. Therefore, when the index entry
is locked, a single row is locked. With bitmap indexes, an entry can potentially
contain a range of rowids. When a bitmap index entry is locked, the entire range
of rowids is locked. The number of rowids in this range affects concurrency.
As the number of rowids increases in a bitmap segment, concurrency decreases.
Locking issues affect DML operations, and may affect heavy OLTP environments.
Locking issues do not, however, affect query performance. As with other types
of indexes, updating bitmap indexes is a costly operation. Nonetheless, for
bulk inserts and updates where many rows are inserted or many updates are made
in a single statement, performance with bitmap indexes can be better than with
regular B*-tree indexes."
**************
What is not mentioned is the fact that the same architectural feature that
locks a range of rowid's also means that its possible to get a deadlock within
the bitmap when updating rows in the underlying table. This deadlock is not in
the table itself, as one might suspect, but rather in the bitmap index blocks.
This kind of deadlock is easily diagnosable by the deadlock trace file, which
has an entry that looks like the example below:
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:
Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TX-00080027-0000d2a1 12 37 X 15 35 S
TX-000a0016-0000d6d2 15 35 X 12 37 S
session 37: DID 0001-000C-00000002 session 35: DID 0001-000F-00000002
session 35: DID 0001-000F-00000002 session 37: DID 0001-000C-00000002
Rows waited on:
Session 35: no row
Session 37: no row
The piece of information that leads us to a bitmap deadlock is the "no row"
value in the session information. If we had encountered a deadlock in the
underlying table, the Session line would give us row information so that we
could track down the exact point of failure. Without a row, it would seem that
we are at a dead end. Even more mysterious is when we get this deadlock on
inserts, where we are inserting only new rows and therefore it would seem
impossible to get a deadlock. No one should be requesting a row that someone
else holds locked.
There are no solutions to this kind of problems, except not using bitmap indexes
when having an application where you can't control when the DML are issued against
the tables with bitmap indexes. Bitmaps are normally intended for datawarehouse
applications that are loading data via batches and that users are only querying.
The following testcase can be used to see the results of this type of problem.
We will create a table called CAR_TYPE, which holds information about cars,
including the car's color. We will build a bitmap index on the COLOR column.
After doing so, we will populate the table with data. After the initial insert,
we will open two sessions of the same user, and run simultaneous inserts into
the CAR_TYPE table.
TESTCASE:
=====================================
=====================================
create table car_type (
make varchar2(20),
model varchar2(20),
color varchar2(20),
VIN number(15) primary key,
year number(4));
create bitmap index car_type_bm_idx on car_type(color);
create sequence car_type_seq
start with 35001
increment by 1
nocache
nocycle;
declare
v_CarMake varchar2(20) := 'Audi';
v_CarModel varchar(20) := 'Quattro';
v_CarColor varchar(20) := 'Gold';
v_CarVin binary_integer :=1;
begin
loop
insert into car_type (make,model,color,VIN,year)
values (v_CarMake, v_CarModel, v_CarColor, v_CarVin, '2002');
v_CarVin := v_CarVin + 1;
exit when v_CarVin > 5000;
end loop;
end;
/
commit;
declare
v_CarMake varchar2(20) := 'Toyota';
v_CarModel varchar(20) := 'Camry';
v_CarColor varchar(20) := 'Red';
v_CarVin binary_integer :=5001;
begin
loop
insert into car_type (make,model,color,VIN,year)
values (v_CarMake, v_CarModel, v_CarColor, v_CarVin, '2002');
v_CarVin := v_CarVin + 1;
exit when v_CarVin > 10000;
end loop;
end;
/
commit;
declare
v_CarMake varchar2(20) := 'Audi';
v_CarModel varchar(20) := 'Quattro';
v_CarColor varchar(20) := 'Blue';
v_CarVin binary_integer :=10001;
begin
loop
insert into car_type (make,model,color,VIN,year)
values (v_CarMake, v_CarModel, v_CarColor, v_CarVin, '2002');
v_CarVin := v_CarVin + 1;
exit when v_CarVin > 15000;
end loop;
end;
/
commit;
declare
v_CarMake varchar2(20) := 'Toyota';
v_CarModel varchar(20) := 'Camry';
v_CarColor varchar(20) := 'Silver';
v_CarVin binary_integer :=15001;
begin
loop
insert into car_type (make,model,color,VIN,year)
values (v_CarMake, v_CarModel, v_CarColor, v_CarVin, '2002');
v_CarVin := v_CarVin + 1;
exit when v_CarVin > 20000;
end loop;
end;
/
commit;
declare
v_CarMake varchar2(20) := 'Audi';
v_CarModel varchar(20) := 'Quattro';
v_CarColor varchar(20) := 'Green';
v_CarVin binary_integer :=20001;
begin
loop
insert into car_type (make,model,color,VIN,year)
values (v_CarMake, v_CarModel, v_CarColor, v_CarVin, '2002');
v_CarVin := v_CarVin + 1;
exit when v_CarVin > 25000;
end loop;
end;
/
commit;
declare
v_CarMake varchar2(20) := 'Audi';
v_CarModel varchar(20) := 'Quattro';
v_CarColor varchar(20) := 'Black';
v_CarVin binary_integer :=25001;
begin
loop
insert into car_type (make,model,color,VIN,year)
values (v_CarMake, v_CarModel, v_CarColor, v_CarVin, '2002');
v_CarVin := v_CarVin + 1;
exit when v_CarVin > 30000;
end loop;
end;
/
commit;
declare
v_CarMake varchar2(20) := 'Toyota';
v_CarModel varchar(20) := 'Camry';
v_CarColor varchar(20) := 'White';
v_CarVin binary_integer :=30001;
begin
loop
insert into car_type (make,model,color,VIN,year)
values (v_CarMake, v_CarModel, v_CarColor, v_CarVin, '2002');
v_CarVin := v_CarVin + 1;
exit when v_CarVin > 35000;
end loop;
end;
/
commit;
===============================
===============================
After this initial creation, cut the following script into a .sql file, and
then execute it simultaneously from two sessions:
===============================
===============================
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
commit;
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
commit;
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
commit;
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
commit;
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
commit;
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','White',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Red',car_type_seq.nextval,'2002');
insert into car_type values (
'Toyota','Camry','Silver',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Black',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Gold',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Blue',car_type_seq.nextval,'2002');
insert into car_type values (
'Audi','Quatro','Green',car_type_seq.nextval,'2002');
commit;
========================================
========================================
The result will be occasional deadlock errors:
insert into car_type values (
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
The trace file will show the tell-tale 'No Row' message:
Rows waited on:
Session 11: no row
Session 10: no row
RELATED DOCUMENTS
-----------------
Oracle8i Designing and Tuning for Performance
Release 2 (8.1.6)
Part Number A76992-01
.