全局索引(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
