Oracle 分区

Posted 自学Oracle

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle 分区相关的知识,希望对你有一定的参考价值。

分区的目的:

  • 改善表的查询性能。

  • 表更容易管理。

  • 备份和恢复操作会执行得更好。

分区表的优点、缺点、特性:

(1)由于将数据分散到各个分区中,减少了数据损坏的可能性;

(2)可以对单独的分区进行备份和恢复;

(3)可以将分区映射到不同的物理磁盘上,来分散IO;

(4)提高可管理性、可用性和性能。

(5)缺点:已经存在的表没有方法可以直接转化为分区表。不过Oracle 提供了在线重定义表的功能。

(6)特殊性:含有LONG、LONGRAW 数据类型的表不能进行分区,一般分区类型为:varchar,varchar2,number,date

(7)每个表的分区或子分区数的总数不能超过1023 个。

什么时候用分区表(设计原则):

(1)单表过大,当表的大小超过2GB,或对于OLTP 系统,表的记录超过1000 万,都应考虑对表进行分区。

(2)历史数据据需要剥离的,表中包含历史数据,新的数据被增加到新的分区中。

(3)查询特征非常明显,比如是按整年、整月或者按某个范围!

(4)基于表的大部分查询应用,只访问表中少量的数据。

(5)按时间段删除成批的数据,例如按月删除历史数据。

(6)按时间周期进行表空间的备份时,将分区与表空间建立对应关系。

(7)如果一个表中大部分数据都是只读数据,通过对表进行分区,可将只读数据存储在只读表空间中,对于大数据库的备份是非常有益的。

(8)对于经常执行并行查询的表应考虑进行分区。

(9)当对表的部分数据可用性要求很高时,应考虑进行表分区。

分区表的类型

1、范围分区(Range Partitioning) 8+,

以某一个范围进行分区。eg:时间段划分。

范围分区特点(如何选择的关键):

(1)最早、最经典、应用最广的分区写法

(2)范围分区通过对分区字段值的范围进行分区

(3)范围分区特别适合于按时间周期进行数据的存储。日、周、月、年等

(4)数据管理能力强(数据迁移、数据备份、数据交换)

(5)范围分区的数据可能不均匀

(6)范围分区与记录值相关,实施难度和可维护性相对较差。

官方案例:

create table sales

(

    prod_id number(6),

    cust_id number,

    time_id date,

    channel_id char(1),

    promo_id number(6),

    quantity_sold number(3),

    amount_sold number(10,2)

)

partition by range(time_id)

(partition soles_al_2006 values less than (to_date('01-APR-2006','dd-mon-yyyy')) tablespace tsa,

partition soles_q2_2006 values less than (to_date('01-JUL-2006','dd-mon-yyyy')) tablespace tsb,

partition soles_q3_2006 values less than (to_date('01-OCT-2006','dd-mon-yyyy')) tablespace tsc,

partition soles_q4_2006 values less than (to_date('01-JAN-2006','dd-mon-yyyy')) tablespace tsd);

2、列表分区(List Partitioning) 9i+

该分区的特点是某列的值只有几个,基于这样的特点我们可以采用列表分区。

列表分区特点(如何选择的关键)

(1)list分区通过对分区字段的离散值进行分区。

(2)list分区是不排序的,而且分区之间没有关联关系

(3)ist分区适合于对数据离散值进行控制

(4)list分区只支持单个字段。

list分区具有与范围分区相似的优缺点:

数据管理能力强

list分区的数据可能不均匀

list分区与记录值相关,实施难度和可维护性相对较差。

官方案例1:

CREATE TABLE sq_soles_by_region

(

   deptno            NUMBER,

   deptname          VARCHAR2 (20),

   quarterly_soles   NUMBER (10, 2),

   state             VARCHAR2 (2)

)

