索引的分裂行为
当某个索引块中要插入新的索引条目,但其中又没有可用空间时,就会发生索引的分裂。根据分裂发生所在的索引块类型的不同,可以分为在根块上发生的分裂,在分支块上发生的分裂和在叶子块上发生的分裂。下面,就这三种情况做分别介绍。
从前面的实验中,我们已经看到,大约每个索引块中可以放下13个索引条目。所以,我们通过以下SQL,创建了新的测试表和索引:
图 98
查看此时的索引树形结构信息,如下图所示:
图 99
如上图所示,目前索引中只有一个索引块,是根块,分支块和叶子块三合一的状态。
查看此时该索引块中的数据内容:
图 100
如上图所示,我们可以看到此时索引块中共有13个索引条目(kdxconro 13)和1327个字节的可用空间(kdxcoavs 1327)。索引条目中的内容如下:
图 101
如上图所示,索引条目中的最小值是’005’结尾的字符串,最大值是’065’结尾的字符串。从row#0到row#12,共13条索引条目。
1 根块的分裂
现在,我们向测试表中再插入一行,看看是否会触发分裂。从索引块中还有1327个字节的可用空间,而一个索引条目的大小为511个字节来看,应该不会导致分裂。
图 102
此时,查看索引的树形结构信息:
图 103
如上图所示,确实没有发生分裂。再查看此时该索引块中的数据内容:
图 104
如上图所示,索引块中的可用空间由1327个字节,降到了814个字节,减少了513个字节(多出的2个字节,是索引块中的管理字节,用于维护索引条目的逻辑顺序和物理存储位置的相互关系。)。索引条目也变化为如下内容:
图 105
如上图所示,row#12是我们新插入的值。
现在,我们继续插入,看看会不会分裂。至少,从可用空间上看,足够放下一个新索引条目所需的空间。
图 106
此时,查看索引的树形结构信息:
图 107
果然没有发生分裂,我们继续再插入一行,这次应该会发生分裂了,因为可用空是已经不够容纳下一条索引条目了。
图 108
查看现在的索引的树形结构信息:
图 109
如上图所示,此时,索引发生了分裂,变成了2层的索引结构。1个根块和2个叶子块。同时,我们注意到,此时的根块,仍是原来的根块,并没有因为分裂,而产生新的根块。而且,两个叶子块中,一个有11个索引条目,一个有5个索引条目。
我们来从根块上看一下,最后一个叶子块中的最小值是多少?
图 110
如上图所示,后一个叶子块中的最小时是以’6x’结尾的字符串。综上,我们可以看到根块分裂时,并不是严格地按照一半的索引条目数来分裂的。
2 叶子块的分裂
我们继续向表中插入记录,目的是引起叶子块上的分裂。从前面根块中获取的信息可知,第一个叶子块中有11个索引条目,而一个索引块中,最多可以存储15个索引条目。因此,我们决定向第一个叶子块所在索引条目的值范围内,即小于’6x’结尾的字符串值内,再插入4行:
图 111
此时,查看索引的树形结构信息,如下所示:
图 112
如上所示,我们可以看到新插入的4行,已经全部按照我们的预期,插入到了第一个叶子块中。现在我们再向其内插入一个索引条目,来引发叶子块级的索引分裂:
图 113
查看此时索引的树形结构信息:
图 114
如上图所示,我们发现叶子块由2个,增加到了3个。而且,从叶子块的dba地址可以看出,在叶子块层的分裂,是增加一个新的叶子块,将被分裂块中的一部分索引条目,移到新的叶子块中。同时,因为多了一个叶子块,所以,叶子块层的上一层中,会增加相应索引条目来指向这个新的叶子块。我们来看一下此时根块中的数据内容:
图 115
如上图所示,根块中增加一个索引条目。其将原来的row#0挤到了row#1,自己占据了row#0的位置。
对于这种近似将原叶子块中的内容一分为二的分裂形式,我们称之为50-50分裂。与之对应,还有一种相对少见的分裂形式90-10分裂。从这个名称中,我们可以推测出,这种分裂发生时,绝大部分的索引条目仍保留在原叶子块中,而只有很少部分的索引条目会保存到新叶子块中。下面,我们来模拟一下90-10分裂。
我们继续向最后一个叶子块中插入索引条目,使其达到分裂前的上限—15个索引条目:
图 116
查看此时的索引树形结构信息:
图 117
如上图所示,我们可以看到最后一个叶子块中已经有15个索引条目了。我们来看一下此时表中C1的最大值,并插入一个更大的值:
图 118
此时,查看索引的树形结构信息:
图 119
如上图所示,我们发现现在有4个索引块了。通过与分裂前的树形结构信息对比,并从索引块的dba地址,我们可以确认最后一个叶子块是新增的。而且,我们可以看到新增的叶子块中,只有1条索引条目。根据索引有序的特点,我们可以推断最后一个叶子块上的这条索引条目的值,应该就是我们刚刚插入的’156’结尾的字符串。我们来验证一下:
图 120
如上图所示,我们可以看到该叶子块的索引条目,确实就是我们刚刚插入的值。
总结一下,当我们向最后一个叶子块中插入一个最大值,并且因此而导致叶子块分裂时,Oralce会采用90-10的分裂形式。简化一下,当我们向索引列上插入一个最大值,并因此导致索引叶子块分裂时,就会出现90-10的分裂。发生90-10分裂时,原叶子块中的索引条目不动,直接生成新的叶子块,并将新索引条目插入到该新生成的叶子块中。这种形式,对于索引列是单调递增的列是非常有益的。比如我们用序列值填充的ID列,或者总是记录当前时间值的日期列。如果不采用90-10分裂,而仍采用50-50的分裂形式,我们可以知道,被分裂的块中大致会有一半的空间被浪费,因为新插入的值总是更大的值,是不会落入用于存储较小值的前一个叶子块的。
3 分支块的分裂
为了观察分支块的分裂,我们创建一个有三层的索引。创建过程如下:
图 121
查看此时索引的树形结构信息:
图 122
如上图所示,我们可以看到此时是一个3层高的索引,根块,分支块和叶子块俱全。而且,我们注意到,第一个分支块(“branch: 0x1844b8e 25447310 (-1: nrow: 16, level: 1)”)中已经有16个叶子块了,根据我们此前实验的经验,该分支块应该是已经没有空间可以再容纳一个新的叶子块了。我们来实际查看一下该分支块中的数据:
图 123
如上图所示,该分支块中只有402个字节的可用空间了。我们再来看一下该分支块的索引条目部分:
图 124
如上图所示,这里显示每个索引条目至少要占用499个字节,所以,只要再增加一个索引条目(由于本索引块是分支块,所以,新增一个索引条目,就是要再增加一个叶子块。),就会导致该分支块分裂。同时,我们也可以注意到,在row#1中记录的最小值是’135’结尾的字符串,而row#0中记录的最小值是’07x’结尾的字符串。所以,我们只要在这个范围内插入新记录,就最终会导致在本分支块之下的row#0的叶子块发生分裂,进而需要增加一个叶子块。
我们再来看一下row#0所指向的叶子块中,还有多少可用空间:
图 125
如上图所示,该叶子块中,还有1327个字节的可用空间(“kdxcoavs 1327”)。因此,再插入3行,就会导致该叶子块分裂,进而导致其所属的分支块也发生分裂。
图 126
如上图所示,我们向该叶子块中,插入了三行记录。现在,来看一下索引的树形结构,是否发生了分支块的分裂:
图 127
如上图所示,分支块已经从之前的2个,增加到了3个。与分裂前的树形结构信息(如下图所示)相比较:
图 128
我们可以发现“branch: 0x1844b91 25447313 (0: nrow: 7, level: 1)”即为新增加的分支块。而该分支块下所指向的叶子块全部是原来“branch: 0x1844b8e 25447310 (-1: nrow: 10, level: 1)”下面的叶子块。所以,可以确认,是该分支块发生了分裂。该分支块分裂时,也像叶子块分裂一样,是增加一个新的分支块,将原分支块中的部分内容移到新的分支块上。由于我们新插入的值的范围,是在原分支块所指向的范围内,因此,新增加的叶子块“leaf: 0x1844b90 25447312 (1: nrow: 7 rrow: 7)”出现在原分支块下。
4 索引分裂的简要总结
回顾整个索引分裂的实验,我们对实验中验证的主要分裂方式做一个简要的总结;
1、 根块位置不动,即便发生根块的分裂,也是将原根块中的内容移到新生成的下一层的分支块中。
2、 叶子块分裂时,会新增加一个叶子块,将原叶子块中的部分内容,移到新的叶子块中。但有一个特例,即当导致叶子块分裂的索引条目,是当前表中索引列的最大值,会发生90-10分裂。此时,新增加的叶子块只放新插入的索引条目,而分裂的叶子块中的索引条目保持不变。
3、 分支块的分裂与叶子块的分裂类似。
4、 分支块的分裂取决于其下层叶子块的分裂,而根块的分裂,又取决于下层分支块的分裂。因此,在特定条件下,会发生连锁分裂,即叶子块发生了分裂,导致了其上层分支块的分裂,又进而导致其上一层分支块,直至根块的分裂。而且,当这种连锁分裂发生时,如果在最初的叶子块上的分裂是以90-10的方式分裂的,则本次连锁分裂中的分支块和根块的分裂,也将是90-10的方式分裂。
3.5 如何判断索引分裂
判断索引分裂的方法比较简单,AWR中的 Instance Activity Stats 统计信息中就有采样时间段内数据库共发生多少次index split。
图 129
上图是某个生产环境的AWR信息中 Instance Activity Stats 信息,采样时间内leaf node splits 共有156008次,其中包含 90-10 分裂和 50-50 分裂, 90-10 分裂有21255次,那剩下的就全部是 50-50 分裂了(leaf node splits - leaf node 90-10 splits)。这里需要注意,如果在索引分裂中 leaf node 和 branch node 同时发生,那分裂的类型是相同的,即要么都是 90-10 分裂,要么都是 50-50 分裂。
AWR中的这些索引分裂统计信息数值也可以通过动态性能视图 v$sesstat 和 v$sysstat 来观察索引分裂的情况,查看当前会话的索引分裂的统计量:
select se.value,sy.name
from v$sesstat se ,v$sysstat sy
where se.statistic#=sy.statistic#
and name like '%split%'
and sid=(select distinct sid from v$mystat);
索引分裂往往会有enq: TX- index contention等待事件的出现,通过观察v$session 或 v$active_session_history 亦或是AWR中的 TOP Event ,我们也可以判断出数据库中的索引分裂是否严重,是否已经对数据库整体性能造成很大的性能威胁。
标签:--,tree,条目,叶子,索引,分裂,如上图,分支 From: https://blog.51cto.com/u_13482808/8228579