技术开发 频道

B-Tree 索引中的数据块分裂

  我们知道,在表的数据块中,当数据插入时,要保证数据块上剩余空间大于、等于PCTFREE的比例设置,以用于数据更新和多事务处理,从而减少数据迁移(Row Migration)的发生;而当分配新的数据块时,会根据INITRANS的设置预留相应的ITL slot,保证并发事务能分配到ITL slot。

  在索引中,这两个参数仅在有数据时创建或重建索引才会起作用,且仅在叶子节点上起作用。

  INITRANS

  INITRANS在索引数据块上是否起作用,是由索引在创建或重建时是否有数据(即是否会分配数据块)决定的。比较以下代码,第一段代码在truncate之后rebuild(即不会分配索引数据块),因而ITL slot数量为默认值2;第二段代码在有数据时rebuild,然后再truncate,此时再插入数据产生的新的索引块上ITL slot数量就受到INITRANS的控制:

--代码1:

  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:

 HELLODBA.COM> truncate table idx_split;

  
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:

 -- Trans 1

  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.

  然后将它们全部提交或回滚,再插入数据,造成分裂:

 --9-1分裂

  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)的数量:

Block header dump: 0x03c0041d

  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分裂: 

 HELLODBA.COM> begin

  
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

  ...
0
相关文章