PARTITION BY LIST

   (state)

   (

      PARTITION ql_northwest VALUES ('OR', 'MA'),

      PARTITION ql_southwest VALUES ('AZ', 'UT', 'NM'),

      PARTITION ql_northeast VALUES ('NY', 'VM', 'NJ'),

      PARTITION ql_southeast VALUES ('FL', 'GA'),

      PARTITION ql_northcentral VALUES ('SD', 'WI'),

      PARTITION ql_sourhventrol VALUES ('OK', 'TX'));


官方案例2:


CREATE TABLE sales_by_region

(

   item#        INTEGER,

   qty          INTEGER,

   store_name   VARCHAR (30),

   state_code   VARCHAR (2),

   sale_date    DATE

)

STORAGE (INITIAL 10 K NEXT 20 K)

TABLESPACE tbs5

PARTITION BY LIST (sale_code)

   (PARTITION region_east VALUES

                            ('NA',

                             'NY',

                             'CT',

                             'NH',

                             'ME',

                             'MD',

                             'VA',

                             'PA',

                             'NJ')

       STORAGE (INITIAL 8 K)

       TABLESPACE tbs8,

    PARTITION region_west VALUES

                            ('CA',

                             'AZ',

                             'NM',

                             'OR',

                             'MA')

       NOLOGGING,

    PARTITION REGION_SOUTH VALUES

                             ('TX',

                              'KY',

                              'LA',

                              'MS'),

    PARTITION REGION_NULL VALUES (NULL),

    PARTITION REGION_UNKNOWN VALUES (DEFAULT));

3、哈希分区(Hash Partitioning),8i+,

根据hash 值进行的散列分区,可以有效的消除io 的竞争。更多用在组合分区的子分区中。也称散列分区,这类分区是在列值上使用哈希算法,以确定将行放入哪个分区中,当列的值没有合适的条件时,建议使用哈希分区。

哈希分区也通过指定分区编号来均匀分布数据的一个分区类型,因为通过在I/O设备上进行哈希分区,使得这些分区大小一致。

hash分区的语法看起来比range复杂,其实使用起来比range更简单。

哈希分区的特点(如何选择的关键)

基于分区字段的hash值,自动将记录插入到指定分区

分区数一般是2的幂

易于实施

总体性能最佳

适合于静态数据

hash分区适合于数据的均匀存储

hash分区特别适合于pdml和partition-wise joins。

支持(hash)local indexes

9i不支持(hash)global indexes

10g支持(hash)global indexes hash分区

数据管理能力强

hash分区对数据值无法控制

对于那些无法有效划分范围的表,可以使用hash分区,这样对于提高性能还是会有一定的帮助。hash分区会将表中的数据平均分配到你指定的几个分区中,列所在的分区是依据分区列的hash值自动分配,因此你并不能控制也不知道哪条记录会被放到哪个分区中,hash分区可以支持多个依赖列。

官方案例:

CREATE TABLE scubagear

(

   id     nunber,

   name   VARCHAR2 (60)

)

PARTITION BY HASH (id)

   PARTITIONS 4

      STORE IN (gear1, gear2, gear3, gear4);

    -------------------- ---------------------

  CREATE TABLE dept

(

   deptno     NUMBER,

   deptname   VARCHAR (32)

)

PARTITION BY HASH (deptno)

   PARTITIONS 16;

-----------------------------------------

CREATE TABLE dept

(

   deptno     NUMBER,

   deptname   VARCHAR (32)

)

PARTITION BY HASH

   (deptno)

   (

      PARTITION p1 TABLESPACE ts1,

      PARTITION p2 TABLESPACE ts2,

      PARTITION p3 TABLESPACE ts1,

      PARTITION p4 TABLESPACE ts3);

 --------------------------------------------

4、复合分区,9i 开始,Oracle 就包括了2 种复合分区,RANGE-HASH 和RANGE-LIST。8i+

在11g , Oracle 一下就提供了4 种复合分区: RANGE-RANGE 、LIST-RANGE、LIST-HASH 和LIST-LIST;

复合分区作用

如果某表按照某列分区之后,仍然较大,或者是一些其它的需求,还可以通过分区内再建子分区的方式将分区在分区,即组合分区的方式。

