Shrink 机制的一点研究

10g开始,oracle开始提供Shrink的命令,假如我们的表空间中支持自动段空间管理 (ASSM),就可以使用这个特性缩小段,即降低HWM。这里需要强调一点,10g的这个新特性,仅对ASSM表空间有效,否则会报 ORA-10635: Invalid segment or tablespace type

在第4部分,我们已经讨论过,如何考察在ASSMtable是否需要回收浪费的空间,这里,我们来讨论如和对一个ASSMsegment回收浪费的空间。

   同样,我们用系统视图all_objects来在tablespace ASSM上创建测试表my_objects,这一小节的内容,实验环境为oracle10.1.0.2

SQL> select * from v$version;

 

BANNER

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

Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Prod

PL/SQL Release 10.1.0.2.0 - Production

CORE    10.1.0.2.0   Production

 

TNS for 32-bit Windows: Version 10.1.0.2.0 - Production

NLSRTL Version 10.1.0.2.0 – Production

 

SQL> select TABLESPACE_NAME,BLOCK_SIZE,EXTENT_MANAGEMENT,

  2  ALLOCATION_TYPE, SEGMENT_SPACE_MANAGEMENT

  3  from dba_tablespaces where TABLESPACE_NAME = 'ASSM';

 

TABLESPACE_NAME  BLOCK_SIZE EXTENT_MANAGEMENT ALLOCATION_TYPE SEGMENT_SPACE_MANAGEMENT

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

ASSM                   8192 LOCAL             UNIFORM         AUTO

 

SQL> create table my_objects tablespace assm

  2  as select * from all_objects;

Table created

 

然后我们随机地从table MY_OBJECTS中删除一部分数据:

SQL> select count(*) from my_objects;

  COUNT(*)

----------

     47828

SQL> delete from my_objects where object_name like '%C%';

16950 rows deleted

 

SQL> delete from my_objects where object_name like '%U%';

4503 rows deleted

 

SQL> delete from my_objects where object_name like '%A%';

6739 rows deleted

 

现在我们使用show_spaceshow_space_assm来看看my_objects的数据存储状况:

SQL> exec show_space('MY_OBJECTS','DLINGER');

Total Blocks............................680

Total Bytes.............................5570560

Unused Blocks...........................1

Unused Bytes............................8192

Last Used Ext FileId....................6

Last Used Ext BlockId...................793

Last Used Block.........................4

 

PL/SQL 过程已成功完成。

 

SQL> exec show_space_assm('MY_OBJECTS','DLINGER');

free space 0-25% Blocks:................0

free space 25-50% Blocks:...............205

free space 50-75% Blocks:...............180

free space 75-100% Blocks:..............229

Full Blocks:............................45

Unformatted blocks:.....................0

 

PL/SQL 过程已成功完成。

 

这里,table my_objectsHWM下有679block,其中,free space25-50%block205个,free space50-75%block180个,free space75-100%block229个,full spaceblock只有45个,这种情况下,我们需要对这个table的现有数据行进行重组。

要使用assm上的shink,首先我们需要使该表支持行移动,可以用这样的命令来完成:

alter table my_objects enable row movement;

现在,就可以来降低my_objectsHWM,回收空间了,使用命令:

alter table bookings shrink space;

我们具体的看一下实验的结果:

SQL> alter table my_objects enable row movement;

表已更改。

 

SQL> alter table my_objects shrink space;

表已更改。

 

SQL> exec show_space('MY_OBJECTS','DLINGER');

Total Blocks............................265

Total Bytes.............................2170880

Unused Blocks...........................2

Unused Bytes............................16384

Last Used Ext FileId....................6

Last Used Ext BlockId...................308

Last Used Block.........................3

 

PL/SQL 过程已成功完成。

 

SQL> exec show_space_assm('MY_OBJECTS','DLINGER');

free space 0-25% Blocks:................0

free space 25-50% Blocks:...............1

free space 50-75% Blocks:...............0

free space 75-100% Blocks:..............0

Full Blocks:............................249

Unformatted blocks:.....................0

 

PL/SQL 过程已成功完成。

 

在执行玩shrink命令后,我们可以看到,table my_objectsHWM现在降到了264的位置,而且HWM下的block的空间使用状况,full spaceblock249个,free space 25-50% Block只有1个。

 

我们接下来讨论一下shrink的实现机制,我们同样使用讨论move机制的那个实验来观察。

SQL> create table TEST_HWM (id int ,name char(2000)) tablespace ASSM;

 

Table created

 

table test_hwm中插入如下的数据:

insert into TEST_HWM values (1,'aa');

insert into TEST_HWM values (2,'bb');

insert into TEST_HWM values (2,'cc');

insert into TEST_HWM values (3,'dd');

insert into TEST_HWM values (4,'ds');

insert into TEST_HWM values (5,'dss');

