我们知道,在表的数据块中,当数据插入时,要保证数据块上剩余空间大于、等于PCTFREE的比例设置,以用于数据更新和多事务处理,从而减少数据迁移(Row Migration)的发生;而当分配新的数据块时,会根据INITRANS的设置预留相应的ITL slot,保证并发事务能分配到ITL slot。
在索引中,这两个参数仅在有数据时创建或重建索引才会起作用,且仅在叶子节点上起作用。
INITRANS
INITRANS在索引数据块上是否起作用,是由索引在创建或重建时是否有数据(即是否会分配数据块)决定的。比较以下代码,第一段代码在truncate之后rebuild(即不会分配索引数据块),因而ITL slot数量为默认值2;第二段代码在有数据时rebuild,然后再truncate,此时再插入数据产生的新的索引块上ITL slot数量就受到INITRANS的控制:
HELLODBA.COM> truncate table idx_split;
Table truncated.
HELLODBA.COM> alter index idx_split_idx rebuild initrans 3 pctfree 10;
Index altered.
HELLODBA.COM> begin
2 for i in 1..35
3 loop
4 insert into idx_split (a, b, c) values (i*3, lpad('A', 100, 'A'), sysdate);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
HELLODBA.COM> commit;
Commit complete.
HELLODBA.COM> alter session set events 'immediate trace name treedump level 199127';
Session altered.
--代码2:
HELLODBA.COM> truncate table idx_split;
Table truncated.
HELLODBA.COM> insert into idx_split (a, b, c) values (1*3, lpad('A', 100, 'A'), sysdate);
1 row created.
HELLODBA.COM> commit;
Commit complete.
HELLODBA.COM> alter index idx_split_idx rebuild initrans 3 pctfree 10;
Index altered.
HELLODBA.COM> truncate table idx_split;
Table truncated.
HELLODBA.COM> begin
2 for i in 1..35
3 loop
4 insert into idx_split (a, b, c) values (i*3, lpad('A', 100, 'A'), sysdate);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
HELLODBA.COM> alter session set events 'immediate trace name treedump level 199127';
Session altered.
需要注意的是,当数据块上ITL Slot数量大于起作用的INITRANS时,在分裂时被“继承”。在以下例子中,在rebuild时,指定了INITRANS为3:
Table truncated.
HELLODBA.COM> conn demo/demo
Connected.
HELLODBA.COM> alter session set events '10224 trace name context forever,level 1';
Session altered.
HELLODBA.COM> begin
2 for i in 1..100
3 loop
4 insert into idx_split (a, b, c) values (i*3, lpad('A', 100, 'A'), sysdate);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
HELLODBA.COM> commit;
Commit complete.
HELLODBA.COM> alter index idx_split_idx rebuild initrans 3 pctfree 60;
Index altered.
HELLODBA.COM> alter session set events 'immediate trace name treedump level 199127';
Session altered.
我们同时启动4个事务作用在最后一个节点,导致该数据块上分配5个(加一个递归事务ITL slot)ITL slot:
HELLODBA.COM> delete from idx_split where a=100*3;
1 row deleted.
-- Trans 2
HELLODBA.COM> delete from idx_split where a=99*3;
1 row deleted.
--Trans 3
HELLODBA.COM> delete from idx_split where a=98*3;
1 row deleted.
--Trans 4
HELLODBA.COM> delete from idx_split where a=97*3;
1 row deleted.
然后将它们全部提交或回滚,再插入数据,造成分裂:
HELLODBA.COM> begin
2 for i in 101..150
3 loop
4 insert into idx_split (a, b, c) values (i*3, lpad('A', 100, 'A'), sysdate);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
HELLODBA.COM> alter session set events 'immediate trace name treedump level 199127';
Session altered.
--5-5分裂
HELLODBA.COM> insert into idx_split (a, b, c) values (138*3, lpad('A', 100, 'A'), sysdate);
1 row created.
HELLODBA.COM> alter session set events 'immediate trace name treedump level 199127';
Session altered.
Dump出分裂的数据块,可以看到所有数据块都被分配了5个ITL slot,而不是INITRANS(3)的数量:
Object id on Block? Y
seg/obj: 0x30a50 csc: 0x00.b1859616 itc: 5 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x3c00402 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x005f.016.00000256 0x008019b5.0120.02 -B-- 1 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x03 0x005f.005.00000257 0x008019b4.0120.27 ---- 5 fsc 0x0000.00000000
0x04 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x05 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
PCTFREE
PCTFREE在分裂时则被忽略。在上述例子中,我们找到一块发生9-1分裂产生的数据块,可以看到其空闲空间为44b,空闲率为44/2048=2.1%,远远小于我们rebuild时的设定值(60)。
kdxcofbo 66=0x42
kdxcofeo 110=0x6e
kdxcoavs 44
...
我们再插入一些中间数据,造成5-5分裂:
2 for i in 1..15
3 loop
4 insert into idx_split (a, b, c) values (50*3, lpad('A', 100, 'A'), sysdate);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
HELLODBA.COM> alter session set events 'immediate trace name treedump level 199127';
Session altered.
可以看到,发生分裂的数据块的空闲率为556/2048=27.1%,可见PCTFREE(60)也被忽略了。
kdxcofbo 58=0x3a
kdxcofeo 614=0x266
kdxcoavs 556
...