既适合于历史数据,又适合于数据均匀分布

与范围分区一样提供高可用性和管理性

更好的pdml和partition-wise joins性能

实现粒度更细的操作

支持复合 local indexes

不支持复合 composite global indexes

复合分区分类:

RANGE-HASH 

RANGE-LIST 

RANGE-RANGE

--注意顺序,根分区只能是range分区,子分区可以是hash分区,list分区。

LIST-RANGE 

LIST-HASH 

LIST-LIST 

--注意顺序,根分区只能是list分区,子分区可以是hash分区,range分区。

组合分区比range分区更容易管理,充分使用了hash分区的并行优势。

5、间隔分区(Interval Extension to Range Partitioning (11g) ),

它就是以一个区间分区表为“起点”,并在定义中增加了一个规则(即间隔),使数据库知道将来如何增加分区。eg:比如每个月增加一个分区,从而省去了你不断的ADD 或者SPLIT 新的分区。数据库管理员日常要做的一件重复而无聊的工作 比如间隔一天/月要生成新的24个分区,用以存储第二天/月的数据,而在11g中 这项工作可以交给oracle自动完成了,基于range、list、hash的interval partitioning分区类型登场。

官方案例:

CREATE TABLE interval_sales

(

   prod_id         NUMBER (6),

   cust_id         NUMBER,

   time_id         DATE,

   channel_id      CHAR (1),

   promo_id        NUMBER (6),

   quantity_sold   NUMBER (3),

   amount_sold     NUMBER (10, 2)

)

PARTITION BY RANGE

   (time_id)

   INTERVAL ( NUMTOYMINTERVAL (1, 'month') )

   (

      PARTITION p0 VALUES LESS THAN (TO_DATE ('2008-1-1', 'yyyy-mm-dd')),

      PARTITION p1 VALUES LESS THAN (TO_DATE ('2009-1-1', 'yyyy-mm-dd')),

      PARTITION p2 VALUES LESS THAN (TO_DATE ('2010-1-1', 'yyyy-mm-dd')),

      PARTITION p3 VALUES LESS THAN (TO_DATE ('2011-1-1', 'yyyy-mm-dd')));

官方案例2:

CREATE TABLE sales

(

   prod_id         NUMBER (6),

   cust_id         NUMBER,

   time_id         DATE,

   channel_id      CHAR (1),

   promo_id        NUMBER (6),

   quantity_sold   NUMBER (3),

   amount_sold     NUMBER (10, 2)

)

PARTITION BY RANGE

   (time_id)

   INTERVAL ( NUMTOYMINTERVAL (1, 'month') )

   SUBPARTITION BY HASH (cust_id)

      SUBPARTITIONS 4

   (

      PARTITION

         before_2000 VALUES LESS THAN (TO_DATE ('2007-01-01', 'yyyy-mm-dd')))

PARALLEL;

6、参考分区(引用分区)(Referential Partitioning (11g))

对于主子表关系,如果对主表进行了分区,那么可以在子表上根据外键约束来建立对应主表的分区。引用分区可以根据这样一列来创建分区,虽然此列不再被分区的表中,但它是另一个表的外键引用。

这样主表和子表采用相同的等同分区方式,不但连接的时候可以利用partition-wise join ,而且对于主子表的分区操作也会十分方便。而且,这种方式并不需要在子表中存在主表的分区列。

尽量少使用这种,目前虽然oracle推出了,但是基本上用得案例没见着,我这里也不讲了,如果真要用到就查官方手册吧。

官方例子:

CREATE TABLE orders

(

   order_id       NUMBER (12),

   order_date     TIMESTAMP WITH LOCAL TIME ZONE,

   order_mode     VARCHAR (8),

   customer_id    NUMBER (6),

   order_status   NUMBER (2),

   order_total    NUMBER (8, 2),

   sales_rep_id   NUMBER (6),

   promotion_id   NUMBER (6),

   CONSTRAINT orders_pk PRIMARY KEY (order_id)

)

