ITPub博客

首页 > 数据库 > Oracle > split 分区的简单研究

split 分区的简单研究

原创 Oracle 作者:space6212 时间:2019-04-23 11:45:07 0 删除 编辑
1、split时需要有多少空闲空间
2、split会不会产生大量的redo
3、split的实现原理
[@more@]我们要分两种情况讨论:
1、在split边界的两边都有值
2、只在split边界的一边有值

第一个问题:
1、在split边界的两边都有值
我们先创建一个只有20M的表空间,我们构造一个大小为11M的分区,看空间是否足够。

drop table s purge;
create table s(a char(1950),b date)
partition by range(b)
(partition p2008 values less than (to_date('2008-12-31','yyyy-mm-dd')) tablespace test
) pctfree 0;
--让两部分数据差别很大,更能说明问题
insert into s select 'a',sysdate-100 from dual connect by rownum<=1;
insert into s select 'a',sysdate from dual connect by rownum<=5500;
commit;

suk@D10R2N1> select round(sum(bytes))/1024/1024 "size(M)" from dba_segments where segment_name='S' and owner='SUK';

size(M)
----------
11

suk@D10R2N1> select sysdate from dual;

SYSDATE
-------------------
2008-10-17 02:01:04

suk@D10R2N1> ALTER TABLE S SPLIT partition p2008 AT (TO_DATE('2008-10-01','YYYY-MM-DD')) INTO (PARTITION p1 TABLESPACE TEST,PARTITION p2 TABLESPACE TEST) ;

ERROR at line 1:
ORA-01652: unable to extend temp segment by 128 in tablespace TEST

从这个例子可以看出,如果在split边界的两边都有值,则需要的空闲空间至少能放下被split的分区的数据。

2、只在split边界的一边有值
drop table s purge;
create table s(a char(1950),b date)
partition by range(b)
(partition p2008 values less than (to_date('2008-12-31','yyyy-mm-dd')) tablespace test
) pctfree 0;
--让范围都限制在当天
insert into s select 'a',trunc(sysdate) from dual connect by rownum<=9500;
commit;

suk@D10R2N1> select round(sum(bytes))/1024/1024 "size(M)" from dba_segments where segment_name='S' and owner='SUK';

size(M)
----------
19

suk@D10R2N1> ALTER TABLE S SPLIT partition p2008 AT (TO_DATE('2008-10-01','YYYY-MM-DD')) INTO (PARTITION p1 TABLESPACE TEST,PARTITION p2 TABLESPACE TEST) ;

Table altered.

这个例子说明,如果只在split边界的一边有值,则不需要额外的空间就可以split。

为什么区别这么大呢?ORACLE在内部是怎么做的?
我们仍然用两种方式做另外一个测试来验证:
1、在split边界的两边都有值
--创建表
drop table ex1 purge;
drop table ex2 purge;
drop table s purge;
suk@D10R2N1> create table s(a char(1950),b date)
2 partition by range(b)
3 (partition p2008 values less than (to_date('2008-12-31','yyyy-mm-dd')) tablespace test) pctfree 0;

Table created.

--记录redo size
suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
57820

suk@D10R2N1> insert into s select 'a',sysdate-mod(rownum,40) from dual connect by rownum<=4000;

4000 rows created.

suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
8471560

suk@D10R2N1> create table ex1 as select block_id,block_id+blocks-1 end_block from dba_extents where partition_name='P2008' and segment_name='S' order by extent_id;

Table created.

suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
8489680

suk@D10R2N1> ALTER TABLE S SPLIT partition p2008 AT (TO_DATE('2008-10-01','YYYY-MM-DD')) INTO (PARTITION P1 TABLESPACE test,PARTITION P2 TABLESPACE test) ;

Table altered.

suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
16931784

--从产生的redo看,spilt和第一步insert产生的redo量基本一致,故可以推断,split partition也是insert ....select....操作,当被split的分区的所有的数据insert到新分区后,删除旧分区。

suk@D10R2N1> create table ex2 as select block_id,block_id+blocks-1 end_block from dba_extents where partition_name in ('P1','P2') and segment_name='S' order by extent_id;

Table created.

--我们再从记录下来的extent信息看看使用情况:
suk@D10R2N1> set serverout on
suk@D10R2N1> DECLARE l_cnt INT:=0;
2 BEGIN
3 FOR c IN (SELECT * FROM ex2) LOOP
4 SELECT COUNT(1) INTO l_cnt FROM ex1 WHERE (c.block_id BETWEEN ex1.block_id AND ex1.end_block) OR (c.end_block BETWEEN ex1.block_id AND ex1.end_block);
5 IF l_cnt>0 THEN
6 dbms_output.put_line(c.block_id);
7 END IF;
8 END LOOP;
9 END;
10 /

PL/SQL procedure successfully completed.

