Oracle分区索引

概述

  索引就是通过事先排好序,从而在查找时可以应用二分查找等高效率的算法,索引也可以简单理解成字典的目录,通过目录定位到详细的内容页。索引可分为分区索引和非分区索引。分区索引一般用在分区表中,关联表中的某个或者某些字段,来提高表数据的查询效率。分区索引通过使用结构可以分为有前缀(prefix)的分区索引和无前缀(nonprefix)的分区索引。通过使用范围又可以分为分为本地(local index)索引和全局索引(global index)。局部索引比全局索引容易管理, 而全局索引比较快。

与索引信息有关的系统表:

    dba_part_indexes 分区索引的概要统计信息,可以得知每个表上有哪些分区索引,分区索引的类型(local/global)。

    dba_ind_partitions 每个分区索引的分区级统计信息,记录分区索引分区信息;

    dba_indexes 可以得到每个表上有哪些索引信息包括分区索引和非分区索引;

    Local索引肯定是分区索引,Global索引可以选择是否分区,如果分区,只能是有前缀的分区索引。

创建基本的分区表

create table T_TESTPDBA (id number, time date,statue number) 
partition by range (time) ( partition p1 values less than (to_date(
'2017-01-1', 'yyyy-mm-dd')), partition p2 values less than (to_date('2018-01-1', 'yyyy-mm-dd')), partition p3 values less than (to_date('2019-01-1', 'yyyy-mm-dd')), partition p4 values less than (maxvalue) )

查询是否创建成功

---记录表分区信息
select
table_name,partition_name,tablespace_name from user_tab_partitions where table_name='T_TESTPDBA';
---记录表信息
select owner,table_name,tablespace_name,cluster_name from all_tables where table_name='T_TESTPDBA';

 

局部索引(Local Index)

  对于局部索引,oracle会自动维护分区索引的信息,局部索引默认是有前缀的分区索引;

创建局部索引

create index IX_TESTPDBA_ID on t_testpdba(id) local;

由于Oracle自动维护局部索引信息,所以上面语句和下面语句同等效果

create index IX_TESTPDBA_ID on T_TESTPDBA(id) local 
(
   partition p1,
   partition p2,
   partition p3,
   partition p4
);

查询索引信息

select t.owner,t.index_name,t.table_name,t.locality,t.alignment,t.table_name,t.index_name from dba_part_indexes  t where t.table_name='T_TESTPDBA';

查询索引分区详细

select * from dba_ind_partitions  t where t.index_name='IX_TESTPDBA_ID';

通过查询,可以看见系统维护了4个索引分区信息。由于Oracle自动维护局部索引分区信息,不论表分区发生CUD操作,Oracle都会自动维护

表分区删除,Oracle自动维护索引分区

alter table T_TESTPDBA drop partition p3;

表分区拆分,Oracle自动维护索引分区

ALTER TABLE T_TESTPDBA SPLIT PARTITION P4 AT(TO_DATE('2020-01-01','YYYY-MM-DD')) INTO (PARTITION P41,PARTITION P5); 

表分区合并,Oracle自动维护索引分区

ALTER TABLE T_TESTPDBA merge partitions P41,P5 into partition P4;

表分区表空间转移,Oracle自动维护索引分区

--移动分区到特定表空间
alter table T_TESTPDBA move partition p3 tablespace EINVOICEDATA;

全局索引(global index)

    Global索引可以选择是否分区,如果分区,只能是有前缀的分区索引。Oracle不自动维护表的索引信息;

有前缀(prefix)的分区索引和无前缀(nonprefix)的分区索引的区别

(1)有前缀的分区索引指包含了分区键,并且将其作为引导列的索引。

create index IX_TESTPDBA_ID_GLOBAL on t_Testpdba(time) global --time是引导列
partition by range(time)                      --time分区列
(
  partition p1 values less than(to_date('2019-01-1', 'yyyy-mm-dd')),
  partition p2 values less than(maxvalue)
);

(2)无前缀的分区索引的列不是以分区键开头,或者不包含分区键列。

create index IX_TESTPDBA_ID_GLOBAL on t_Testpdba(id) global  --id是索引列
partition by range(time)                      --time分区列
(
  partition p1 values less than(to_date('2019-01-1', 'yyyy-mm-dd')),
  partition p2 values less than(maxvalue)
);

     全局分区索引不支持非前缀的分区索引,如果创建,系统会提示GLOBAL 分区索引必须加上前缀

全局索引注意事项