PARTITION BY RANGE

   (order_date)

   (

      PARTITION

         Q1_2017 VALUES LESS THAN (TO_DATE ('2017-01-01', 'yyyy-mm-dd')),

      PARTITION

         Q2_2017 VALUES LESS THAN (TO_DATE ('2017-03-01', 'yyyy-mm-dd')))

CREATE TABLE order_items

(

   order_id       NUMBER (12) NOT NULL,

   line_item_id   NUMBER (3) NOT NULL,

   product_id     NUMBER (6) NOT NULL,

   unit_price     NUMBER (8, 2),

   quantity       NUMBER (8),

   CONSTRAINT order_items_fk FOREIGN KEY

      (order_id)

       REFERENCES orders (order_id)

)

PARTITION BY REFERENCE (order_items_fk);

7、虚拟字段分区(Virtual Column Partitioning (11g)),

虚拟列的值从其他的列推导而来,Oracle 只保存源数据,这个列不占存储空间。虚拟列其中一个引申功能就是虚拟列分区功能。11g 增加对虚拟列的支持,这使得分区功能更加灵活。oracle 11g 新增了虚拟列功能,虚拟列的值从其他的列推导而来,oracle 只保存源数据,这个列并不占用存储空间。

虚拟列其中一个引申功能就是虚拟列分区功能。

在运行时计算,不存储在数据库中,不能更新虚拟列的值。

11g增加对虚拟列的支持,这使得分区功能更加灵活。

定义一个虚拟列的语法;

column_name[datatype] [fenerated always] as [exoression] [vritual]

1>只能在堆组织表(普通表)上创建虚拟列,不能在索引组织表、外部表、临时表上创建虚拟列

2>虚拟列不能是LOB或者RAW

3>虚拟列的值并不是真实存在的、只有用到时,才根据表达式计算出虚拟列的值,磁盘上并不存放。

4>可把虚拟列当做分区关键字建立分区表,这是oracle11g的另一新特性,--虚拟列分区

5>可在虚拟列上建立索引

6>如果在已经创建的表中增加虚拟列时,若没有指定虚拟列的字段类型,oracle 会根据generated always as 后面的表达式计算的结果自动设置该字段的类型

7>虚拟列的值由oracle 根据表达式自动计算得出,不可以做update和insert操作,可以对虚拟列做delete操作,

8>表达式中的所有列必须在同一张表

9>表达式不能使用其他虚拟列

generated always as (

case 

when sales_amt <=10000 then 'LOW'

when sales_amt >10000 and sales_amt<=100000 then 'MEDIUM'

when sales_amt >100000 and sales_amt<=1000000 then 'HIGH'

else 'ULTRA'

end

)


generated always as 

(quantity_sold * amount_sold)


generated always as 

(TO_NUMBER(TO_CHAR(jiondate,'MM')))

8、系统分区11g 

11g以前的分区表,需要指定一个或多个分区字段,并根据这个分区字段的值,按照一定的算法(RANGE,HASH和LIST)来决定一条

记录属于那个分区,从11g开始,oracle允许用户不指定分区列,完全根据程序来控制数据存储在那个分区中。这就是11g提供的系统分区功能

在以前,确定了分区列和分区方式,那么一条数据属于哪个分区也就被确定下来,而对于系统分区而言,分区是分区,数据是数据,二者没用对应的关系。

数据可以被放在任意一个分区中,这不是由数据本身决定的,而是应用程序在插入时确定的。

案例:

create table itpux_s1

(

id NUMBER(20) not null primary key,

name VARCHAR2(20) not null,

age NUMBER(10) not null,

sex VARCHAR2(10) not null,

cardid NUMBER(30) not null,

joindate DATE not null,

region VARCHAR2(12) not null,

tel VARCHAR2(12) not null,

email VARCHAR2(30) not null,

recommend VARCHAR2(10),

identifier VARCHAR2(100)

) tablespace itpux

partition by system

