DBA成长随笔---oracle11g,性能优化之表分区与索引分区

目录

 

表分区与索引分区

表分区的具体作用

什么时候使用分区表:

表分区的优缺点

 表分区的实现

列表--list partitioning

范围--range partitioning

哈希--hash partitioning

组合分区(子分区)

自动扩展分区

显示数据库相关的分区信息

分区索引

分区索引特点

分区修剪行为

本地索引和唯一约束

全局索引


表分区与索引分区

当表中的数据量不断增大,查询数据的速度就会变慢,应用程序的性能就会下降,这时就应该考虑对表进行分区。表进行分区后,逻辑上表仍然是一张完整的表,只是将表中的数据在物理上存放到多个表空间(物理文件上),这样查询数据时,不至于每次都扫描整张表。

表分区的具体作用

Oracle的表分区功能通过改善可管理性、性能和可用性,从而为各式应用程序带来了极大的好处。通常,分区可以使某些查询以及维护操作的性能大大提高。此外,分区还可以极大简化常见的管理任务,分区是构建千兆字节数据系统或超高可用性系统的关键工具。

分区功能能够将表、索引或索引组织表进一步细分为段,这些数据库对象的段叫做分区。每个分区有自己的名称,还可以选择自己的存储特性。从数据库管理员的角度来看,一个分区后的对象具有多个段,这些段既可进行集体管理,也可单独管理,这就使数据库管理员在管理分区后的对象时有相当大的灵活性。但是,从应用程序的角度来看,分区后的表与非分区表完全相同,使用 SQL DML 命令访问分区后的表时,无需任何修改。

什么时候使用分区表:

表的大小超过2GB。

表中包含历史数据,新的数据被增加都新的分区中。

表分区的优缺点

表分区有以下优点:

改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索速度。

增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用;

维护方便:如果表的某个分区出现故障,需要修复数据,只修复该分区即可;

均衡I/O:可以把不同的分区映射到磁盘以平衡I/O,改善整个系统性能。

缺点:

已经存在的表没有方法可以直接转化为分区表。

分区修剪行为:如果查询首先访问索引,他是否能够修建分区完全取决于查询中的谓词。

分区修剪是数据仓库的一个重要特性。当访问分区表的时候,优化器分析FROM和WHERE子句来消除不必要的分区。

select count(*) from t9 where object_id=10;

 select count(*) from t8 where object_id=10;

 表分区的实现

列表--list partitioning

create table emp_list(

empno   number(4)

,ename   varchar2(10)

,job      varchar2(9)

,mgr     number(4)

,deptno   number(2)

 )partition by list(deptno)

(partition p_10 values (10)

,partition p_20 values (20)

,partition p_30 values (30)

,partition p_40 values (40));  

范围--range partitioning

create table emp_range

( empno       number(4)        

 ,ename       varchar2(10)

 ,job         varchar2(9)

 ,mgr         number(4)

 ,create_date date

) partition by range(create_date)

 (partition p_20220101  values less than (to_date('2017-01-02','YYYY-MM-DD'))  

 ,partition p_20220102  values less than (to_date('2017-01-03','YYYY-MM-DD'))

 ,partition p_20220103  values less than (to_date('2017-01-04','YYYY-MM-DD'))

 ); --也可以  结合maxvalue

-- 插入数据

insert into emp_range values(1003,'xiaoming','salas',100,to_date('20160101','YYYYMMDD'));

insert into emp_range values(1004,'xiaoming','salas',100,to_date('20170101','YYYYMMDD'));

insert into emp_range values(1005,'xiaoming','salas',100,to_date('20170102','YYYYMMDD'));

insert into emp_range values(1007,'xiaoming','salas',100,to_date('20170103','YYYYMMDD'));

-- 根据分区名查询该分区下数据

select * from emp_range partition(p_20220103);

-- 根据值所在的分区,查询该分区的所有数据

select * from emp_range partition for(to_date('20170103','YYYYMMDD'));

--  清空对应的分区,但其他分区数据不做改变