这段程序的是为了找出S表在spilt前后被重用的block,没有结果输出,可以判定spilt后使用的空间都不在split前使用的空间范围内,这也说明了spilt时,所在表空间的空闲空间必须足够容纳被spilt的分区的所有数据。

2、只在split边界的一边有值
我们再看另外一种情况:只在split边界的一边有值

suk@D10R2N1> create table s(a char(1950),b date)
2 partition by range(b)
3 (partition p2008 values less than (to_date('2008-12-31','yyyy-mm-dd')) tablespace test) pctfree 0;

Table created.

suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
0

suk@D10R2N1> insert into s select 'a',sysdate-40 from dual connect by rownum<=4000;

4000 rows created.

suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
8413784

suk@D10R2N1> create table ex1 as select block_id,block_id+blocks-1 end_block from dba_extents where partition_name='P2008' and segment_name='S' order by extent_id;

Table created.

suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
8431020

suk@D10R2N1> ALTER TABLE S SPLIT partition p2008 AT (TO_DATE('2008-10-01','YYYY-MM-DD')) INTO (PARTITION P1 TABLESPACE test,PARTITION P2 TABLESPACE test) ;

Table altered.

suk@D10R2N1> select value from v$mystat where statistic#=134;

VALUE
----------
8515144


首先我们可以看出,这种情况下,split产生的redo非常少,我们可以推测这种情况下,数据没有做实际的移动,只是简单修改数据字典的信息。为了验证这一点,我们看看spilt前后extent信息:

suk@D10R2N1> select block_id,end_block from ex1
2 minus
3 select block_id,end_block from ex2;

no rows selected
suk@D10R2N1> select block_id,end_block from ex2
2 minus
3 select block_id,end_block from ex1;

BLOCK_ID END_BLOCK
---------- ----------
1033 1040

suk@D10R2N1> select segment_name,partition_name from dba_extents where block_id=1033 and blocks=1040-1033+1 and owner='SUK';

SEGMENT_NAME PARTITION_NAME
------------------------------ ------------------------------
S P2

这个结果表明:对于P1,它使用的block和split前是完全一致的;而仅仅是为P2单独分配一个初始化的extent。这也证明了只在split边界的一边有值的情况下,数据没有发生实际移动,仅仅修改一些数据字典信息。
但是这种情况也有特例:就算只在split边界的一边有值,如果有值的一端在split后对应的分区前后表空间不一致,,则新的表空间的空闲空间至少能放下被split的分区的数据。

如果有兴趣,可以用trace跟踪一下,我们可以发现split时,会递归调用如下SQL:
select /*+ FIRST_ROWS PARALLEL("S", 1) */ 1
from
"SUK"."S" PARTITION ("P2008") where ( ( ( ( "B" < TO_DATE(' 2008-10-01
00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN') ) ) ) )
and rownum < 2
select /*+ FIRST_ROWS PARALLEL("S", 1) */ 1
from
"SUK"."S" PARTITION ("P2008") where ( ( ( ( "B" >= TO_DATE(' 2008-10-01
00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN') ) ) ) )
and rownum < 2

这两个SQL就是用于判断在split边界的两边是否有值。在这里,oracle是非常聪明的,它根据这个判断结果来决定是否有必要移动数据,在某些情况下可以节省很多资源。

到这里,我们可以回答开头提出的几个问题了:
1、split时需要有多少空闲空间
如果在split边界的两边都有值,则需要的空闲空间必能能够放下被split分区的所有数据。
只在split边界的一边有值,且split后有值的一端对应的表空间与原分区对应表空间不一致,则需要的空闲空间必能能够放下被split分区的所有数据。
只在split边界的一边有值,且split后有值的一端对应的表空间与原分区对应表空间一致,则无需额外的空间。
2、split会不会产生大量的redo
split做的实际上是insert...select...操作,所以产生的日志量与是否有数据移动有关。
如果在split边界的两边都有值,则有数据移动,会为移动的数据产生redo。
只在split边界的一边有值,且split后有值的一端对应的表空间与原分区对应表空间不一致,则有数据移动,会为移动的数据产生redo。
只在split边界的一边有值,且split后有值的一端对应的表空间与原分区对应表空间一致,则无数据移动,不会为数据产生redo。
3、split的实现原理
split时,首先判断在split的边界是否都有数据存在。
如果在split边界的两边都有值,则产生两个新的分区,把原分区的所有数据insert到新分区后,删除原分区,更新数据字典。
只在split边界的一边有值,且split后有值的一端对应的表空间与原分区对应表空间不一致,则产生两个新的分区,把原分区的所有数据insert到新分区后,删除原分区,更新数据字典。
只在split边界的一边有值,且split后有值的一端对应的表空间与原分区对应表空间一致,则新加一个分区,并修改原分区信息为另一个新分区的信息,无数据移动。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/231499/viewspace-1036171/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2005-01-25

  • 博文量
    117
  • 访问量
    84542