(partition P1,partition P2,partition P3,partition P4);

--插入数据

insert into itpux_s1 partition(P1) select * from itpux_m5 where id <20020000;

insert into itpux_s1 partition(P2) select * from itpux_m5 where id >20019999 and id < 20040000;

--查询验证(简单,详细的参考第3 节视频)

select * from DBA_tab_partitions where table_name='ITPUX_S1';

select * from Dba_Part_Tables where table_name='ITPUX_S1';

select * from dba_tab_subpartitions where table_name='ITPUX_S1';

select count(*) from ITPUX_S1;

select count(*) from ITPUX_S1 partition(P1);


查询验证

user_part_tables   记录分区的表的信息

user_tab_partitions 记录表的分区的信息

user_part_index      查询分区的索引信息

user_tab_subpartitions 子分区查询

dba_part_tables

dba_tab_partitions

dba_part_index

dba_tab_subpartitions

all_part_tables

all_tab_partitions

all_part_index

all_tab_subpartitions


分区索引的作用与分类

与分区表类似(partitioned table),分区索引(partitioned index)也能够提高系统的可管理性,可用性,可伸缩性,及系统性能。

对于索引,需要区分创建的是全局索引,或本地索引:

分区索引既可以与分区表相对独立(全局索引(global index)),

也可以采用与分区表相同的分区方式(本地索引(local index))。

普通表可以建分区索引,分区表可以建非分区的global index,也可以建range 或hash 分区的global index,也可以建基于分区的local index。

使用如下:

表      索引

-----------------

分区    不分区

分区    分区

不分区  分区

不分区  不分区

全局索引(global index):

即可以分区,也可以不分区。即可以建range 分区,也可以建hash 分区,即可建于分区表,又可创建于非分区表上,就是说,全局索引是完全独立的,因此它也需要我们更多的维护操作。

本地索引(local index):

其分区形式与表的分区完全相同,依赖列相同,存储属性也相同。对于本地索引,其索引分区的维护自动进行,就是说你add/drop/split/truncate 表的分区时,本地索引会自动维护其索引分区。

本地索引又可以分为有前缀(prefix)的索引和无前缀(nonprefix)的索引。

而全局索引目前只支持有前缀的索引。

如果本地索引的索引列以分区键开头(分区用什么列分的,索引就用什么列创建,11),则称为前缀局部索引。

如果本地索引的列不是以分区键开头,或者不包含分区键列(分区用什么列分的,索引不用什么列创建。12),则称为非前缀索引。全局索引的索引列必须是以索引分区键作为其前几列。

什么情况下建什么分区索引呢?

1)当有表的分区或子分区操作维护的时候,本地索引提供更好的可用性;

(如合并分区时,只有合并的表分区的分区索引失效,而非分区索引以及全局分区索引在合并.删除表分区后全部失效;可以单独重建本地索引;若只有一个分区需要维护,则只有一个本地索引受影响。

2)本地索引可以提高性能,因为数据大,很多分区必然会被并行的查询。

3)对于历史表,本地索引是必须,这样在有规律的drop 分区的时候,比较方便。

4)本地索引多应用于数据仓库环境中。

5)在非分区字段上建立unique 索引只能建全局索引。

6)全局索引不支持位图索引,全局分区索引全局分区索引只能是B 树索引。

7)全局索引多应用于oltp 系统中。

8)全局分区索引只按范围或者散列hash 分区,hash 分区是10g 以后才支持。

9)表用a 列作分区,索引用b 做局部分区索引,若where 条件中用b 来查询,那么oracle 会扫描所有的表和索引的分区,成本会比分区更高,此时可以考虑用b 做全局分区索引。

本地分区索引是对单个分区的,每个分区索引只指向一个表分区,全局索引则不然,一个分区索引能指向n 个表分区,同时,一个表分区,也可能指向n 个索引分区,对分区表中的某个分区做truncate 或者move,shrink 等,可能会影响到n 个全局索引分区,正因为这点,本地分区索引具有更高的可用性。