alter table emp_range truncate partition (p_20220103);

-- 如果被修改的字段是分区判定的字段,那么可能需要开启行偏移

alter table emp_range enable row movement;  

update emp_range set  create_date= to_date('20170102','YYYYMMDD') where empno=1007;

 -- 添加一个分区

alter table emp_range add partition p_20220103 values less than(to_date('2017-01-04','YYYY-MM-DD'));

 -- 删除一个分区

alter table emp_range drop partition p_20220103;

哈希--hash partitioning

/*对于无法有效划分范围的表*/

create table emp_hash(

empno        number(4)        

,ename       varchar2(10)

,job         varchar2(9)

,mgr         number(4)

,hirdate  date

,sal  number(7,2)

,comm  number(7,2)

,deptno  number(2)

,create_date date

)partition by hash(empno)(

partition part01,

partition part02,

partition part03,

partition part04);

组合分区(子分区)

create table emp_range_list(

 empno      number(4)        

,ename       varchar2(10)

,job          varchar2(9)

,mgr         number(4)

,hirdate     date

,sal     number(7,2)

,comm     number(7,2)

,deptno     number(2)

,create_date   date

) partition by range(hirdate) subpartition by list(deptno)

(

partition p1 values less than(to_date('1921-01-01','YYYY-MM-DD'))

(subpartition p1A values (10),

 subpartition p1B values (20),

 subpartition p1C values (30),

 subpartition p1D values (40)

),

partition p2 values less than(to_date('1922-01-01','YYYY-MM-DD'))

(subpartition p1A values (10),

 subpartition p1B values (20),

 subpartition p1C values (30),

 subpartition p1D values (40)

)    

 )

自动扩展分区

-- 按天自动添加分区

create table emp_interval_day

( id number

,time_col date

)partition by range(time_col) interval (numtodsinterval(1,'day'))(

partition p_day_1 values less than (to_date('2018-01-01','yyyy-mm-dd'))

) ;

-- 按月自动添加分区

create table emp_interval_month

( id number

,time_col date

)partition by range(time_col) interval (numtoyminterval(1,'month'))(

partition p_day_1 values less than (to_date('2018-01-01','yyyy-mm-dd'))

) ;

-- 按年分区

create table emp_interval_year

( id number

,time_col date

)partition by range(time_col) interval (numtoyminterval(1,'year'))(

partition p_day_1 values less than (to_date('2018-01-01','yyyy-mm-dd'))

) ;  

显示数据库相关的分区信息

--显示数据库所有分区表的信息:
select * from DBA_PART_TABLES
--显示当前用户可访问的所有分区表信息:
select * from ALL_PART_TABLES
--显示当前用户所有分区表的信息:
select * from USER_PART_TABLES
--显示表分区信息 显示数据库所有分区表的详细分区信息:
select * from DBA_TAB_PARTITIONS
--显示当前用户可访问的所有分区表的详细分区信息:
select * from ALL_TAB_PARTITIONS
--显示当前用户所有分区表的详细分区信息:
select * from USER_TAB_PARTITIONS
--显示子分区信息 显示数据库所有组合分区表的子分区信息:
select * from DBA_TAB_SUBPARTITIONS
--显示当前用户可访问的所有组合分区表的子分区信息:
select * from ALL_TAB_SUBPARTITIONS
--显示当前用户所有组合分区表的子分区信息:
select * from USER_TAB_SUBPARTITIONS
--显示分区列 显示数据库所有分区表的分区列信息:
select * from DBA_PART_KEY_COLUMNS
--显示当前用户可访问的所有分区表的分区列信息:
select * from ALL_PART_KEY_COLUMNS
--显示当前用户所有分区表的分区列信息:
select * from USER_PART_KEY_COLUMNS
--显示子分区列 显示数据库所有分区表的子分区列信息:
select * from DBA_SUBPART_KEY_COLUMNS
--显示当前用户可访问的所有分区表的子分区列信息:
select * from ALL_SUBPART_KEY_COLUMNS
--显示当前用户所有分区表的子分区列信息:
select * from USER_SUBPART_KEY_COLUMNS
--怎样查询出oracle数据库中所有的的分区表
select * from user_tables a where a.partitioned='YES'

