B树索引重构原因和目的
B树索引是最为常用的索引,create index idx1 on xxx就是创建的B树索引。
在平常工作和案例中,对于index经常会有rebuild操作,也就是索引重建
1.那么索引重建的原因和目的是什么?
2.什么时候应该重建索引?
要回答上面的问题,我们需要先了解什么是B树索引
B树索引结构:
(图片来自《基于Oracle的SQL优化》)
B树索引的结构特性:
1)访问B树索引必须从根节点开始,从根节点到分支块到叶子块,从叶子块中找到rowid再回表去访问行记录。
2)所有叶子块都在同一层,叶子块数据是排序的,从左向右递增;
3)通过B树索引访问行纪录的效率不会因为数据量的递增而显著降低,而全表扫描则会显著增加
4)在分支块和根块中放的是索引的范围
5)B-Tree索引由“索引列+rowid”组成,必须经过两次逻辑读。第一次读取索引段,第二次使用索引rowid读取数据段。
B树索引的统计信息:
索引统计信息可以查看dba_indexes,index_stats两个数据字典1)BLEVEL(dba_indexes中blevel字段)+1=HEIGHT(index_stats中的height字段)。blevel的值越大,从根节点到叶子块所需访问的块数越多,耗费的IO就越多,成本越大
height相当于B树的高。当height=0时,B树索引只要一层,也就是根节点和叶子块为同一个块。
2)b树索引rebuild可以更改b树索引的结构
3)avg_leaf_blocks_per_key每个distinct索引键值占用叶子块数量的平均值,对于唯一索引而言该值为1
4)leaf_blocks:叶子块数量,直接影响索引全扫描的成本计算,也会影响索引范围扫描的成本
5)cluster_factor:聚簇因子。按索引键值排序的索引行和表中数据行的存储顺序相似度
知道了B树索引的结构和访问方式以及B树索引的统计信息,我们用一个示例来解释rebuild到底在干什么
示例:rebuild索引改变B树索引的结构
select index_name,blevelfrom dba_indexes where index_name='IDX_LIU_IDXH';
SQL> /
IDX BLEVEL
-------------------- ----------
IDX_LIU_IDXH 1
--blevel=1表示B树索引有2层
select name,height,lf_rows,lf_blks,del_lf_rows from index_stats ;
NAME HEIGHT LF_ROWS LF_BLKS DEL_LF_ROWS
-------------------- ---------- ---------- ---------- -----------
IDX_LIU_IDXH1 2 72630 161 0
SQL> select name,height,lf_rows,lf_blks,del_lf_rows fromindex_stats ;
NAME HEIGHT LF_ROWS LF_BLKS DEL_LF_ROWS
-------------------- ---------- ---------- ---------- -----------
IDX_LIU_IDXH1 2 72630 161 0
--删除表内的数据
SQL>delete from liu_idxh1;
SQL>commit;
Commitcomplete
--此时索引块的数据还没有删除
SQL>select name,height,lf_rows,lf_blks,del_lf_rows from index_stats ;
NAME HEIGHT LF_ROWS LF_BLKS DEL_LF_ROWS
------------------------------ ---------- ---------- -----------
IDX_LIU_IDXH1 2 72630 161 0
SQL> analyze index idx_liu_idxh1 validate structure; --仅仅是重新收集包含索引结构的统计信息,并未改变索引结构
Index analyzed.
SQL> select name,height,lf_rows,lf_blks,del_lf_rows fromindex_stats ;
NAME HEIGHT LF_ROWS LF_BLKS DEL_LF_ROWS
-------------------- ---------- ---------- ---------- -----------
IDX_LIU_IDXH1 2 72630 161 72630
--height和lf_rows叶节点行数没变,只增加了DEL_LF_ROWS叶节点删除行数。
--其实这里就是问题所在,表内数据改变(比如删除)后,数据块已经回收了,但索引行仍然存在,如果执行计划继续走索引,效率将很低。
SQL> alter index idx_liu_idxh1 rebuild;
Index altered.
SQL> select name,height,lf_rows,lf_blks,del_lf_rows fromindex_stats ;
no rows selected -- 重建索引后索引的统计信息不存在,重新收集统计信息
SQL> analyze index idx_liu_idxh1 validate structure;
Index analyzed.
SQL> select name,height,lf_rows,lf_blks,del_lf_rows fromindex_stats ;
NAME HEIGHT LF_ROWS LF_BLKS DEL_LF_ROWS
-------------------- ---------- ---------- ---------- -----------
IDX_LIU_IDXH1 1 0 1 0
此时height为1,也就是只有根节点,叶子块数也从161降为1,成本也大大降低了
最后来回答上面两个问题
1.索引重构是因为在表数据更新后,索引结构不会更改,此时的索引结构不适合访问该表的数据,在重建了索引以后,索引结构发生变化,此时的B树结构去访问表数据效率更高
2.在经常有dml操作的表上的索引需要定期rebuild,且应在业务低峰期进行
参考资料:
《基于Oracle的SQL优化》