日期:2014-05-16  浏览次数:20403 次

oracle 索引介绍(二)

????? 索引也可以像表分区结构那样进行分区。有多种方法可以对索引进行分区。在分区表上可以创建局部或全局索引。并且,有多种分区方案可选,例如范围分区、散列分区、列表分区以及混合分区方案。自ORACLE数据库10g版本以来,也可以在非分区表上建立分区索引。

?

  • 局部索引(TABLE ACCESS BY LOACL INDEX ROWID)

?

???? 局部分区索引使用LOCAL关键字来建立,其分区边界与表相同。简单来说,与每个表分区相联结的有一个索引分区。因为维护操作可以在独立分区级进行,表的可用性更好。对索引分区的维护操作仅需要锁定相应的表分区而不是整张表。尽管表的可用性很重要,你仍然应该考虑另外一点:如果谓语没有声明分区键列,那么在局部索引中必须访问所有索引分区以识别候选的数据行。如果分区数非常多,达到几千的量级的话,这有可能会导致性能问题的现出。即使这样,你也需要衡量创建局部索引而不是全局索引所带来的影响。

?

  • 全局索引(TABLE ACCESS BY GLOBAL INDEX ROWID)

?

????? 全局索引通过关键字GLOBAL来创建。在全局索引中,索引的分区边界与表的分区边界不一定要匹配,并且表和索引的分区键也可以不一样。对全局索引的任何维护都将需要获得表的较高层级的锁,从而降低了应用的可用性。相反,对于局部索引的维护可以只在分区级上完成,只会影响相应的表分区。

?

  • 散列分区与范围分区

?

???? 在散列分区方案中,分区键列的值使用散列算法进行散列化来确定存储数据行的分区。这种类型的分区方案适合于分区列使用人造键值填充的情况,例如分区列由顺序生成的值填充。如果列值的分布是均匀的,那么每个分区将会存储几乎相等数目的数据行。散列分区方案还有几个额外的优点,范围分区方案有一些管理开支,因为在将来要想放入新的数据行就需要定期加入新的分区。例如,如果分区键是ORDER_DATE列,那么就必须增加新分区来放放将来日期的数据。而在散列分区方案中,这个开支就避免了,因为数据行在使用散列算法进行分区的各个分区上是均匀分布的。如果列值是均匀分布的,则所有分区将保存近似相等数量的数据行,也就没有理由定期增加更多新的分区了。

?

????? 散列分区表和索引在应对由唯一索引和主键索引所引起的与并发性相关的性能问题时是非常有效的。典型的主键列可能会使用生成的顺序序列值来填充。因为索引按照排序后的顺序存储列值,新数据行的列值将会进入到索引最右侧的叶子块。在该叶子块满了以后,接下来插入的数据行就会进入新的最右侧叶子块中,资源争夺点也就从一个叶子块转移到了另一个叶子块。随着表的插入并发性增加,会话将会大幅修改索引的最右侧块。基本上,索引的当前最右侧叶子块将会是最主要的资源争夺点。你将会看到会话等待事件,例如缓冲区繁忙等待。在RAC中,由于全局缓存通信成本导致了这个问题的放大,GC缓冲器繁忙将会是排在第一位的等待事件。这种类型的最右侧索引的快速增长被称为"右侧增长索引"。与右侧增长索引相关的并发性问题可以通过将索引散列分区到多个分区表中来消除。例如,如果索引被散列化到32个分区中,那么插入操作将会被有效地分散到32个最右侧叶子块中,因为有32个索引树。使用散列分区方案来对表进行分区,然后再在分区后的表上创建局部索引也将具有同样的效果。

?

查看散列分区分布:

select dbms_rowid.rowid_object(rowid) obj_id, count(*)
  from test
 group by dbms_rowid.rowid_object(rowid);

?

????? 从本质上来说,通过对表进行分区并将右侧增长索引创建为局部索引可以增加并发性。如果表不能进行分区,那么也可以单独对索引使用散列分区方案进行分区来解决性能问题。

?

  • 压缩索引

?

????? 压缩索引是常规B-树索引的变体。这种类型的索引更适合于引导列中具有重复值的列。通过将引导列中的重复值在索引叶子块中仅保存一次来实现压缩。数据行区的指针指向这些前置行,避免在数据行显式存储这些重复值。如果列具有很多重复值的话,与常规的索引相比压缩索引可能小很多。在处理压缩索引的时候CPU使用率可能会略有上升,这可以很安全地忽略。

?

select 'compressed index size(MB) :' || trunc(bytes / 1024 / 1024, 2)
  from user_segments
 where segment_name = 'T';
 
 create index t_ind_bill_month on t(bill_month) compress 1;
 
 select 'compressed index size(MB) :' || trunc(bytes / 1024 / 1024, 2)
  from user_segments
 where segment_name = 'T';

?

很明显,数据自身的特点对压缩比起着非常重要的作用。如果列值重复的次数越高,则索引压缩就能得到越多的益处。如果没有重复数据,则压缩索引可能比常规索引还要大。因此,压缩索引适合于引导列具有较少唯一值的索引。dba_indexes/user_indexes视图中的compression和prefix_length列显示了索引的压缩属性。

?

最优压缩列数:

analyze index t_ind_bill_month validate structure;
select opt_cmpr_count, opt_cmpr_pctsave
  from index_stats
 where name = upper('t_ind_bill_month');

?注意:analyze index validate structure语句需要对表的共享级锁,可能会引起应用停机。

?

  • 基于函数的索引

?

?????? 如果一个谓语在索引列上应用了函数,则优化器不会选用该列上的索引。例如,对谓语TO_CHAR(ID)='100',不会选用ID列上的索引,因为在索引列上应用了TO_CHAR函数。这个限制可以通过在表在式TO_CHAR(ID)上创建基于函数的索引来克服。基于函数的索引预存函数的结果。谓语中所声明的表达式必须与基于函数的索引所声明的表达式相匹配。基于函数的索引也可以建立在用户自定义的函数上,但这个函数必须定义为确定性函数,也就是说对这个函数的每一次执行必须返回一致的值。不遵守这一规则的用户自定义函数不能用来创建基于函数的索引。

?

虚拟列与基于函数的索引
select data_default, hidden_column, virtual_column
  from dba_tab_cols
 where table_name = 'T'
   and virtual_column = 'YES';

select index_name, column_name
  from dba_ind_columns
 where index_name = 'T_IND_BILLNO';

?

在增加了基于函的索引后收集表的统计信息是很重要的。如果不收集,新的虚拟列就没有统计信息,这有可能会导致性能异常。

分析脚本:
SQL> begin
  2  dbms_stats.gather_table_stats(
  3  ow