分区索引

对索引分区有以下两种方法:

按表分区的方式对索引分区:这也称为本地索引(local index)。每个表分区都有一个索引分区,而且这个索引分区只会对这个表分区中的数据进行索引。同一索引分区中的所有条目都指向一个表分区,同一表分区中的所有行都会放在一个索引分区中。

按区间或散列对索引分区:这也称为全局分区索引(globally partitioned index)。索引可以按区间分区,一个索引分区可以指向任何表分区。

本地索引分为两类:

本地前缀索引(local prefixed index):在这类索引中,分区键在索引定义的前几列上。

本地非前缀索引(local nonprefixed index):这些索引不以表的分区键作为它的前几列。索引可能包含表的分区键列,也可能不包含。

分区索引特点

表的DML操作无需rebuild索引

可以很方便的管理数据

        

 全局索引特点

表的dml操作导致索引无效

             

分区修剪行为

如果查询首先访问索引,他是否能够修建分区完全取决于查询中的谓词。

--示例

    /*创建测试环境,验证分区修建行为*/ 

---创建测试表空间

create tablespace test_partitioned_tablespace_p1  

logging  

datafile '/data/u01/app/oracle/oradata/orcl/test_partitioned_tablespace_p1'

size 40M  

autoextend on  

next 100M

maxsize unlimited

extent management local

segment space management auto;

create tablespace test_partitioned_tablespace_p2  

logging  

datafile '/data/u01/app/oracle/oradata/orcl/test_partitioned_tablespace_p2'

size 40M  

autoextend on  

next 100M

maxsize unlimited

extent management local

segment space management auto;

--创建测试表环境

CREATE TABLE partitioned_table

( a int,

b int,

data char(20)

) PARTITION BY RANGE (a)

(

PARTITION part_1 VALUES LESS THAN(2) tablespace test_partitioned_tablespace_p1,

PARTITION part_2 VALUES LESS THAN(3) tablespace test_partitioned_tablespace_p2);

--创建本地前缀索引

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 dual connect by level <= 70000;

-- 生成直方图

begin  dbms_stats.gather_table_stats( user,'PARTITIONED_TABLE',cascade=>TRUE ); end;

--将p2放于离线状态,模拟介质故障

alter tablespace test_partitioned_tablespace_p2 offline;

select * from partitioned_table where a = 1 and b = 1;

-- 通过返回结果我们知道,p2是离线状态的表空间;但数据还是能正常访问;

 select * from partitioned_table where b = 1;

-- 然而,使用本地非前置索引却失败;PSTOP为2,说明在使用本地非前缀索引的时候必须考虑part_2,但是表空间已经离线所以失败。

--删除前置索引

drop index local_prefixed;

select * from partitioned_table where a = 1 and b = 1;

-- 发现使用了非前缀索引,而且也能进行分区修剪,说明本地非前缀索引也可以进行分区修剪。

本地索引和唯一约束

如果在分区表上想使本地索引来支撑UNIQUE约束或者PRIMARY KEY约束,那么必须将分区键加到这个约束中;这是本地索引的最大限制,Oracle只能保证索引分区内部不能保证跨分区的唯一性。

如果你使用一个TIMESTAMP字段作为分区键,并且在使用一个本地分区索引的ID上有一个主键,那么Oracle会转而利用全局索引来保证唯一性。

create table partitioned

      ( timestamp date,

       id         int,

      constraint partitioned_pk primary key(id)  --id有主键

      )

      PARTITION BY RANGE (timestamp)  --分区键为timestamp

     (

      PARTITION part_1 VALUES LESS THAN( to_date('01/01/2014','dd/mm/yyyy') ) ,

      PARTITION part_2 VALUES LESS THAN( to_date('01/01/2015','dd/mm/yyyy') )

     )

全局索引

