全局索引(global)、本地索引(local)
1.1 local索引(局部索引)
1.1.1 local索引仅适用于分区表,如果在未做分区的表上尝试建立local索引将报错
SQL> create table npart_local as select * from all_objects; Table created. SQL> create index idx_npart_local_oid on npart_local(object_id) local; create index idx_npart_local_oid on npart_local(object_id) local * ERROR at line 1: ORA-14016: underlying table of a LOCAL partitioned index must be partitioned
1.1.2对分区表中的某个分区做truncate或者move,shrink等,可能会影响到n个全局索引分区,正因为这点,局部分区索引具有更高的可用性。
SQL> alter table PART_IND_TEST truncate partition p1; Table truncated.
truncated后分区上的local索引状态正常,或最多影响到被truncate的分区
SQL> select index_name,partition_name,status from user_ind_partitions where partition_name like 'P%'; INDEX_NAME PARTITION_NAME STATUS ------------------------------ ------------------------------ -------- IDX_PT_LOCAL_OID P2 USABLE IDX_PT_LOCAL_OID P1 USABLE IDX_PT_LOCAL_OID P3 USABLE
truncated后全局分区不可用
SQL> select table_name,index_name,status from user_indexes where table_name='PART_IND_TEST'; TABLE_NAME INDEX_NAME STATUS ------------------------------ ------------------------------ -------- PART_IND_TEST IDX_PT_UN_RID UNUSABLE --索引失效 PART_IND_TEST IDX_PT_GLOB_ID UNUSABLE --索引失效 PART_IND_TEST IDX_PT_LOCAL_OID N/A
rebuild一下恢复索引
SQL> alter index IDX_PT_UN_RID rebuild; Index altered. SQL> alter index IDX_PT_GLOB_ID rebuild; Index altered. SQL> select table_name,index_name,status from user_indexes where table_name='PART_IND_TEST'; TABLE_NAME INDEX_NAME STATUS ------------------------------ ------------------------------ -------- PART_IND_TEST IDX_PT_UN_RID VALID PART_IND_TEST IDX_PT_GLOB_ID VALID PART_IND_TEST IDX_PT_LOCAL_OID N/A
1.1.3 如何在做此类操作时避免索引失效?
SQL> alter table part_ind_test truncate partition p2 update global indexes; Table truncated. SQL> select table_name,index_name,status from user_indexes where table_name='PART_IND_TEST'; TABLE_NAME INDEX_NAME STATUS ------------------------------ ------------------------------ -------- PART_IND_TEST IDX_PT_UN_RID VALID PART_IND_TEST IDX_PT_GLOB_ID VALID PART_IND_TEST IDX_PT_LOCAL_OID N/A
update global indexes可以解决操作过引起的全局索引失效问题
所以操作中会影响到全局索引时注意加上 update global indexes以免索引失效
local索引在增加分区后会自动创建,分区的global索引则不会
注意:ORA-14038: GLOBAL partitioned index must be prefixed 分区表上的全局分区必须是前缀分区
SQL> create index idx_pt_partglob_name on part_ind_test(name) 2 global partition by range(name) 3 partition idx_pt_pg_name_p1 values less than (10000), 4 partition idx_pt_pg_name_p2 values less than (20000), 5 partition idx_pt_pg_name_p3 values less than (30000), 6 partition idx_pt_pg_name_p4 values less than (maxvalue) 7 ); global partition by range(id) * ERROR at line 2: ORA-14038: GLOBAL partitioned index must be prefixed SQL> drop index IDX_PT_GLOB_ID; Index dropped. SQL> create index idx_pt_partglob_name on part_ind_test(id) 2 global partition by range(id) 3 ( 4 partition idx_pt_pg_name_p1 values less than (10000), 5 partition idx_pt_pg_name_p2 values less than (20000), 6 partition idx_pt_pg_name_p3 values less than (30000), 7 partition idx_pt_pg_name_p4 values less than (maxvalue) 8 ); Index created. SQL> alter table part_ind_test split partition p4 at(40000) into (partition p4,partition p5) update global indexes; Table altered. SQL> select index_name,partition_name,status from user_ind_partitions where index_name='IDX_PT_PARTGLOB_NAME'; INDEX_NAME PARTITION_NAME STATUS ------------------------------ ------------------------------ -------- IDX_PT_PARTGLOB_NAME IDX_PT_PG_NAME_P1 USABLE IDX_PT_PARTGLOB_NAME IDX_PT_PG_NAME_P2 USABLE IDX_PT_PARTGLOB_NAME IDX_PT_PG_NAME_P3 USABLE IDX_PT_PARTGLOB_NAME IDX_PT_PG_NAME_P4 USABLE
木有P5的分区全局索引
再看看分区local索引
SQL> select index_name,partition_name,status from user_ind_partitions where index_name='IDX_PT_LOCAL_OID'; INDEX_NAME PARTITION_NAME STATUS ------------------------------ ------------------------------ -------- IDX_PT_LOCAL_OID P2 USABLE IDX_PT_LOCAL_OID P3 USABLE IDX_PT_LOCAL_OID P1 USABLE IDX_PT_LOCAL_OID P4 UNUSABLE IDX_PT_LOCAL_OID P5 UNUSABLE
P5的索引自动创建,但是注意split操作会导致新拆分的两个分区局部索引失效,可以加上 update indexes 避免索引失效
SQL> create index idx_pt_local on part_ind_test(rid) local; Index created. SQL> create index idx_pt_partglob_id on part_ind_test(id) 2 global partition by range(id) 3 ( 4 partition idx_pt_pg_name_p1 values less than (10000), 5 partition idx_pt_pg_name_p2 values less than (20000), 6 partition idx_pt_pg_name_p3 values less than (maxvalue) 7 ); Index created. SQL> select index_name,partition_name,status from user_ind_partitions where index_name='IDX_PT_PARTGLOB_ID'; INDEX_NAME PARTITION_NAME STATUS ------------------------------ ------------------------------ -------- IDX_PT_PARTGLOB_ID IDX_PT_PG_NAME_P1 USABLE IDX_PT_PARTGLOB_ID IDX_PT_PG_NAME_P2 USABLE IDX_PT_PARTGLOB_ID IDX_PT_PG_NAME_P3 USABLE SQL> alter table part_ind_test split partition p3 at(30000) into (partition p3,partition p4) update indexes; Table altered.
分区后global local索引都没失效
SQL> select index_name,partition_name,status from user_ind_partitions where index_name='IDX_PT_PARTGLOB_ID'; INDEX_NAME PARTITION_NAME STATUS ------------------------------ ------------------------------ -------- IDX_PT_PARTGLOB_ID IDX_PT_PG_NAME_P1 USABLE IDX_PT_PARTGLOB_ID IDX_PT_PG_NAME_P2 USABLE IDX_PT_PARTGLOB_ID IDX_PT_PG_NAME_P3 USABLE SQL> select index_name,partition_name,status from user_ind_partitions where index_name='IDX_PT_LOCAL'; INDEX_NAME PARTITION_NAME STATUS ------------------------------ ------------------------------ -------- IDX_PT_LOCAL P3 USABLE IDX_PT_LOCAL P4 USABLE IDX_PT_LOCAL P1 USABLE IDX_PT_LOCAL P2 USABLE SQL>
UPDATE GLOBAL INDEXES只维护全局索引
UPDATE INDEXES同时维护全局和本地索引
1.2 global索引(全局索引)
global(全局)索引和表没有直接的关联,必须显式的指定maxvalue值
全局索引可以分区,也可以是不分区索引,全局索引必须是前缀索引,即全局索引的索引列必须是以索引分区键作为其前几列。
全局分区索引的索引条目可能指向若干个分区,因此,对于全局分区索引,即使只动,截断一个分区中的数据,都需要rebulid若干个分区甚至是整个索引。
oracle9i以后对分区表做move或者truncate的时可以用update global indexes语句来同步更新全局分区索引,用消耗一定资源来换取高度的可用性。
1.3 前缀索引、分前缀索引
前缀和非前缀索引都可以支持索引分区消除,前提是查询的条件中包含索引分区键
如果分区表中[索引列]是以[分区列]作为索引称作前缀索引,前缀索引可以用于分区消除
反之则为非前缀索引
我的理解是分区消除通俗点说就是最小分区扫描,在不明确数据在那个分区的情况下由于前缀索引就是分区列作为索引列,所以前缀索引可以精确定位到数据在那个分区只扫描单独分区,以达到优化
做个测试看看
SQL> create table part_ind_test 2 partition by RANGE (id) 3 ( 4 partition p1 values less than (10000) , 5 partition p2 values less than (20000) , 6 partition p3 values less than (maxvalue) 7 ) as select rownum id,rownum rid,object_id oid,object_name name,created ctime from all_objects; Table created. SQL> create index idx_pt_glob_id on part_ind_test(id); --global索引 Index created. SQL> create unique index idx_pt_un_rid on part_ind_test(rid); --唯一索引(我的理解这个也属于global索引,暂时不确定) Index created. SQL> create index idx_pt_local_oid on part_ind_test(oid) local; --local索引 Index created.
对比下面两个执行计划
SQL> set autotrace trace exp; SQL> set linesize 200 SQL> select * from part_ind_test where id=1; Execution Plan ---------------------------------------------------------- Plan hash value: 327010664 --------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | --------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 65 | 2 (0)| 00:00:01 | | | | 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| PART_IND_TEST | 1 | 65 | 2 (0)| 00:00:01 | 1 | 1 | |* 2 | INDEX RANGE SCAN | IDX_PT_GLOB_ID | 1 | | 1 (0)| 00:00:01 | | | ---------------------------------------------------------------------------------------------------------------------
执行计划中Pstart 表示分区开始 Pstop 表示分区停止
只扫描了1号分区
SQL> select * from part_ind_test where oid=1; Execution Plan ---------------------------------------------------------- Plan hash value: 2353038485 ----------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | ----------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 65 | 3 (0)| 00:00:01 | | | | 1 | PARTITION RANGE ALL | | 1 | 65 | 3 (0)| 00:00:01 | 1 | 3 | | 2 | TABLE ACCESS BY LOCAL INDEX ROWID| PART_IND_TEST | 1 | 65 | 3 (0)| 00:00:01 | 1 | 3 | |* 3 | INDEX RANGE SCAN | IDX_PT_LOCAL_OID | 1 | | 3 (0)| 00:00:01 | 1 | 3 | -----------------------------------------------------------------------------------------------------------------------
执行计划中Pstart 表示分区开始 Pstop 表示分区停止
而这个执行计划扫描了1-3号分区,也就是扫描了所有分区
原因就是IDX_PT_LOCAL_OID索引建立在了非分区列上
所以在分区表中,无论global索引还是local索引,如果想要达到分区消除效果必须是分区列作为索引列
复合索引也可
日常使用中,我以数值分区的表很少,多数使用基于月份分区的日志表
查询时一般很明确会使用到那个分区表,比如login_time date这个字段做基于月份的分区
select uid,login_time from user_logs partition(UL_201305) where uid=10034;
这个时候uid是否为分区列就无所谓了
但在使用分区不确定的时候前缀索引具有很好的性能
1.4 如何查看索引是global索引还是local索引
方法一、通过字典表查看
SQL> set linesize 200 SQL> select table_name,index_name,status from user_indexes where table_name='PART_IND_TEST'; ERROR: ORA-01756: quoted string not properly terminated SQL> select table_name,index_name,status from user_indexes where table_name='PART_IND_TEST'; TABLE_NAME INDEX_NAME STATUS ------------------------------ ------------------------------ -------- PART_IND_TEST IDX_PT_GLOB_ID VALID --次状态一定为global索引 PART_IND_TEST IDX_PT_UN_RID VALID --此状态一定为global索引 PART_IND_TEST IDX_PT_LOCAL_OID N/A --次状态为分区表,需到user_part_indexes的localiy属性确认 SQL> select index_name,locality from user_part_indexes where table_name='PART_IND_TEST'; INDEX_NAME LOCALI ------------------------------ ------ IDX_PT_LOCAL_OID LOCAL --这里确定此索引为local索引 SQL> select index_name,locality from user_part_indexes where table_name='PART_IND_TEST'; INDEX_NAME LOCALI ------------------------------ ------ IDX_PT_PARTGLOB_ID GLOBAL --分区全局索引 IDX_PT_LOCAL LOCAL --分区局部索引
local索引分布在每个分区上,与分区表对应
SQL> select index_name,partition_name,status from user_ind_partitions where partition_name like 'P%'; INDEX_NAME PARTITION_NAME STATUS ------------------------------ ------------------------------ -------- IDX_PT_LOCAL_OID P2 USABLE IDX_PT_LOCAL_OID P1 USABLE IDX_PT_LOCAL_OID P3 USABLE
方法二、通过dbms_meta.get_ddl查看索引建立语句
global索引无标记或者标记为global
SQL> select dbms_metadata.get_ddl('INDEX','IDX_PT_GLOB_ID') from dual; DBMS_METADATA.GET_DDL('INDEX','IDX_PT_GLOB_ID') -------------------------------------------------------------------------------- CREATE INDEX "TEST"."IDX_PT_GLOB_ID" ON "TEST"."PART_IND_TEST" ("ID") PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "TEST"
local索引一定会有local标记
SQL> select dbms_metadata.get_ddl('INDEX','IDX_PT_LOCAL_OID') from dual; DBMS_METADATA.GET_DDL('INDEX','IDX_PT_LOCAL_OID') -------------------------------------------------------------------------------- CREATE INDEX "TEST"."IDX_PT_LOCAL_OID" ON "TEST"."PART_IND_TEST" ("OID") PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE( BUFFER_POOL DEFAULT) LOCAL --看这里LOCAL (PARTITION "P1" PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "TEST" , PARTITION "P2" PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "TEST" , PARTITION "P3" PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "TEST" )
by cycsa