Cache Buffer Chains Latch Contention Case Study-1: Reverse Key Index
Index Block Split Point Distribution,
this Blog will first demonstrates Index Service ITL usage and Recursive Transaction during index block splits, and then, as an example, gives a proof of their existence in index stats gathering.
We will trace transaction with Enqueue Trace Event 10704, and make index block / undo header block dumps to observe Index Service ITL and Recursive Transaction.
1. Test
First repeat our previous setup by inserting 4707 rows:
truncate table test_tab;
insert into test_tab select level, -level from dual connect by level <= 4707;
commit;
Then collect meta info:
select object_name, object_id, to_char(object_id, 'XXXXXXXX') id_hex, data_object_id, to_char(data_object_id, 'XXXXXXXX') did_hex
from dba_objects where object_name in ('TEST_TAB', 'TEST_TAB#R');
OBJECT_NAME OBJECT_ID ID_HEX DATA_OBJECT_ID DID_HEX
----------- --------- ------ -------------- -------
TEST_TAB 2459814 2588A6 2462199 2591F7
TEST_TAB#R 2461548 258F6C 2462198 2591F6
-- index block for id: 1 to 11
select blk, count(*), min(id) min_id, max(id) max_id from (
select id
,dbms_rowid.rowid_block_number(sys_op_lbid (2461548, 'L', rowid)) blk
from test_tab)
where id between 1 and 11
group by blk order by 1, min_id, blk;
BLK COUNT(*) MIN_ID MAX_ID
------- -------- ------ ------
3704663 11 1 11
-- find index root block for later dump
select segment_name, header_file, header_block, (header_block+1) root_block from dba_segments where segment_name = 'TEST_TAB#R';
SEGMENT_NAME HEADER_FILE HEADER_BLOCK ROOT_BLOCK
------------ ----------- ------------ ----------
TEST_TAB#R 1548 3704658 3704659
Trace row 4708 insert with Event 10704. First setup Trace Events:
alter session set events='10046 trace name context forever, level 1 : 10704 trace name context forever, level 3'
tracefile_identifier='10704_r1_insert';
Then, open an UNIX window, start a Dtrace script to suspend process when Oracle gets to "ksqrcl:return" (session SPID: 543),
$ > sudo dtrace -w -n 'pid$target:oracle:ksqrcl:return {@CNT[ustack(5, 0)] = count(); stop(); exit(0);}' -p 543
Now make row 4708 insert:
insert into test_tab values(4708, -4708);
Immediately, process stopped, and Dtrace displays:
oracle`ksqrcl+0xa
oracle`ktucmt+0xe95
oracle`ktcCommitTxn_new+0x35d
oracle`ktccrb2+0x85
oracle`kdisle+0x4a219
1
From other Sqlplus session, run a query on gv$transaction:
select RECURSIVE, addr, xidusn, xidslot, xidsqn, status, ses_addr, start_scn, xid, prv_xid,
flag, DECODE (BITAND (flag, 32), 0, 'NO', 'YES') flag_recur
from gv$transaction v;
RECURSIVE ADDR XIDUSN XIDSLOT XIDSQN STATUS SES_ADDR START_SCN XID PRV_XID FLAG FLAG_RECUR
--------- --------- ------ ------- -------- -------- --------- ------------- ---------------- ---------------- -------- ----------
NO 1866294E8 2 33 14402489 ACTIVE 18EE42590 9447170829632 02002100B9C3DB00 0000000000000000 3587 NO
YES 18662A088 1 30 7334024 INACTIVE 18EE42590 9447170829684 01001E0088E86F00 02002100B9C3DB00 67116587 YES
We can see two transactions for the same Oracle session (SES_ADDR: 18EE42590).
One is marked RECURSIVE: NO; another is YES. Their ADDR and XID are different.
Recursive transaction has its PRV_XID (02002100B9C3DB00) same as XID of main transaction.
Recursive transaction is started after main transaction,
which is showed by START_SCN: 9447170829632 < 9447170829684.Index leaf block split also performed.
select blk, count(*), min(id) min_id, max(id) max_id from (
select id
,dbms_rowid.rowid_block_number(sys_op_lbid (2461548, 'L', rowid)) blk
from test_tab)
where id between 1 and 11
group by blk order by 1, min_id, blk;
BLK COUNT(*) MIN_ID MAX_ID
------- -------- ------ ------
3704663 5 1 5
3665613 6 6 11
Dump index root block (one block after header_block of segment TEST_TAB#R):
-- dump index root block 3704659
alter session set tracefile_identifier = "index_root_block_3704659_insert";
alter system dump datafile 1548 block 3704659;
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0001.01e.006fe888 0x00c07b03.5855.03 --U- 1 fsc 0x0000.9742ed75
Xid shows that it is done by RECURSIVE transaction, already committed
(Flag: "--U-" for fast commit), Scn/Fsc filled with "0x0000.9742ed75".So RECURSIVE transaction on root block is committed even that main transaction on leaf block is still open. Probably it is controlled by some Oracle internal mechanism, and more interestingly, this commit is independent to the commit of main transaction, so that contentions (locking duration) on root and branch blocks are minimized.
Note that there is only one Itl entry for index root and branch block, named Service Itl.
Open 10704 Trace file, all lines about TX are as follows:
ksqgtl *** TX-00020021-00DBC3B9-00000000-00000000 mode=6 flags=0x401 timeout=0 ***
ksqgtl: xcb=0x1866294e8, ktcdix=2147483647, topxcb=0x1866294e8
ksqgtl *** TX-0001001E-006FE888-00000000-00000000 mode=6 flags=0x401 timeout=0 ***
ksqgtl: xcb=0x18662a088, ktcdix=2147483647, topxcb=0x1866294e8
It shows two Transactions: TX-00020021 (Undo Segment 2, Slot 0x021=33)
and TX-0001001E (Undo Segment 1, Slot 0x01E=30).
TX-0001001E has TX-00020021 as topxcb=0x1866294e8
(TX-0001001E is a recursive transaction of TX-00020021, performed in the same session). Now resume UNIX process (prun 543), 10704 Trace file is added with lines below. Recursive transaction: TX-0001001E returns with ksqrcl (release lock) although recursive transaction already committed before.
ksqrcl: TX-0001001E-006FE888-00000000-00000000
ksqrcl: returns 0
Terminate 10704 Trace:
alter session set events='10046 trace name context off : 10704 trace name context off ';
Query v$transaction, only main transaction for row 4708 insert is open, recursive transaction committed:
-- XIDSQN: 14402489 = 0xdbc3b9
select xidusn, xidslot,xidsqn, ubafil, ubablk, ubasqn, ubarec, status from v$transaction;
XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK UBASQN UBAREC STATUS
------ -------- -------- ------ ------ ------ ------ ------
2 33 14402489 3 17248 18029 15 ACTIVE
Then make index block dump for the new split block, and look its two Itl
(leaf block has at least two Itl, the first one is reserved as Service Itl):
alter system checkpoint;
alter system flush buffer_cache;
-- dump new index block 3665613 (contains row 6 and new row 4708)
alter session set tracefile_identifier = "index_block_3665613_insert";
alter system dump datafile 1548 block 3665613;
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0001.01e.006fe888 0x00c07b03.5855.01 CB-- 0 scn 0x0897.9742ed75
0x02 0x0002.021.00dbc3b9 0x00c04360.b993.0f ---- 1 fsc 0x0000.00000000
First Itl is committed, second Itl is still open (Flag: '----', Lck: 1, Scn/Fsc not filled).
We can see first Itl is marked with Xid (0x0001.01e.006fe888), same as Itl of above index root block.
So root block and leaf block split are performed by the same recursive transaction,
but leaf block Uba (0x00c07b03.5855.01) is before root Uba (0x00c07b03.5855.03)
(same "block address" and "block sequence number", but different "record within block", 01 vs. 03).Find Undo Segment headers (segment_id 0x0001 and 0x0002) for both Itl:
-- Itl 0x01 is Service Itl
select segment_name, file_id, segment_id, block_id from dba_rollback_segs where segment_id in (1, 2);
SEGMENT_NAME FILE_ID SEGMENT_ID BLOCK_ID
-------------------- ------- ---------- --------
_SYSSMU1_1118279661$ 3 1 776
_SYSSMU2_3069567101$ 3 2 872
Dump both Itl Undo Segment Headers:
-- dump Service Itl undo header 776 (Xid: 0x0001.01e.006fe888, undo segment 1, slot 0x01e)
alter session set tracefile_identifier = "undo_header_776_insert";
alter system dump datafile 3 block 776;
index state cflags wrap# uel scn dba stmt_num cmt
------ ----- ------ --------- ------- --------------- ---------- ----------- ----------
0x1e 9 0x00 0x6fe888 0x0021 0x0897.9742ed75 0x00c07b03 0x00000000 1583849538
-- dump current active (state 10) TRX XID undo header 872 (Xid: 0x0002.021.00dbc3b9, undo segment 2, slot 0x021)
alter session set tracefile_identifier = "undo_header_872_insert";
alter system dump datafile 3 block 872;
index state cflags wrap# uel scn dba stmt_num cmt
------ ----- ------ --------- ------- --------------- ---------- ----------- ---
0x21 10 0x80 0xdbc3b9 0x000b 0x0897.9742ed40 0x00c04360 0x00000000 0
First Itl is committed (state 9) and stamped with cmt (commit time): 1583849538.
Second Itl is active (state 10).Now trace commit with Event 10704:
alter session set events='10046 trace name context forever, level 1 : 10704 trace name context forever, level 3'
tracefile_identifier='10704_r1_commit';
commit;
alter session set events='10046 trace name context off : 10704 trace name context off ';
XCTEND rlbk=0, rd_only=0, tim=7414747162919
ksqrcl: TX-00020021-00DBC3B9-00000000-00000000
ksqrcl: returns 0
The main transaction TX-00020021 terminated (XCTEND) and released lock (ksqrcl).Dump again index block of new split block:
alter system checkpoint;
alter system flush buffer_cache;
-- dump new index block 3665613 (contains row 6 and new row 4708)
alter session set tracefile_identifier = "index_block_3665613_commit";
alter system dump datafile 1548 block 3665613;
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0001.01e.006fe888 0x00c07b03.5855.01 CB-- 0 scn 0x0897.9742ed75
0x02 0x0002.021.00dbc3b9 0x00c04360.b993.0f C--- 0 scn 0x0897.9742efa1
Both Itl are committed. Scn/Fsc in second Itl (9742efa1) is bigger than that of first Itl (9742ed75). Dump again both Undo Segment Headers:
-- dump Service Itl undo header 776 (Xid: 0x0001.01e.006fe888, undo segment 1, slot 0x01e)
alter session set tracefile_identifier = "undo_header_776_commit";
alter system dump datafile 3 block 776;
index state cflags wrap# uel scn dba stmt_num cmt
------ ----- ------ --------- ------- --------------- ---------- ----------- ----------
0x1e 9 0x00 0x6fe888 0x0021 0x0897.9742ed75 0x00c07b03 0x00000000 1583849538
-- dump just committed TRX XID undo header 872 (Xid: 0x0002.021.00dbc3b9, undo segment 2, slot 0x021)
alter session set tracefile_identifier = "undo_header_872_commit";
alter system dump datafile 3 block 872;
index state cflags wrap# uel scn dba stmt_num cmt
------ ----- ------ --------- ------- --------------- ---------- ----------- ----------
0x21 9 0x00 0xdbc3b9 0x000b 0x0897.9742efa1 0x00c04360 0x00000000 1583850562
Second Itl is also committed (state 9) and stamped with cmt: 1583850562,
which is bigger than that of first Itl: 1583849538.
So recursive transaction is committed before main transaction,
and started after main transaction, or its lifecycle is totally inside main transaction.
2. Service ITL and Recursive Transaction with Index Stats
Now we can have a look of the impact caused by Recursive Transaction.
Open two Sqlplus sessions: SID1 and SID2.
In SID1, we create table and index with one row insert:
SID1 >
drop table test_service_itl purge;
create table test_service_itl
as select level x, rpad('Test Service ITL', 100, 'X') y from dual connect by level <= 1;
create index test_service_itl#i#1 on test_service_itl(x, y);
In SID2, insert 1,000,000 rows without commit:
SID2 >
insert into test_service_itl
select level x, rpad('Test Service ITL', 100, 'X') y from dual connect by level <= 1e6;
Back to In SID1, gather stats, run queries, and make index root block dump and index treedump.:
SID1 >
exec dbms_stats.gather_table_stats(null, 'TEST_SERVICE_ITL', cascade=> true);
select blevel, leaf_blocks, num_rows from dba_indexes v where index_name = 'TEST_SERVICE_ITL#I#1';
BLEVEL LEAF_BLOCKS NUM_ROWS
------ ----------- --------
2 1 1
-- find index root block for later dump
select segment_name, header_file, header_block, (header_block+1) root_block
from dba_segments where segment_name = 'TEST_SERVICE_ITL#I#1';
SEGMENT_NAME HEADER_FILE HEADER_BLOCK ROOT_BLOCK
------------------------------ ----------- ------------ ----------
TEST_SERVICE_ITL#I#1 1678 2257586 2257587
-- dump index root block 2257587
alter session set tracefile_identifier = "index_root_block_2257587";
alter system dump datafile 1678 block 2257587;
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x006c.00d.00003b62 0x00c1be06.0e31.04 C--- 0 scn 0x000008ceef036f76
Branch block dump
=================
header address 4233650252=0xfc58604c
kdxcolev 2
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 3
kdxcosdc 2
kdxconro 21
kdxcofbo 70=0x46
kdxcofeo 7846=0x1ea6
kdxcoavs 7776
kdxbrlmc 1795863=0x1b6717
kdxbrsno 20
kdxbrbksz 8056
kdxbr2urrc 0
select object_name, object_id, to_char(object_id, 'XXXXXXXX') id_hex,
data_object_id, to_char(data_object_id, 'XXXXXXXX') did_hex
from dba_objects where object_name = 'TEST_SERVICE_ITL#I#1';
OBJECT_NAME OBJECT_ID ID_HEX DATA_OBJECT_ID DID_HEX
-------------------- --------- ------ -------------- -------
TEST_SERVICE_ITL#I#1 3305086 326E7E 3305086 326E7E
-- dump index treedump
alter session set tracefile_identifier = "index_treedump_3305086";
alter session set events 'immediate trace name treedump level 3305086';
----- begin tree dump
branch: 0x2272b3 2257587 (0: nrow: 22, level: 2)
branch: 0x1b6717 1795863 (-1: nrow: 682, level: 1)
leaf: 0x2272b5 2257589 (-1: row:69.69 avs:61)
leaf: 0x2272b6 2257590 (0: row:69.69 avs:24)
leaf: 0x2272b7 2257591 (1: row:68.68 avs:109)
leaf: 0x18032d 1573677 (2: row:68.68 avs:108)
leaf: 0x18032e 1573678 (3: row:68.68 avs:109)
The index stats shows that both NUM_ROWS and LEAF_BLOCKS are 1 since un-committed 1,000,000 rows are not gathered.
However BLEVEL is 2 because BLEVEL is updated by Recursive Transaction with Index Service ITL,
which has already committed as showed by the index root Itl:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x006c.00d.00003b62 0x00c1be06.0e31.04 C--- 0 scn 0x000008ceef036f76
Therefore index meta data BLEVEL is computed independent of user data NUM_ROWS and LEAF_BLOCKS.
BLEVEL is updated by recursive transaction, others are updated by main transaction.The above test is an indirect proof of Service ITL and Recursive Transaction existence.