(1)全局索引可以分区,也可以是不分区索引,全局索引必须是前缀索引,即全局索引的索引列必须是以索引分区键作为其前几列。

(2)全局索引可以依附于分区表;也可以依附于非分区表。

(3)全局分区索引的索引条目可能指向若干个分区,因此,对于全局分区索引,即使只截断一个分区中的数据,都需要rebulid若干个分区甚至是整个索引。

(4)全局分区索引只按范围或者散列分区。

(5) oracle9i以后对分区表做move或者truncate的时可以用update global indexes语句来同步更新全局分区索引,用消耗一定资源来换取高度的可用性。

(6) 表用a列作分区,索引用b做局部分区索引,若where条件中用b来查询,那么oracle会扫描所有的表和索引的分区,成本会比分区更高,此时可以考虑用b做全局分区索引。

全局索引重构

  Oracle 会自动维护分区索引,对于全局索引,如果在对分区表操作时,没有指定update index,则会导致全局索引失效,需要重建。对于分区索引,不能整体进行重建,只能对单个分区进行重建。

语法如下:

Alter index idx_name rebuild partition index_partition_name [online nologging]

语法说明:

  (1)online:表示重建的时候不会锁表。

  (2)nologging:表示建立索引的时候不生成日志,加快速度。

使用举例:

Alter index IX_TESTPDBA_ID_GLOBAL rebuild partition p3 online nologging;

索引查询使用

  正常情况下,我们只需要在查询条件中带上索引字段,该查询语句便可以走索引查询而不是全表扫描,使用事例如下:

SELECT COUNT(0)
FROM "T_TESTPDBA" "T0"
WHERE "T0"."TIME" >= to_date('2016/11/1 0:00:00', 'yyyy-MM-dd hh24:mi:ss') 
AND "T0"."TIME" <= to_date('2017/1/13 23:59:59', 'yyyy-MM-dd hh24:mi:ss') AND "T0"."STATUE" = 12

索引失效  

  可是在Oracle中,如果查询的结果内容过多,Oracle便不会走索引查询,也就是索引失效,一般索引失效的情况如下:

(1) 没有查询条件,或者查询条件没有建立索引 ;
(2) 在查询条件上没有使用引导列 ;
(3) 查询的数量是大表的大部分,应该是30%以上;
(4) 索引本身失效;
(5) 查询条件使用函数在索引列上;
(6) 统计数据不真实 ;
(7) CBO计算走索引花费过大的情况。其实也包含了上面的情况,这里指的是表占有的block要比索引小;
(8) 隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误. 由于表的字段tu_mdn定义为varchar2(20), 但在查询时把该字段作为number类型以where条件传给Oracle,这样会导致索引失效.
  错误的例子:select * from test where tu_mdn=13333333333;
  正确的例子:select * from test where tu_mdn='13333333333';
(9) 对索引列进行运算导致索引失效,我所指的对索引列进行运算包括(+,-,*,/,! 等)
  错误的例子:select * from test where id-1=9;
  正确的例子:select * from test where id=10;
(10)使用Oracle内部函数导致索引失效.对于这样情况应当创建基于函数的索引.
  错误的例子:select * from test where round(id)=10; 说明此时id的索引已经不起作用了。
  正确的例子:首先建立函数索引,create index test_id_fbi_idx on test(round(id));
  然后 select * from test where round(id)=10; 这时函数索引起作用了;
(11)查询中使用<> 、单独的>、<、like "%_" 百分号在前;
(12)B-tree索引 is null不会走,is not null会走,位图索引 is null,is not null 都会走;
(13)单独引用复合索引里非第一位置的索引列;

强制使用索引查询

  可是有时候,我们查询希望走索引,那么就需要使用+INDEX,使用举例如下

SELECT /*+INDEX(T0 IX_TESTPDBA_TIME_LOCAL)*/  COUNT(0)
FROM "T_TESTPDBA" "T0"
WHERE "T0"."TIME" >= to_date('2017/1/1 0:00:00', 'yyyy-MM-dd hh24:mi:ss') 
AND "T0"."TIME" <= to_date('2017/1/13 23:59:59', 'yyyy-MM-dd hh24:mi:ss') AND "T0"."STATUE" = 12

注意/*+INDEX(T0 IX_TESTPDBA_TIME_LOCAL)*/  前后有空格,T0是表的别名,IX_TESTPDBA_TIME_LOCAL指定查询要走的索引名称。

 

posted @ 2017-01-08 23:07  jiajinhao  阅读(913)  评论(0编辑  收藏  举报