insert into TEST_HWM values (6,'dss');

insert into TEST_HWM values (7,'ess');

insert into TEST_HWM values (8,'es');

insert into TEST_HWM values (9,'es');

insert into TEST_HWM values (10,'es');

 

我们来看看这个tablerowidblockID和信息:

SQL>  select rowid , id,name from TEST_HWM;

 

ROWID                      ID NAME

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

AAANhqAAGAAAAFHAAA        1 aa

AAANhqAAGAAAAFHAAB        2 bb

AAANhqAAGAAAAFHAAC        2 cc

AAANhqAAGAAAAFIAAA         3 dd

AAANhqAAGAAAAFIAAB         4 ds

AAANhqAAGAAAAFIAAC         5 dss

AAANhqAAGAAAAFJAAA         6 dss

AAANhqAAGAAAAFJAAB         7 ess

AAANhqAAGAAAAFJAAC         8 es

AAANhqAAGAAAAFKAAA         9 es

AAANhqAAGAAAAFKAAB         10 es

 

11 rows selected

 

SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

  2  from dba_extents  where segment_name='TEST_HWM' ;

 

EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

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

         0          6            6        324          5

         1          6            6        329          5

 

然后从table test_hwm中删除一些数据:

delete from TEST_HWM where id = 2;
delete from TEST_HWM where id = 4;
delete from TEST_HWM where id = 3;
delete from TEST_HWM where id = 7;
delete from TEST_HWM where id = 8;

观察table test_hwmrowidblockid的信息:

SQL> select rowid , id,name from TEST_HWM;

 

ROWID                      ID NAME

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

AAANhqAAGAAAAFHAAA      1 aa

AAANhqAAGAAAAFIAAC       5 dss

AAANhqAAGAAAAFJAAA       6 dss

AAANhqAAGAAAAFKAAA       9 es

AAANhqAAGAAAAFKAAB       10 es

 

SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

  2   from dba_extents  where segment_name='TEST_HWM' ;

 

EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

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

         0          6            6        324          5

         1          6            6        329          5

 

从以上的信息,我们可以看到,在table test_hwm中,剩下的数据是分布在AAAAFHAAAAFIAAAAFJAAAAFK这样四个连续的block

 

SQL> exec show_space_assm('TEST_HWM','DLINGER');

free space 0-25% Blocks:................0

free space 25-50% Blocks:...............1

free space 50-75% Blocks:...............3

free space 75-100% Blocks:..............3

Full Blocks:............................0

Unformatted blocks:.....................0

 

通过show_space_assm我们可以看到目前这四个block的空间使用状况,AAAAFHAAAAFIAAAAFJ上各有一行数据,我们猜测free space50-75%3block是这三个block,那么free space25-50%1block就是AAAAFK了,剩下free space 75-100% 3block,是HWM下已格式化的尚未使用的block。(关于assmhwm的移动我们前面已经详细地讨论过了,在extent不大于于16block时,是以一个extent为单位来移动的)

 

然后,我们对table my_objects执行shtink的操作:

SQL> alter table test_hwm enable row movement;

 

Table altered

 

SQL> alter table test_hwm shrink space;

 

Table altered

 

SQL> select rowid ,id,name from TEST_HWM;

 

ROWID                       ID NAME

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

AAANhqAAGAAAAFHAAA      1 aa

AAANhqAAGAAAAFHAAB      10 es

AAANhqAAGAAAAFHAAD      9 es

AAANhqAAGAAAAFIAAC       5 dss

AAANhqAAGAAAAFJAAA       6 dss

 

SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS

  2  from dba_extents  where segment_name='TEST_HWM' ;

 

 EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS

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

         0          6            6        324          5

         1          6            6        329          5

 

当执行了shrink操作后,有意思的现象出现了。我们来看看oracle是如何移动行数据的,这里的情况和move已经不太一样了。我们知道,在move操作的时候,所有行的rowid都发生了变化,table所位于的block的区域也发生了变化,但是所有行物理存储的顺序都没有发生变化,所以我们得到的结论是,oracleblock为单位,进行了block间的数据copy。那么shrink后,我们发现,部分行数据的rowid发生了变化,同时,部分行数据的物理存储的顺序也发生了变化,而table所位于的block的区域却没有变化,这就说明,shrink只移动了table其中一部分的行数据,来完成释放空间,而且,这个过程是在table当前所使用的block中完成的。

 

那么oracle具体移动行数据的过程是怎样的呢?我们根据这样的实验结果,可以来猜测一下:

Oracle是以行为单位来移动数据的。Oracle从当前table存储的最后一行数据开始移动,从当前table最先使用的block开始搜索空间,所以,shrink之前,rownum10的那行数据(10,es),被移动到block AAAAFH上,写到(1,aa)这行数据的后面,所以(10,es)的rownumrowid同时发生改变。然后是(9,es)这行数据,重复上述过程。这是oracle从后向前移动行数据的大致遵循的规则,那么具体移动行数据的的算法是比较复杂的,包括向ASSMtableinsert数据使用block的顺序的算法也是比较复杂的,大家有兴趣的可以自己来研究,在这里我们不多做讨论。

 

我们还可以在shrink table的同时shrink这个table上的index

alter table my_objects shrink space cascade;

同样地,这个操作只有当table上的index也是ASSM时,才能使用。

   

关于日志的问题,我们对比了同样数据量和分布状况的两张table,在moveshrink下生成的redo sizetable上没有index的情况下):

SQL> select tablespace_name,SEGMENT_SPACE_MANAGEMENT from dba_tablespaces

  2  where tablespace_name in('ASSM','HWM');

 

TABLESPACE_NAME                SEGMENT_SPACE_MANAGEMENT

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

ASSM                           AUTO

HWM                            MANUAL

SQL> create table my_objects  tablespace ASSM as select * from all_objects where rownum<20000;

Table created

 

SQL> create table my_objects1 tablespace HWM  as select * from all_objects where rownum<20000;

Table created

 

SQL> select bytes/1024/1024 from user_segments where segment_name = 'MY_OBJECTS';

BYTES/1024/1024

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

         2.1875

 

SQL> delete from my_objects where object_name like '%C%';

7278 rows deleted

SQL> delete from my_objects1 where object_name like '%C%';

7278 rows deleted

SQL> delete from my_objects where object_name like '%U%';

2732 rows deleted

SQL> delete from my_objects1 where object_name like '%U%';

2732 rows deleted

SQL> commit;

Commit complete

SQL>  alter table my_objects enable row movement;

Table altered

 

SQL> select value from v$mystat, v$statname

  2  where v$mystat.statistic# = v$statname.statistic#

  3  and  v$statname.name = 'redo size';

     VALUE

----------

  27808792

 

SQL> alter table my_objects shrink space;

Table altered

 

SQL> select value from v$mystat, v$statname

  2  where v$mystat.statistic# = v$statname.statistic#

  3  and  v$statname.name = 'redo size';

     VALUE

----------

  32579712

 

SQL> alter table my_objects1 move;

Table altered

 

SQL> select value from v$mystat, v$statname

  2  where v$mystat.statistic# = v$statname.statistic#

  3  and  v$statname.name = 'redo size';

     VALUE

----------

  32676784

对于table my_objects,进行shrink,产生了32579712 – 27808792=4770920,约4.5Mredo ;对table my_objects1进行move,产生了32676784-32579712=            97072,95Kredo size。那么,与move比较起来,shrink的日志写要大得多。

 

 

Shrink的几点问题:

a.  shrinkindex是否需要rebuild

因为shrink的操作也会改变行数据的rowid,那么,如果table上有index时,shrink tableindex会不会变为UNUSABLE呢?我们来看这样的实验,同样构建my_objects的测试表:

create table my_objects  tablespace ASSM as select * from all_objects where rownum<20000;

create index i_my_objects on my_objects (object_id);

delete from my_objects where object_name like '%C%';

delete from my_objects where object_name like '%U%';

现在我们来shrink table my_objects

SQL> alter table my_objects enable row movement;

 

Table altered

 

SQL> alter table my_objects shrink space;

 

Table altered

SQL> select index_name,status from user_indexes where index_name='I_MY_OBJECTS';

 

INDEX_NAME                     STATUS

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

I_MY_OBJECTS                    VALID

我们发现,table my_objects上的index的状态为VALID,估计shrink在移动行数据时,也一起维护了index上相应行的数据rowid的信息。我们认为,这是对于move操作后需要rebuild index的改进。但是如果一个table上的index数量较多,我们知道,维护index的成本是比较高的,shrink过程中用来维护index的成本也会比较高。

 

b.  shrink时对tablelock

在对table进行shrink时,会对table进行怎样的锁定呢?当我们对table MY_OBJECTS进行shrink操作时,查询v$locked_objects视图可以发现,table MY_OBJECTS上加了row-X (SX) lock

SQL>select OBJECT_ID, SESSION_ID,ORACLE_USERNAME,LOCKED_MODE from v$locked_objects;

 

 OBJECT_ID SESSION_ID ORACLE_USERNAME    LOCKED_MODE

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

     55422          153 DLINGER                      3

SQL> select object_id from user_objects where object_name = 'MY_OBJECTS';

 

 OBJECT_ID

----------

     55422

那么,当table在进行shrink时,我们对table是可以进行DML操作的。

 

c.  shrink对空间的要求

我们在前面讨论了shrink的数据的移动机制,既然oracle是从后向前移动行数据,那么,shrink的操作就不会像move一样,shrink不需要使用额外的空闲空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值