与本地索引不同,全局索引只有一类,那就是前缀全局索引(prefixed global index)。

全局索引必须使用以他的起始列作为分区键。

索引中不能包含表的分区列。

-- 创建全局索引分区测试表  

create table global_partitioned_table

     ( timestamp date,

     id    int

     )  partition by range (timestamp)  --timestamp作为分区键

     (

     partition part_1 values less than ( to_date('01/01/2014','dd/mm/yyyy') ) ,

     partition part_2 values less than ( to_date('01/01/2015','dd/mm/yyyy') )

     );

--创建全局索引

  create index global_partitioned_index

    on global_partitioned_table(id)

    GLOBAL  partition  by range(id)

    (

    partition part_1 values less than(1000),

    partition part_2 values less than (MAXVALUE)

);

-- 添加主键约束

 alter table global_partitioned_table add constraint global_partitioned_pk  primary key(id);

-- 约束依赖于该全局索引,如果要删除该全局索引必须先删除约束。 

误区--分区索引性能高于全局索引

 --搭建测试环境  

create table part_1(id int,name varchar2(1000))

   partition by range(id)

   (

   partition p1 values less than(1000),

   partition p2 values less than(2000),

   partition p3 values less than(3000),

   partition p4 values less than(4000),

   partition p5 values less than(5000),

   partition p6 values less than(6000),

   partition p7 values less than(7000),

   partition p8 values less than(8000),

   partition p9 values less than(9000),

   partition p10 values less than(10000),

   partition p11 values less than(11000),

   partition p12 values less than(12000),

   partition p13 values less than(13000),

   partition p14 values less than(14000),

   partition p15 values less than(15000),

   partition p16 values less than(16000),

   partition pmax values less than(maxvalue)

   );

   insert into part_1

   select t.OBJECT_ID as id,t.OWNER||

                      t.OBJECT_NAME||

                      t.SUBOBJECT_NAME||

                      t.OBJECT_ID||

                      t.DATA_OBJECT_ID||

                      t.OBJECT_TYPE||

                      t.CREATED||

                      t.LAST_DDL_TIME||

                      t.TIMESTAMP||

                      t.status||

                      t.TEMPORARY||

                      t.GENERATED||

                      t.SECONDARY||

                      t.namespace||

                      t.EDITION_NAME as name from dba_objects t;

   

   create table part_2(id int,name varchar2(1000))

   partition by range(id)

   (

   partition p1 values less than(1000),

   partition p2 values less than(2000),

   partition p3 values less than(3000),

   partition p4 values less than(4000),

   partition p5 values less than(5000),

   partition p6 values less than(6000),

   partition p7 values less than(7000),

   partition p8 values less than(8000),

   partition p9 values less than(9000),

   partition p10 values less than(10000),

   partition p11 values less than(11000),

   partition p12 values less than(12000),

   partition p13 values less than(13000),

   partition p14 values less than(14000),

   partition p15 values less than(15000),

   partition p16 values less than(16000),

   partition pmax values less than(maxvalue)

   );

   insert into part_2 select * from part_1;

   create index part_1_idx on part_1 (id) local;  -- part_1 建立本地索引

   create index part_2_idx   -- part_2建立全局索引

       on part_2(id)

       GLOBAL  partition  by range(id)

       (

       partition part_1 values less than(30000),

       partition part_2 values less than (MAXVALUE)

   );

   begin  dbms_stats.gather_table_stats( user,'PART_1',cascade=>TRUE ); end;

   begin  dbms_stats.gather_table_stats( user,'PART_2',cascade=>TRUE ); end;

--查询结果

 select /*+ part_1 part_1_idx */ id from part_1 where id >10000 and id <30000;

select /*+ part_2 part_2_idx */ id from part_2 where id >10000 and id <30000;

--结论 分区索引性能不一定比全局索引高

总结

1.分区索引的目的在于数据的管理而非性能。

2.一个分区表上如果经常有DDL操作,将会导致全局索引无效,需要对索引重建,此时

创建分区索引更加适合。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello world857

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值