ORACLE分區索引學習


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





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM