oracle分区技术

oracle分区技术

一、分区技术概述
1、表或索引的所有分区必须具备相同的逻辑结构。
2、利用分区技术,用户可以在分区级进行数据加载、索引创建及重建,或备份恢复等数据管理操作。
3、分区技术能够提高查询性能。
4、分区技术能显著缩短维护操作导致的停机时间。
5、对各分区的维护操作可以相互独立地进行。

二、分区键和分区表
1、分区键的特点:
由1--16个数据列顺序构成;
不能包含LEVEL、ROWID、MLSLABEL(虚列),也不能包含类型为ROWID的列;
不能包含可为空(NULL)的列。


一张表最多 可由1024K-1个分区构成。
任何表都能被分区,除非其中含有数据类型为LONG或者LONG RAW的列。
CLOB或者BLOB的类型列的表可以被分区。

2、范围分区
通过数据范围进行分区。
--创建范围分区表并使用 (MAXVALUE) 键值
create table scott.range_test(range_key date not null,EMID number,uname varchar2(20))
partition by range(range_key)
(partition part1 values less than (to_date('2015-01-01','yyyy-mm-dd')),
partition part2 values less than (to_date('2016-01-01','yyyy-mm-dd')),
partition part3 values less than (to_date('2017-01-01','yyyy-mm-dd')),
partition part4 values less than (to_date('2018-01-01','yyyy-mm-dd')),
partition part5 values less than (to_date('2019-01-01','yyyy-mm-dd')),
partition part_MAX values less than (MAXVALUE));

--查询一个分区表的数据
select * from scott.range_test partition(part2)
--查询用户分区表信息
select table_name,partitioning_type,partition_count,status
from user_part_tables
--查询用户表分区信息
select table_name,partition_name,tablespace_name
from user_tab_partitions

--向范围分区增加分区
alter table scott.range_test
add partition part6 values less than (to_date('2020-01-01','yyyy-mm-dd'));
--删除一个分区,删除这个分区时这个分区里的数据也会一同删除
alter table scott.range_test drop partition part4;

ORA-14074:分区界限必须调整为高于最后一个分区界限。
一旦列表分区表有一个MAXVALUE分区,要再向这个表中增加更多的分区就必须拆分分区或者删除MAXVALUE分区
,然后增加新的分区再加回MAXVALUE分区。

当插入的数据超出分区范围时报错如下:
ORA-14400:插入的分区关键字未映射到任何分区。
此时要新增相应的分区。

3、列表分区
列表分区根据离散的值列表。
--创建列表分区
create table list_exp
(state_name varchar2(20),city varchar2(20))
partition by list (city)
(partition SZ values ('SZ'),
partition SH values ('SH'),
partition BJ values ('BJ'));

--向列表分区增加分区
alter table list_exp
add partition TJ values('TJ');

--创建列表分区并使用 default 键值
create table list_exp2
(state_name varchar2(20),city varchar2(20))
partition by list (city)
(partition SZ values ('SZ'),
partition SH values ('SH'),
partition BJ values ('BJ'),
partition part_MAX values(default));

一旦列表分区表有一个DEFAULT分区,要能再向这个表中增加更多的分区就必须拆分分区或者删除DEFAULT分区,
然后增加新的分区再加回DEFAULT分区。


CREATE TABLE "SCOTT"."EMP_LIST"
( "EMPNO" NUMBER(4,0),
"ENAME" VARCHAR2(10),
"JOB" VARCHAR2(9),
"MGR" NUMBER(4,0),
"HIREDATE" DATE,
"SAL" NUMBER(7,2),
"COMM" NUMBER(7,2),
"DEPTNO" NUMBER(2,0) not null
)
partition BY list(deptno)
(partition P10 values(10),
partition P20 values(20),
partition P30 values(30),
partition P40 values(40),
partition P50 values(50)
);
--向列表分区插入数据
insert into scott.emp_list select * from scott.emp;

insert into table_name1 select * from table_name2 --要求table_name1表必须存在。
select * into table_name1 from table_name2; --要求table_name1表不存在,insert时会自动
创建table_name1表。

4、哈希分区
对一个表执行散列分区时,oracle会对分区键应用一个散列函数,以此确定数据应当放在N个分区中的哪个分区
中。oracle建议N是2的一个幂(2、4、8、16等),从而得到最佳的总体分布。
散列分区设计是为了能使数据很好地分布在多个不同的设备上,为表选择的散列键应当是唯一的一个列或组列,
或者至少有足够的相异值,以便行能在多个分区上很好地(均匀地)分布。

--创建哈希分区
create table scott.hash_exp
(hash_key date not null,
eid varchar2(20) not null,
ename varchar2(20) not null)
partition by hash (hash_key)
(partition part_1 tablespace users,
partition part_2 tablespace test
);

--向哈希分区表插入数据
insert into scott.hash_exp values(TO_DATE('2004-10-01','yyyy-mm-dd'),'SZ10001','Alex');
insert into scott.hash_exp values(TO_DATE('2015-10-01','yyyy-mm-dd'),'SZ10002','Owen');
insert into scott.hash_exp values(TO_DATE('2018-10-01','yyyy-mm-dd'),'SZ10003','pipi');

--向哈希分区增加两个分区
alter table scott.hash_exp
add partition part_3 tablespace test;
alter table scott.hash_exp
add partition part_4 tablespace users;

注意事项:
使用哈希分区将无从控制一行数据最终会存放在哪个分区。
如果改变哈希分区的个数,数据会在所有分区中重新分布,向一个哈希分区表增加或删除一个分区时,将导致所
有的数据都重写。
分区数应该是2的幂,如果分区数是2的幂,那么分区将会均匀分布。

5、组合分区
在组合分区中,顶层分区机制总是区间分区。第二级分区机制可能是列表分区或散列分区。
*范围--哈希组合分区,首先使用范围分区进行第一级分区,然后根据哈希值进行第二级分类,最终将数据存放
在二级子分区。
*范围--列表分区中,一级使用范围分区,二级使用列表分区。

--创建范围--哈希组合分区
create table scott.composite_exp
(range_key date not null,
hash_key int not null,
eid varchar2(20),
ename varchar2(20)
)
partition by range(range_key)
subpartition by hash(hash_key) subpartitions 2
(
partition part1 values less than(to_date('2015-01-01','yyyy-mm-dd'))
(subpartition part1_sub1,
subpartition part1_sub2
),
partition part_2 values less than(to_date('2016-01-01','yyyy-mm-dd'))
(subpartition part2_sub1,
subpartition part2_sub2
),
partition part_max values less than(MAXVALUE)
(subpartition partMAX_sub1,
subpartition partMAX_sub2
)
);

--创建范围--散列分区
create table scott.compo_range_list_exp
(range_key date not null,
list_key int not null,
eid varchar2(20),
ename varchar2(20)
)
partition by range(range_key)
subpartition by list(list_key)
(
partition part1 values less than(to_date('2015-01-01','yyyy-mm-dd'))
(subpartition part1_sub1 values(1,3,5,7),
subpartition part1_sub2 values(2,4,6,8)
),
partition part_2 values less than(to_date('2016-01-01','yyyy-mm-dd'))
(subpartition part2_sub1 values(1,3),
subpartition part2_sub2 values(2,4),
subpartition part2_sub3 values(5,7),
subpartition part2_sub4 values(6,8)
),
partition part_max values less than(MAXVALUE)
(subpartition partMAX_sub1 values(default)
)
);

6、分区键修改引起的问题
分区键的实现分区的依据,如果用于确定分区的列有修改,则需要考虑两种情况:
(1)、修改不会导致使用一个不同的分区,行仍属于原来的分区。这在所有情况下都得到支持。
(2)、修改会导致跨行分区移动,只有当表启用了行移动时才支持这种情况,否则会产生一个错误。
例如:
SQL> update scott.range_test
2 set range_key = (to_date('2015-07-01','yyyy-mm-dd'))
3 where range_key = (to_date('2014-10-01','yyyy-mm-dd'));
update scott.range_test
set range_key = (to_date('2015-07-01','yyyy-mm-dd'))
where range_key = (to_date('2014-10-01','yyyy-mm-dd'))
ORA-14402: 更新分区关键字列将导致分区的更改

解决这个障碍的关键就是启动移动特性。允许从一个分区移动到另一个分区。行的ROWID会由于更新而改变。
例如:
SQL> select rowid from scott.range_test
2 where range_key = (to_date('2014-10-01','yyyy-mm-dd'));
ROWID
--------------------------------------------------------------------------------
AAAVckAAEAAABEvAAA

--启用行移动
SQL> alter table scott.range_test enable row movement;
Table altered

--更新数据

SQL> update scott.range_test
2 set range_key = (to_date('2015-07-01','yyyy-mm-dd'))
3 where range_key = (to_date('2014-10-01','yyyy-mm-dd'));
1 row updated
SQL>commit;


--查看更新后的ROWID
SQL> SELECT ROWID from scott.range_test where uname='JESSICA';
ROWID
--------------------------------------------------------------------------------
AAAVclAAEAAABUyAAA

SQL> SELECT ROWID from scott.range_test where range_key = (to_date('2015-07-01','yyyy-mm-
dd'));
ROWID
--------------------------------------------------------------------------------
AAAVclAAEAAABUyAAA

以下情况也会引起ROWID改变
(1)、更新IOT的主键可能导致ROWID改变,该行的通用ROWID(UROWID)也会改变。
(2)、oracle 10g的FALSHBACK TABLE命令可能改变行的ROWID。
(3)、oracle 10g的ALTER TABLE SHRINK命令也可能使行的ROWID改变。


7、索引分区
设计合理的分区索引对于表分区技术应用的好坏关系密切。分区索引有:本地前缀分区索引、本地非前缀索引和
全局分区索引。
前缀索引: 所谓前缀索引是指分区字段是索引字段的前缀。
本地非前缀分区索引: 创建的本地分区索引不使用表分区的分区键作为前缀就是本地非前缀分区索引。在表分
区被DROP或者MERGE后,本地非前缀分区索引依然有效。
全局分区索引: 索引的分区与表的分区无关。有两种类型:全局范围分区索引和全局哈希分区索引。

本地分区索引不会受到表分区的变化影响,本地分区索引自动维护,全局分区索引一旦表分区变化则失效,需要
重建。

8、创建本地前缀分区索引
--创建本地前缀分区索引
create index scott.idx_range_test_rangekey on scott.range_test(range_key) local;


9、创建本地非前缀分区索引
--创建本地非前缀分区索引
create index scott.idx_range_test_eid on scott.range_test(emid) local;

10、创建全局索引
create index scott.idx_range_test_ename on scott.range_test(ename) global;

11、新增分区
如果分区边界不是MAXVALUE,那么可以直接添加一个新分区,如果边界是MAXVALUE,则需要先删除掉原有分区
,然后再添加,或者采用分区的拆分。
--删除边界分区
SQL>alter table pt drop partition p_max;
--新增分区
SQL>alter table pt add partition p_9000 values less than(9000) tablespace users;
--增加边界分区
SQL>alter table pt add partition p_max values less than(maxvalue) tablespace users;

**表分区变化后,全局分区索引将失效,需要重建。

12、移动分区
--创建一个新表空间
SQL>create tablespace newtbs datafile '/u01/app/oracle/oradata/newtbs.dbf' size 100m
autoextend on;
--移动表分区
SQL>alter table pt move partition p_9000 tablespace newtbs;

表分区移动后全局分区索引会失效,需要重建。
--手工重建全局分区索引
SQL>alter index idx_global_pt_owner rebuild;

13、截断表分区
SQL>alter table pt truncate partition p_9000;

14、删除表分区
SQL> alter table pt drop partition p_9000;

15、拆分表分区
拆分分区是把一个分区拆分成两个分区,利用split技术可以实现这种拆分。


--创建分区表
create table scott.pt
(owner,object_name,subobject_name,object_id,data_object_id,object_type,created,last_ddl_time,
timestamp,status,temporary,generated,secondary)
partition by range(object_id)
(partition p_3000 values less than(3000) tablespace users,
partition p_6000 values less than(6000) tablespace users,
partition p_max values less than(maxvalue)
)
as
select
owner,object_name,subobject_name,object_id,data_object_id,object_type,created,last_ddl_time,t
imestamp,status,temporary,generated,secondary
from dba_objects;

--创建全局索引
create index idx_global_pt_owner on pt(owner) global;

--创建本场前缀分区索引
create index idx_local_pt_object_id on pt(object_id) local;

--查看索引状态
select index_name,status from user_indexes where table_name='PT';


--拆分分区
alter table pt split partition p_max at (9000)
into (partition p_9000 tablespace users,partition p_max tablespace users);

--重建全局索引
alter index idx_global_pt_owner rebuild;


--验证拆分分区
select table_name,partition_name,tablespace_name from user_tab_partitions;

--查询分区边界值
select max(object_id),min(object_id) from pt partition(p_6000);


16、合并分区
相邻的分区可以合并为一个分区,新分区的下边界为原来边界值较低的分区,上边界为原来边界值较高的分区,
原先的局部索引也相应的会合并,全局索引会失效,需要重建。

--合并分区
alter table pt merge partitions p_6000,p_9000 into partition p_9000;

--查看表分区情况
select table_name,partition_name,tablespace_name from user_tab_partitions where
table_name='PT';

17、分区交换
分区的交换可以把一个表和分区表中的一个分区中的数据进行对换,分区的交换只是一个数据字典的操作,因此
操作速度很快。
操作示例:
--截断p_6000分区的数据
alter table scott.pt truncate partition p_6000;

--查询分区数据
SQL> select count(*) from scott.pt partition(p_6000);

COUNT(*)
----------
0

--创建用于交换的表数据
SQL> create table scott.t_6000 as select
owner,object_name,subobject_name,object_id,data_object_id,object_type,created,last_ddl_time,t
imestamp,status,temporary,generated,secondary from dba_objects where object_id>=3000 and
object_id<6000;

Table created.

--使用EXCHANGE指令交换分区
alter table scott.pt exchange partition p_6000 with table scott.t_6000;
--验证分区数据
SQL> select count(*) from scott.pt partition(p_6000);

COUNT(*)
----------
2998
SQL> select count(*) from scott.t_6000;

COUNT(*)
----------
0
--表scott.t_6000的数据被交换到了scott.pt表的partition(p_6000)里了,表scott.t_6000表里已经没有数
据了。

--使用without validation 跳过数据检查示例
--截断p_6000分区的数据
alter table scott.pt truncate partition p_6000;
--创建用于交换的表数据
SQL> create table scott.t_7000 as select
owner,object_name,subobject_name,object_id,data_object_id,object_type,created,last_ddl_time,t
imestamp,status,temporary,generated,secondary from dba_objects where object_id>=3000 and
object_id<7000;
--不带 without validation 参数交换数据
SQL> alter table scott.pt exchange partition p_6000 with table scott.t_7000;
alter table scott.pt exchange partition p_6000 with table scott.t_7000
*
ERROR at line 1:
ORA-14099: all rows in table do not qualify for specified partition
ORA-14099:表中不是所有行都符合指定的分区。


--使用without validation 跳过数据检查
SQL> alter table scott.pt exchange partition p_6000 with table scott.t_7000 without
validation;

Table altered.

--验证数据交换,发现会把交换表里所有数据插入到partition(p_6000)里。
SQL> select count(*) from scott.pt partition(p_6000);

COUNT(*)
----------
3970

--EXCHANGE还有一个子句 including indexes,指分区和表的索引相互交换,索引也可以交换。

猜你喜欢

转载自www.cnblogs.com/owenzou/p/9723595.html