【IT168 技术文档】什么是B-tree索引块分裂
当一个事务需要修改(大多数情况是Insert操作,某些情况下也可能为Delete操作)索引块(枝节点或叶子节点)上的数据,但没有足够空间容纳新的数据(包括索引条目、ITL slot)时,会将原有块上的部分数据放到一个新的数据块上去,这一过程就是索引块分裂(Index Block Splitting)。
什么情况下发生索引块分裂
按照分裂的对象不同,分为叶子节点分裂和枝节点分裂,而枝节点分裂中还有一个特殊的分裂:根节点分裂。
按照分裂时,2个数据块上分布的数据比例,分为5-5分裂和9-1分裂:
5-5分裂:新旧2个数据块上的数据基本相等;
9-1分裂:大部分数据还在原有数据块上,只有少量数据被转移到新的数据块上。
叶子节点分裂
1、当Insert、Update(实际上就是Delete+Insert)时,叶子节点块上没有足够空间容纳新的索引条目,就会发生叶子节点分裂:
HELLODBA.COM> create table idx_split (a number, b varchar2(1446), c date);
Table created.
HELLODBA.COM> create index idx_split_idx on idx_split (a, b) tablespace idx_2k pctfree 10;
Index created.
HELLODBA.COM> begin
2 for i in 1..1000
3 loop
4 insert into tx_index_contention (a, b, c) values (i, lpad('A', 10, 'A'), sysdate);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
HELLODBA.COM> commit;
Commit complete.
HELLODBA.COM> alter session set events '10224 trace name context forever,level 1';
Session altered.
--叶子节点没有足够空间,发生分裂
HELLODBA.COM> insert into idx_split (a, b, c) values (800, lpad('A', 20, 'A'), sysdate);
1 row created.
Table created.
HELLODBA.COM> create index idx_split_idx on idx_split (a, b) tablespace idx_2k pctfree 10;
Index created.
HELLODBA.COM> begin
2 for i in 1..1000
3 loop
4 insert into tx_index_contention (a, b, c) values (i, lpad('A', 10, 'A'), sysdate);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
HELLODBA.COM> commit;
Commit complete.
HELLODBA.COM> alter session set events '10224 trace name context forever,level 1';
Session altered.
--叶子节点没有足够空间,发生分裂
HELLODBA.COM> insert into idx_split (a, b, c) values (800, lpad('A', 20, 'A'), sysdate);
1 row created.
在10224事件的trace文件中可以看到叶子节点块分裂的记录:
splitting leaf,dba 0x03c00557,time 12:44:01.652
kdisnew_bseg_srch_cbk reject block -mark full,dba 0x03c0054a,time 12:44:01.699
kdisnew_bseg_srch_cbk rejecting block ,dba 0x03c0054a,time 12:44:01.699
kdisnew_bseg_srch_cbk using block,dba 0x03c0054b,time 12:44:01.699
kdisnew_bseg_srch_cbk reject block -mark full,dba 0x03c0054a,time 12:44:01.699
kdisnew_bseg_srch_cbk rejecting block ,dba 0x03c0054a,time 12:44:01.699
kdisnew_bseg_srch_cbk using block,dba 0x03c0054b,time 12:44:01.699
同时,将Btree结构dump出来,也可以看到节点被分裂:
HELLODBA.COM> alter session set events 'immediate trace name treedump level 198801';
Session altered.
Trace文件:
leaf: 0x3c00557 62915927 (14: nrow: 31 rrow: 31)
leaf: 0x3c0054b 62915915 (15: nrow: 21 rrow: 21)
Session altered.
Trace文件:
leaf: 0x3c00557 62915927 (14: nrow: 31 rrow: 31)
leaf: 0x3c0054b 62915915 (15: nrow: 21 rrow: 21)
2、当事务需要修改节点上的数据,叶子节点上没有足够空间容纳新的ITL slot时,也会发生分裂。
我们dump出一个“满”的节点,注意到它上面的空闲空间只有20字节,小于一条ITL slot的大小(24字节)
Block header dump: 0x03c00551
Object id on Block? Y
seg/obj: 0x30892 csc: 0x00.b10c56ed itc: 2 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x3c00542 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x00b0.01d.00000009 0x00800b1e.0021.02 -BU- 1 fsc 0x0000.b10c56f0
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
...
kdxconro 51
kdxcofbo 138=0x8a
kdxcofeo 158=0x9e
kdxcoavs 20
...
Object id on Block? Y
seg/obj: 0x30892 csc: 0x00.b10c56ed itc: 2 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x3c00542 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x00b0.01d.00000009 0x00800b1e.0021.02 -BU- 1 fsc 0x0000.b10c56f0
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
...
kdxconro 51
kdxcofbo 138=0x8a
kdxcofeo 158=0x9e
kdxcoavs 20
...
并且此时它里面有一条空闲ITL slot(第一条ITL slot是用于递归事务的,后面会有解释),先用一个事务占用它:
HELLODBA.COM> delete from idx_split where a=500;
1 row deleted.
1 row deleted.
然后再启动一个事务,造成了空间不足分配新的ITL slot,而导致节点分裂:
HELLODBA.COM> alter session set events '10224 trace name context forever,level 1';
Session altered.
HELLODBA.COM> delete from idx_split where a=501;
1 row deleted.
Session altered.
HELLODBA.COM> delete from idx_split where a=501;
1 row deleted.
在10224trace文件中记录此次分裂:
splitting leaf,dba 0x03c00551,time 12:54:00.827
kdisnew_bseg_srch_cbk using block,dba 0x03c00550,time 12:54:00.874
kdisnew_bseg_srch_cbk using block,dba 0x03c00550,time 12:54:00.874