1、本地索引 

(1)有前缀的本地索引

create index i_itpux_r1_joindate on itpux_r1(joindate) local;

(2)无前缀的本地索引

create index i_itpux_r1_cardid on itpux_r1(cardid) local;

(3)有前缀的本地分区索引

--drop index i_itpux_r1_joindate

create index i_itpux_r1_joindate on itpux_r1(joindate) local 

(

partition p_i_2014 tablespace ts2014,

partition p_i_2015 tablespace ts2015,

partition p_i_2016 tablespace ts2016,

partition p_i_2017 tablespace ts2017

);

(4)查询 

select dbms_metadata.get_ddl('INDEX','i_itpux_r1_joindate','ITPUX') index_name from dual;

select * from dba_part_indexes where table_name='itpux_r1';

select * from dba_ind_partitions where table_name='itpux_r1';

select * from dba_indexes where table_name='itpux_r1';

select * from dba_ind_columns where table_name='ITPUX_R1';

--查看索引的分配

select segment_name, segment_type, sum(bytes) / 1024 / 1024 from dba_segments where segment_name in (select index_name from dba_indexes where table_name = 'ITPUX_R1') group by segment_name, segment_type;

--查询每个索引分区的分配

select segment_name, segment_type,partition_name, bytes / 1024 / 1024 from dba_segments where segment_name in (select index_name from dba_indexes where table_name = 'ITPUX_R1')

--查看执行计划

select * from itpux_r1 where joindate=to_date('2013-2-28 11:00:00','yyyy-mm-dd hh24:mi:ss');

select * from itpux_r1 where cardid = 10088541614;

2、全局分区索引的创建

前缀、范围、hash

需要手工维护

create index i_itpux_r1_joindate on itpux_r1(joindate) global 

partition by range(joindate)

(

   PARTITION

         sales_q1_2006

         VALUES LESS THAN (TO_DATE ('2014-01-01', 'yyyy-mm-dd')),

      PARTITION

         sales_q2_2006

         VALUES LESS THAN (TO_DATE ('2015-01-01', 'yyyy-mm-dd')),

      PARTITION

         sales_q3_2006

         VALUES LESS THAN (TO_DATE ('2016-01-01', 'yyyy-mm-dd')),

      PARTITION

         sales_q4_2006

         VALUES LESS THAN (maxvalue)

     );


create index i_itpux_r1_joindate on itpux_r1(joindate) global 

partition by hash(joindate)

(

  PARTITION  sales_q1_2006,

      PARTITION   sales_q2_2006,

      PARTITION   sales_q3_2006,

      PARTITION   sales_q4_2006

     );

alter table itpux_r1 drop partition sales_q1_2006;

3、分区索引的重建

--分区索引必须对每个分区重建,不能作为整体重建

alter index i_itpux_r1_joindate rebuild online nologging; --报错:ORA-14086

--删除重建,不在线默认写日志,在线不写日志

select * from dba_ind_partitions where index_owner='ITPUX' and status='UNUSABLE';

select * from dba_ind_partitions where INDEX_name='I_ITPUX_R1_JOINDATE';

--全局索引

--hash 索引分区

alter index i_itpux_r1_joindate rebuild partition P2014 online;

alter index i_itpux_r1_joindate rebuild partition P2015 online;

alter index i_itpux_r1_joindate rebuild partition P2016 online;

alter index i_itpux_r1_joindate rebuild partition P2017 online;

--范围索引分区

alter index i_itpux_r1_joindate rebuild partition P2014 online nologging;

--本地索引

alter index i_itpux_r1_joindate rebuild partition P2014 online nologging;


 

欢迎关注“自学Oracle”



以上是关于Oracle 分区的主要内容,如果未能解决你的问题,请参考以下文章

oracle 分区表怎么回收高水位

查看oracle的分区表都有哪些分区

oracle 11g分区表最多支持多少分区

如何更新oracle表中的分区字段

oracle 大表时间分区

oracle根据多字段创建分区表