索引分裂:90-10分裂
Posted forwrader
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了索引分裂:90-10分裂相关的知识,希望对你有一定的参考价值。
数据库的索引基于B-树的结构,通过将数据插入到对应的叶子节点中实现数据新增的功能。更新值的操作是通过"删除"(将值标记为删除)原有的值并在对应的叶子节点上添加修改后的值来实现的。
举个例子,假设现在有一个索引键,并且假设一个数据块只能容纳该索引的4个索引值,然后当前的索引值包含以下这些数据:
Block 1 :
Abacus
Carrot
Block 2 :
Dandelion
Elephant
Heat
Iridium
Block 3 :
Lamb
Militant
Tight
此时,如果想要往这个索引中添加新的索引值 “Fear”, Oracle 需要进行以下的操作步骤:
- 定位正确的插入数据块 (按首字母 a.b.c.d.. 排列,定位的是第二个数据块)
- 验证新的值是否可以插入到步骤一种定位的数据块中 (每个块只能插入4个值)
- 将新的值插入到数据块中或者将数据块进行分裂操作
此时,因为数据块2已经有4个索引值,因此需要做一次索引分裂的操作,分裂后的索引值的分布情况如下:
Block 1 :
Abacus
Carrot
Block 2 :
Dandelion
Elephant
Block 3 :
Fear
Heat
Iridium
Block 4 :
Lamb
Militant
Tight
这个过程就是一个常规的“50-50分裂”的索引分裂过程。
而与此相对应的,是这篇文章要介绍的“90-10分裂”的过程。90-10分裂发生在当新插入的值是当前“最右”的值的时候。简单的说,当这个索引是一个自动正常的类型,比如是一个序列,交易订单的id或者是一个日期类型,交易订单的日期,在这种新插入值总是比当前存在的值都要大的情况下,就会发生90-10分裂,此时,oracle会把“最右”的值复制到分裂产生的新的数据块中。
查询索引分裂产生的数据块的信息,可以通过查询 V$SYSSTAT / V$SESSTAT 表中的 "leaf node splits" 的值来分析。值得注意的是,表中的 leaf node 90-10 splits 这个字段的值,实际上是属于leaf node splits的一个子集,即是说,leaf node splits 的值代表了索引分裂的90-10分裂和50-50分裂的信息。
下面将通过新建一张表,并通过几轮数据新增的操作来展示索引分裂的过程。
Table dropped.
SQL>
SQL> -- REM create the demo table
SQL> create table demo_ibs_90_10 (
2 employee_id number not null,
3 country_name varchar2(10) not null,
4 dept_name varchar2(18) not null,
5 employee_name varchar2(128) not null,
6 join_date date)
7 /
Table created.
创建表的索引
SQL> -- create the index with a PCTFREE of 1 to pack it tightly
SQL> create unique index demo_ibs_90_10_u1 on demo_ibs_90_10 (employee_id,country_name,employee_name) pctfree 1;
Index created.
SQL>
对表进行新增数据操作
SQL> delete source_table;
50653 rows deleted.
SQL> insert into source_table select * from dba_objects where object_id is not null;
50653 rows created.
SQL> select max(object_id) from source_table;
MAX(OBJECT_ID)
--------------
53214
SQL>
然后创建一个新的会话,并进行数据插入操作,并对插入操作进行分析。
SQL> connect scott/tiger
Connected.
SQL> insert into demo_ibs_90_10
2 select object_id, substr(owner,1,10),substr(object_type,1,18),rpad(object_name,20,dbms_random.string(‘X‘,3)),created
3 from source_table
4 where object_id is not null
5 order by object_id
6 /
50653 rows created.
SQL> commit;
Commit complete.
对插入数据进行分析
SQL>
SQL> select sn.name, ms.value
2 from v$statname sn, v$mystat ms
3 where sn.statistic#=ms.statistic#
4 and sn.name like ‘%leaf node %‘
5 order by 1
6 /
NAME VALUE
---------------------------------------------------------------- ----------
leaf node 90-10 splits 262
leaf node splits 262
SQL>
SQL> exec dbms_stats.gather_table_stats(‘‘,‘DEMO_IBS_90_10‘,estimate_percent=>100,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> select num_rows, distinct_keys, blevel, leaf_blocks, distinct_keys/leaf_blocks
2 from user_indexes where index_name = ‘DEMO_IBS_90_10_U1‘
3 /
NUM_ROWS DISTINCT_KEYS BLEVEL LEAF_BLOCKS DISTINCT_KEYS/LEAF_BLOCKS
---------- ------------- ---------- ----------- -------------------------
50653 50653 1 263 192.596958
SQL> analyze index demo_ibs_90_10_u1 validate structure;
Index analyzed.
SQL> select lf_rows, lf_blks, br_blks, del_lf_rows, pct_used from index_stats;
LF_ROWS LF_BLKS BR_BLKS DEL_LF_ROWS PCT_USED
---------- ---------- ---------- ----------- ----------
50653 263 1 0 100
这个分析过程,首先从v$statname , v$mystat表中查询到索引分裂的数量信息,然后通过调用dbms_stats的gather_table_stats函数搜集信息,再通过user_indexes 查询展示搜集的数据信息,最后对索引的结构进行分析操作,并通过index_stats查询索引数据块的信息。
结论:
- 在插入50653数据的过程中,产生了262次索引分裂,且都是90-10分裂
- 产生了263个数据块,且每个数据块中大概有193个索引值
接下来进行第二次数据插入操作,需要重新建立一个新的会话,目的是方便分析插入的索引信息。
SQL> connect scott/tiger
Connected.
SQL>
SQL> declare
2 i number;
3
4 begin
5 for i in 1..1000
6 loop
7 insert into demo_ibs_90_10
8 select object_id+100000+i,substr(owner,1,10),substr(object_type,1,18),rpad(object_name,20,dbms_random.string(‘X‘,3)),created+vsize(object_name)
9 from source_table
10 where object_id is not null
11 and object_id = 1000+i;
12 commit;
13 end loop;
14 end;
15 /
PL/SQL procedure successfully completed.
和之前一样,分析插入数据
SQL>
SQL> select sn.name, ms.value
2 from v$statname sn, v$mystat ms
3 where sn.statistic#=ms.statistic#
4 and sn.name like ‘%leaf node %‘
5 order by 1
6 /
NAME VALUE
---------------------------------------------------------------- ----------
leaf node 90-10 splits 5
leaf node splits 5
SQL>
SQL> exec dbms_stats.gather_table_stats(‘‘,‘DEMO_IBS_90_10‘,estimate_percent=>100,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> select num_rows, distinct_keys, blevel, leaf_blocks, distinct_keys/leaf_blocks
2 from user_indexes where index_name = ‘DEMO_IBS_90_10_U1‘
3 /
NUM_ROWS DISTINCT_KEYS BLEVEL LEAF_BLOCKS DISTINCT_KEYS/LEAF_BLOCKS
---------- ------------- ---------- ----------- -------------------------
51653 51653 1 268 192.735075
SQL> analyze index demo_ibs_90_10_u1 validate structure;
Index analyzed.
SQL> select lf_rows, lf_blks, br_blks, del_lf_rows, pct_used from index_stats;
LF_ROWS LF_BLKS BR_BLKS DEL_LF_ROWS PCT_USED
---------- ---------- ---------- ----------- ----------
51653 268 1 0 100
SQL>
这里,通过新建一个过程,将数据的object_id的值增加100000再进行插入。
可以发现,第二轮插入产生了5次90-10分裂,数据块的数量也增加到了268个。(每个块大概是193个索引值,第二轮中插入了1000个值,所以是分裂了5次)
然后进行第三次的数据插入操作, 操作和上面两轮基本一致。
SQL> connect scott/tiger
Connected.
SQL>
SQL> insert into demo_ibs_90_10
2 select object_id+200000,substr(owner,1,10),substr(object_type,1,18),rpad(object_name,20,dbms_random.string(‘X‘,3)),created+vsize(object_name)
3 from source_table
4 where object_id is not null
5 and object_id between 1000 and 2000
6 /
1001 rows created.
SQL>
SQL> commit;
Commit complete.
SQL>
SQL> select sn.name, ms.value
2 from v$statname sn, v$mystat ms
3 where sn.statistic#=ms.statistic#
4 and sn.name like ‘%leaf node %‘
5 order by 1
6 /
NAME VALUE
---------------------------------------------------------------- ----------
leaf node 90-10 splits 6
leaf node splits 6
SQL>
SQL> exec dbms_stats.gather_table_stats(‘‘,‘DEMO_IBS_90_10‘,estimate_percent=>100,cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> select num_rows, distinct_keys, blevel, leaf_blocks, distinct_keys/leaf_blocks
2 from user_indexes where index_name = ‘DEMO_IBS_90_10_U1‘
3 /
NUM_ROWS DISTINCT_KEYS BLEVEL LEAF_BLOCKS DISTINCT_KEYS/LEAF_BLOCKS
---------- ------------- ---------- ----------- -------------------------
52654 52654 1 274 192.167883
SQL> analyze index demo_ibs_90_10_u1 validate structure;
Index analyzed.
SQL> select lf_rows, lf_blks, br_blks, del_lf_rows, pct_used from index_stats;
LF_ROWS LF_BLKS BR_BLKS DEL_LF_ROWS PCT_USED
---------- ---------- ---------- ----------- ----------
52654 274 1 0 100
SQL>
经过三轮的数据操作,可以发现,对一个单调递增的序列上进行新值的插入,索引分裂是90-10分裂类型的。
思考:
如果在插入的过程中有删除的操作,会发生什么样的情况呢?
实际上,oracle并不总是把新值直接插入到任意的一个数据块中的,即是该数据块当前是90%空闲的。当原有的叶子节点上的旧数据被删除的时候,这个数据块就可以被重复利用,作为当前“最右”的数据块的下一个块(即是说,数据块和索引值并不是物理上的连续的排序,只是逻辑上的往右排序下去)。
以上是关于索引分裂:90-10分裂的主要内容,如果未能解决你的问题,请参考以下文章