zoukankan      html  css  js  c++  java
  • 表分区索引介绍

    分区表索引介绍

    局部索引:

    局部前缀索引(local prefixed index):在这些索引中,分区键在索引定义的前几列上。例如,一个表在名为LOAD_DATE 的列上进行区间分区,
                                         该表上的局部前缀索引就是采用LOAD_DATE作为其索引列列表中的第一列。
    局部非前缀索引(local nonprefixed index):这些索引不以分区键作为其列列表的前几列。索引可能包含分区键列,也可能不包含。
        这两类索引都可以进行分区消除,前提是查询的条件中包含索引分区键,它们都支持惟一性(只要局部非前缀索引包含分区键列)。
        局部索引与表的分区数一致,如果新增一个分区,新增加的分区局部索引也会自动创建。全局索引则不行(即需要重建全局索引)。
        
    一、分区消除介绍:
    --下面通过实验来说明索引的分区消除
    --创建一个分区表
    CREATE TABLE partitioned_table ( a int, b int, data char(20))
     PARTITION BY RANGE (a)
     ( PARTITION part_1 VALUES LESS THAN(2) tablespace gcomm,      --以a字段进行分区,小于等于2的存在分区1,小于等于3的存在分区2
       PARTITION part_2 VALUES LESS THAN(3) tablespace gmapdata )
    --创建一个本地前缀索引   
    create index local_prefixed on partitioned_table (a,b) local;
    --创建一个本地非前缀索引
    create index local_nonprefixed on partitioned_table (b) local;
    --向表中插入数据
    insert into partitioned_table select mod(rownum-1,2)+1, rownum, 'x' from all_objects;
    --分析表
    begin dbms_stats.gather_table_stats ( user,'PARTITIONED_TABLE',cascade=>TRUE );end;

    --以sys用户登录后 将gmapdata表空间置为离线
    alter tablespace gmapdata offline;--分区2的数据包括其索引等都被置为离线状态

    select * from partitioned_table where a = 1 and b = 1;
    A        B     DATA
    ----  ------ --------------------
    1       1        x
    --将之前的plan_table表的数据清除
    delete from plan_table;
    --生成统计信息
    explain plan for select * from partitioned_table where a = 1 and b = 1;
    --查看统计信息结果
    select * from table(dbms_xplan.display);
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 1622054381
    --------------------------------------------------------------------------------
    | Id  | Operation                                                  | Name                       | Rows  | Bytes | Pstart| Pstop
    --------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                                  |                                 |     1   |    28 |    
    |   1 |  PARTITION RANGE SINGLE                        |                                 |     1   |    28 |    1      1
    |   2 |   TABLE ACCESS BY LOCAL INDEX ROWID  | PARTITIONED_TABLE  |     1   |    28 |    1      1
    |*  3 |    INDEX RANGE SCAN                              | LOCAL_PREFIXED       |     1   |         |    1      1
    --------------------------------------------------------------------------------
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       3 - access("A"=1 AND "B"=1)
    注:可以进行查询,可以通过本地前缀索引将分区2消除 由于分区2采用的表空间为gmapdata,而这个表空间在上述已将其离线,通过本地前缀索引在查询的时候将分区2消除,只在第一个分区进行查询,因此该查询能够成功查询。

    再看下面的一个查询:  
    select * from partitioned_table where b = 1;
    提示:ora-00376:此时无法读取文件11
          ora-01110:数据文件11:‘D:\ORACE|PRODUCT\10.2.0\ORADATA\FGISDBGMAPDATA.DBF’
    delete from plan_table;
    explain plan for select * from partitioned_table where b = 1;
    select * from table(dbms_xplan.display);
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 440752652
    --------------------------------------------------------------------------------
    | Id  | Operation                                                      | Name                        | Rows  | Bytes |  Pstart| Pstop
    --------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                                   |                                     |     1 |    28 |   
    |   1 |  PARTITION RANGE ALL                              |                                      |     1 |    28 |    1       2
    |   2 |   TABLE ACCESS BY LOCAL INDEX ROWID   | PARTITIONED_TABLE      |     1 |    28 |    1       2
    |*  3 |    INDEX RANGE SCAN                               | LOCAL_NONPREFIXED     |     1 |        |    1       2
    --------------------------------------------------------------------------------
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       3 - access("B"=1)
    注:当查询谓词只有b,即采用非前缀索引,而且查询的条件中又不含分区键a,因此在查询时无法将分区2消除,导致在查询分区2时提示数据文件不在。

    将本地前缀索引删掉后:   
    drop index local_prefixed;
    select * from partitioned_table where a = 1 and b = 1;
    A        B     DATA
    ----  ------ --------------------
    1       1        x
    delete from plan_table;
    explain plan for select * from partitioned_table where a = 1 and b = 1;
    select * from table(dbms_xplan.display);
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 904532382
    --------------------------------------------------------------------------------
    | Id  | Operation                          | Name              | Rows  | Bytes | Pstart| Pstop
    --------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                   |                   |     1 |    28 |
    |   1 |  PARTITION RANGE SINGLE            |                   |     1 |    28 |   1       1
    |*  2 |   TABLE ACCESS BY LOCAL INDEX ROWID| PARTITIONED_TABLE |     1 |    28 |   1       1
    |*  3 |    INDEX RANGE SCAN                | LOCAL_NONPREFIXED |     1 |       |   1       1
    --------------------------------------------------------------------------------
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       2 - filter("A"=1)
       3 - access("B"=1)
      
    注:本地前缀索引删除后,采用本地非前缀索引进行如上查询也可以成功。可见本地非前缀索引也可以进行消除分区,主要取决于谓词。该表利用a字段
    进行分区,因此主要谓词中有a字段的查询,就可以成功查询。



    二、局部索引和惟一约束介绍:

    CREATE TABLE partitioned
     ( load_date date, id int, constraint partitioned_pk primary key(id) )
     PARTITION BY RANGE (load_date)
     ( PARTITION part_1 VALUES LESS THAN( to_date('01/01/2000','dd/mm/yyyy') ) ,
       PARTITION part_2 VALUES LESS THAN( to_date('01/01/2001','dd/mm/yyyy') ))

    select segment_name, partition_name, segment_type from user_segments where segment_name like 'PARTITIONED%';

    SEGMENT_NAME              PARTITION_NAME       SEGMENT_TYPE
    --------------------  ---------------------  ------------------
    PARTITIONED               PART_1                TABLE PARTITION
    PARTITIONED               PART_2                TABLE PARTITION
    PARTITIONED_PK                                  INDEX
    注:PARTITIONED_PK 索引没有进行分区,因此可以保证唯一性

    删掉表重新创建并建立一个本地索引后在创建一个唯一索引

    drop table partitioned
    CREATE TABLE partitioned ( timestamp date, id int)
     PARTITION BY RANGE (timestamp)
     (PARTITION part_1 VALUES LESS THAN( to_date('01-1-2000','dd-mm-yyyy') ) ,
      PARTITION part_2 VALUES LESS THAN( to_date('01-1-2001','dd-mm-yyyy') )
     )

    create index partitioned_idx on partitioned(id) local;--创建一个本地索引

    select segment_name, partition_name, segment_type from user_segments where segment_name like 'PARTITIONED%';

    SEGMENT_NAME                PARTITION_NAME                 SEGMENT_TYPE
    ------------------------ ------------------------------ ------------------
    PARTITIONED                 PART_1                         TABLE PARTITION
    PARTITIONED                 PART_2                         TABLE PARTITION
    PARTITIONED_IDX             PART_1                         INDEX PARTITION
    PARTITIONED_IDX             PART_2                         INDEX PARTITION

    alter table partitioned add constraint partitioned_pk primary key(id);--在id上增加一个全局索引
    提示:此列列表已有索引
    注:分区索引无法 保证唯一性,因为如果要保证分区索引的唯一性,即分区1有id=1,那么分区2中就不能有id=1,而我们如果做了这个限制,往不同
    分区进行插数据就会降低分区表的灵活性。



    全局索引

        全局索引使用一种有别于底层表的机制进行分区。表可以按一个TIMESTAMP 列划分为10 个分区,而这个表上的一个全局索引可以按REGION 列划分
    为5 个分区。与局部索引不同,全局索引只有一类,这就是前缀全局索引(prefixed global index)。如果全局索引的索引键未从该索引的分区键开始
    ,这是不允许的。这说明,不论用什么属性对索引分区,这些属性都必须是索引键的前几列。

    drop table partitioned
    CREATE TABLE partitioned ( timestamp date, id int )
     PARTITION BY RANGE (timestamp)
     ( PARTITION part_1 VALUES LESS THAN ( to_date('01-1-2000','dd-mm-yyyy') ) ,
       PARTITION part_2 VALUES LESS THAN ( to_date('01-1-2001','dd-mm-yyyy') )
     )

    create index partitioned_index on partitioned(id) GLOBAL
     partition by range(id)
     (partition part_1 values less than(1000),
      partition part_2 values less than (MAXVALUE) --全局索引必须指定最大值,否则会提示:ORA-14021:必须指定所有列的MAXVALUE
     )
    注:全局索引有一个要求,即最高分区(最后一个分区)必须有一个值为MAXVALUE 的分区上界。这可以确保底层表中的所有行都能放在这个索引中。

    全局索引可以创建一个唯一索引:

    alter table partitioned add constraint partitioned_pk primary key(id);--创建唯一索引成功
    注:该唯一索引是通过创建的全局索引来保证唯一,可以通过删除其索引的错误来说明
    drop index partitioned_index;提示:ora-02429:无法删除用于强制唯一/主键的索引。

    --以下例子说明了全局索引必须是前缀的
    create index partitioned_index2 on partitioned(timestamp,id) GLOBAL
     partition by range(id)--以id为分区键 那么其索引就必须将id置到最前面
     (partition part_1 values less than(1000),
      partition part_2 values less than (MAXVALUE)
     )
    提示:ORA-14038:全局分区索引必须是前缀

    数据仓库与全局索引:
    数据仓库一般是通过数据的滑入划出进行管理(即旧数据划出,新数据滑入)。一个分区表中,如果进行分区的增删改操作会造成全局索引失效。因此,
    采用何种索引要根据系统的要求。

    实验1:分区的滑入滑出导致全局索引失效,局部索引仍有效
    --创建分区表  
    CREATE TABLE partitioned ( timestamp date, id int )
     PARTITION BY RANGE (timestamp)
     ( PARTITION fy_2004 VALUES LESS THAN ( to_date('01-1-2005','dd-mm-yyyy') ) ,
      PARTITION fy_2005 VALUES LESS THAN ( to_date('01-1-2006','dd-mm-yyyy') )
     )
    --对两个分区都插入数据
    insert into partitioned partition(fy_2004)
      select to_date('31-12-2004', 'dd-mm-yyyy') - mod(rownum,360),
             object_id
        from all_objects;
             
    insert into partitioned partition(fy_2005)
      select to_date('31-12-2005', 'dd-mm-yyyy') - mod(rownum,360),
             object_id
        from all_objects;
    --分别创建一个本地索引和全局索引
    create index partitioned_idx_local on partitioned(id) LOCAL;
    create index partitioned_idx_global on partitioned(timestamp) GLOBAL;
    --创建一个新表(用于装载分区划出的数据)
    create table fy_2004 (timestamp date, id int); create table fy_2005 (timestamp date, id int);
    create index fy_2004_idx on fy_2004(id);create index fy_2005_idx on fy_2005(id);
    --创建一个新表并插入数据
    create table fy_2006 ( timestamp date, id int );
    insert into fy_2006
      select to_date('31-12-2006', 'dd-mm-yyyy') - mod(rownum,360),
             object_id
        from all_objects;
    create index fy_2006_idx on fy_2006(id) nologging;

    create table fy_2007 ( timestamp date, id int );
    insert into fy_2007
      select to_date('31-12-2007', 'dd-mm-yyyy') - mod(rownum,360),
             object_id
        from all_objects;
    create index fy_2007_idx on fy_2007(id) nologging;

    --将分区fy_2004的数据放到表fy_2004中,并删除该分区
    alter table partitioned exchange partition fy_2004 with table fy_2004 including indexes without validation;
    alter table partitioned drop partition fy_2004;
    --创建一个新分区,用于装载新数据
    alter table partitioned add partition fy_2006 values less than ( to_date('01-12-2007','dd-mm-yyyy') );
    alter table partitioned exchange partition fy_2006 with table fy_2006 including indexes without validation;

    --最后查看索引的情况
    select index_name, status from user_indexes where table_name='PARTITIONED';
    1    PARTITIONED_IDX_LOCAL    N/A
    2    PARTITIONED_IDX_GLOBAL    UNUSABLE
    --发现全局索引已失效

    如果强制用其全局索引,会导致无法查询
    set autotrace on explain
    select /*+ index( partitioned PARTITIONED_IDX_GLOBAL ) */
     count(*)
      from partitioned
     where timestamp between sysdate - 50 and sysdate;
    ORA-01502: 索引 'LTTFM.PARTITIONED_IDX_GLOBAL' 或这类索引的分区处于不可用状态

    --直接进行查询,则会进行全表扫描
    select count(*) from partitioned where timestamp between sysdate-50 and sysdate;

    实验2:全局索引失效的解决办法:
    1)可对索引进行重建,
    2)直接在进行分区删改的时候 加上更新索引的字句(UPDATE GLOBAL INDEXES):
    --删除、交换分区时可加上索引更新的字句,增加一个分区不用进行更新索引,因为新增加的分区空行
    alter table partitioned exchange partition fy_2004 with table fy_2004 including indexes without validation UPDATE GLOBAL INDEXES
    alter table partitioned drop partition fy_2004 UPDATE GLOBAL INDEXES

    注:如果在对分区进行操作时加上了 UPDATE GLOBAL INDEXES 更新索引的字句,那么全局索引就不会失效。

    实验3:比较索引重建和更新索引所占用的资源情况:
    begin runStats_pkg.rs_start;end;  
    alter table partitioned exchange partition fy_2004 with table fy_2004 including indexes without validation;
    alter table partitioned drop partition fy_2004;
    alter table partitioned add partition fy_2006 values less than (to_date('01-1-2007','dd-mm-yyyy') );
    alter table partitioned exchange partition fy_2006 with table fy_2006 including indexes without validation;
    alter index partitioned_idx_global rebuild; --采用索引重建的方法
    begin  runStats_pkg.rs_middle;end;

    alter table partitioned exchange partition fy_2005 with table fy_2005 including indexes without validation update global indexes;
    alter table partitioned drop partition fy_2005 update global indexes;
    alter table partitioned add partition fy_2007 values less than ( to_date('01-1-2008','dd-mm-yyyy') );
    alter table partitioned exchange partition fy_2007 with table fy_2007 including indexes without validation update global indexes;
    begin runStats_pkg.rs_stop;end;  --采用索引更新的方法

    输出的结果:
    Run1 ran in 936 hsecs
    Run2 ran in 1101 hsecs
    run 1 ran in 85.01% of the time

    实验结果:其结果表明对全局索引进行更新要花更长时间。但是如果说系统不允许中断的话,那么还是应该采取索引更新的方法。

    分区索引字典

    DBA_PART_INDEXES 分区索引的概要统计信息,可以得知每个表上有哪些分区索引,分区索引的类新(local/global,)
    Dba_ind_partitions每个分区索引的分区级统计信息
    Dba_indexesminusdba_part_indexes,可以得到每个表上有哪些非分区索引

  • 相关阅读:
    Mysql 单表查询-排序-分页-group by初识
    Mysql 单表查询where初识
    Mysql 库表操作初识
    Mysql 常见数据类型及约束
    数据库 初识
    Mysql 游标初识
    Mysql 控制结构初识
    Mysql 存储过程初识
    单链表-Python实现-jupyter->markdown 格式测试
    MySQL 触发器学习-markdown->html 格式测试
  • 原文地址:https://www.cnblogs.com/lanzi/p/1993619.html
Copyright © 2011-2022 走看看