oracle碎片

oracle碎片


author:skate

time:2010-05-31

我们在使用windows的时候,都知道要定期整理磁盘碎片,因为磁盘碎片会影响性能,给管理上带来额外的
负担。那oracle更是如此,当随着数据增加,oracle处理海量数据本身就已经很费力了啊,如果再有大量
的碎片,那就是雪上加霜啊。所以碎片要引起dba的高度重视,尽早发现尽早处理。

碎片是怎么产生的呢?


简单理解就是由于更新和删除产生一些碎小的不能被再次使用的空间,根据每种不同的碎片他们的产生也是有区别的

block-level的碎片,而block又分为data block和index block,在data block中存放的是row数据,在index block
中存放的是索引键值数据,所以按上面所说,block-level碎片有细分为row-level碎片Index Leaf Block-level碎片


oracle的每一个对象都是存储在segment中,而oracle的最小分配单位是extents(区),在数据更新删除中也会产生碎片
这一级别的碎片就是segment碎片。segment又存在datafile中,而tablespace又是包含datafile的逻辑概念。所以这一层
tablespace-level碎片;tablespace是在disk上存储,所以这一层就是disk-level碎片

简单图示如下


disk-level fragmention
  tablespace-level fragmentation
    segment-level fragmentation
      block-level fragmentation
        row-level fragmentation
      index leaf block-level fragmentation

顺便提下oracle extents存在的理由

一个extents是由多个相连的block组成的,多个extents做成一个segment;extent是oracle的最小分配单位

extent的优点:
1. 提高空间分配,释放的效率,降低管理block的资源成本
2. 提高扫描的效率,因为extent是由相连blocks做成的特性,可以一次读取更多的内容,较低io读写次数

extent的缺点
容易产生碎片


如何确定产生了碎片的呢?

一。表空间碎片确定参考

由于自由空间碎片是由几部分组成,如范围数量、最大范围尺寸等,我们可用 FSFI--Free Space Fragmentation Index
(自由空间碎片索引)值来直观体现:

  FSFI=100*SQRT(max(extent)/sum(extents))*1/SQRT(SQRT(count(extents)))

可以看出,FSFI 的最大可能值为 100 (一个理想的单文件表空间)。随着范围的增加, FSFI 值缓慢下降,而随着最大范
围尺寸的减少,FSFI 值会迅速下降。通过如下语句查询FSFI:

select tablespace_name,
       sqrt(max(blocks) / sum(blocks)) * (100 / sqrt(sqrt(count(blocks)))) FSFI
  from dba_free_space
 group by tablespace_name
 order by 1;

在一个有着足够有效自由空间,且FSFI 值超过30的表空间中,很少会遇见有效自由空间的问题。当一个空间将要接近
可比参数时,就需要做碎片整理了(DMT空间可以整理,如果是LMT就无法整理)。(SMON 会将相邻的自由范围自动合并)

temp表空间(非本地管理表空间)   回收    
  可以将表空间的缺省存储参数pctincrease改为非0。一般将其设为1,如:    
  alter   tablespace   temp   default   storage(pctincrease   1);   

 

 这样smon便会将自由范围自动合并。也可以手工合并自由范围:    
  alter   tablespace   temp   coalesce;  

二。segment的碎片整理

segment的碎片整理一般主要整理table和index

9i之前碎片整理的方法

1. exp/truncate/imp

2.
alter table table_name move stroage(initial 1m)
alter index index_name rebuild [tablespace tablespace_name] [nologging] [online]

3.CTAS重组

在10g之后,不但可以用以上两种方法,还提供了新的方法

alter table <table_name> shrink space [ <null> | compact | cascade ];

compact:这个参数当系统的负载比较大时可以用,不降低HWM。如果系统负载较低时,直接用alter table table_name shrink space就一步到位了
cascade:这个参数是在shrink table的时候自动级联索引,相当于rebulid index。

普通表

shrink必须开启行迁移功能。
alter table table_name enable row movement ;

保持HWM,相当于把块中数据打结实了
alter table table_name shrink space compact;

回缩表与降低HWM
alter table table_name shrink space;

回缩表与相关索引,降低HWM
alter table table_name shrink space cascade;

回缩索引与降低HWM
alter index index_name shrink space

虽然在10g中可以用shrink ,但也有些限制:

1). 对cluster,cluster table,或具有Long,lob类型列的对象 不起作用。
2). 不支持具有function-based indexes 或 bitmap join indexes的表
3). 不支持mapping 表或index-organized表。
4). 不支持compressed 表

参考文档:
http://blog.csdn.net/wyzxg/archive/2010/05/28/5631721.aspx

从9i开始index碎片的整理除了drop and create index,还可以 alter index skate.t1_idx rebuild nologging parallel 4 online ;


表的碎片查找

一种笨的方式就是ctas一个表和原来的做下比较,看看空间大小的变化,还有一种就方法就是看视图里数据(要及时analyze才准确)
查看user_tables和user_segments

eg:

SQL> select num_rows,blocks,empty_blocks,avg_space,avg_row_len,last_analyzed from user_tables where table_name='TEST1';

  NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE AVG_ROW_LEN LAST_ANALYZED
---------- ---------- ------------ ---------- ----------- -------------
    393216        562           78         23          13 2010-5-27 21:


AVG_SPACE:表示block内的空闲空间大小,根据pctfree参数来参考,这里以默认%10为准,在block=8192里,允许空间的空间是8192 × %10=819.2,也就是说在block剩余空间是819.2时,oracle就认为它是满块了,如果再有数据就要寻找新的block了,当表的AVG_SPACE远远大于819.2时,就说明有碎片了,因为块没有被充分利用,产生block-level碎片。可以通过alter table move来整理。

那可以节省多少空间呢?
        
   数据的实际大小=AVG_ROW_LEN × NUM_ROWS=13 × 393216=5111808
          表的实际大小  =BLOCKS × block_size   =562 * 8192 =4603904

理论上AVG_ROW_LEN × NUM_ROWS <= BLOCKS × block_size 如果相差比较大,那就需要整理,也可以粗略的算下可以节省多少空间,可现在实际上确
AVG_ROW_LEN × NUM_ROWS > BLOCKS × block_size ,不知道的是什么原因?

突然想到我这个test1表里最后一个字段是clob类型的,而clob在数据大于4k时(一个lob字段包括lobindex和lobsegment,),是存储在另外的log segment中的。


而clob类型的d字段没有存储任何数据,查询user_lobs视图

SQL> select ul.table_name,ul.segment_name, ul.chunk,se.blocks,se.bytes from user_lobs ul ,user_segments se
  2  where ul.segment_name=se.segment_name
  3  and ul.table_name='TEST1'
  4  ;

TABLE_NAME                     SEGMENT_NAME                        CHUNK     BLOCKS      BYTES
------------------------------ ------------------------------ ---------- ---------- ----------
TEST1                          SYS_LOB0000183465C00004$$            8192          8      65536

可这个65536和那两个差值还差很多,再说这8个block还是empty,搞不懂了,谁知道请指点,先谢谢了啊


换个角度会什么样的?我又用CTAS穿件表test4,这回就符合理论值,无论用


alter table table_name move,还是alter table table_name shrink space compact,都是符合理论值的

create table test4 as select * from test1


分析后: analyze  table test4 compute statistics

SQL> select num_rows,blocks,empty_blocks,avg_space,avg_row_len,last_analyzed from user_tables where table_name='TEST4';

  NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE AVG_ROW_LEN LAST_ANALYZED
---------- ---------- ------------ ---------- ----------- -------------
    393216        802            6        820          12 2010-5-28 12:


   数据的实际大小=AVG_ROW_LEN × NUM_ROWS=13 × 393216=5111808
          表的实际大小  =BLOCKS × block_size   =562 * 8192 =6569984

index碎片查找

识别索引是否有碎片

获得关于索引的信息,用下面的命令


analyze index index_name validate structure 或validate index index_name

analyze index index1 validate structure:是用来分析索引的数据块是否有坏块,以及根据分析得到的
数据(存放在index_stats)來判断索引是否需要重新建立。


运行命令后,然后在视图 index_stats查询,这个视图记录索引的一些信息,这个视图只有一个记录,所以在同一时间只能分析一个索引。

1.删除的行数如占总的行数的30%,即del_lf_rows / lf_rows > 0.3,那就考虑索引碎片整理
2.如果”hight“大于4,可以考虑碎片整理
3.如果索引的行数(LF_rows)远远小于‘LF_BLKS’ ,那就说明有了一个大的删除动作,需要整理碎片

索引碎片整理方法

1. recreate index
2. alter index skate.idx_test rebuild nologging parallel 4 online ;
3. 如果叶块是half-empty or near empty,可以运行“alter index coalesce”来合并

参考:

http://blog.csdn.net/wyzxg/archive/2010/05/31/5637549.aspx

-----end-----

 

索引页块碎片(Index Leaf Block Fragmentation)

这篇文章将会回答如下问题:

什么是索引页块碎片?什么时候被重用?
什么是半空索引碎片?什么时候被重用?

oracle的标准索引结构是B×tree结构,一个B×tree结构由三种block组成

根块(root block):在B×tree里有且只有一个block,所有访问索引都从这开始,root block下有很多child blocks。


分支块(Branch blocks):这是中间层,branch block是没有什么限制的,它是随着leaf block的增加而增加的,branch block一般是4层,如果多于4层,就影响性能了。在我们删除行时,branch block是不被删除的。


叶块(leaf block):叶块是最底层,上面存储着索引条目和rowid

索引和表数据是级联关系的,当删除表数据的时候,索引条目也会被自动删除,这样在index leaf
block就会产生碎片,这也就是在OLTP系统上有大量更新的表上不建议创建大量的索引,很影响性能
有的人说删除过的索引条目空间不会被再用,因为在应用中不会再有insert相同的数据。其实这个
说法不完全对的,除了半空叶块外,其他的删除的索引空间是可被再利用的。

eg:

本文的所有实验都是在如下平台测试:

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Prod
PL/SQL Release 10.2.0.4.0 - Production
CORE    10.2.0.4.0      Production
TNS for 32-bit Windows: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production

SQL>

SQL>  create table test_idx as select seq_test.nextval id,2000 syear, a.* from d
ba_objects a;

表已创建。

SQL> insert into test_idx  select seq_test.nextval id,2001 syear, a.* from dba_o
bjects a;

已创建50780行。

SQL> insert into test_idx  select seq_test.nextval id,2002 syear, a.* from dba_o
bjects a;

已创建50780行。

SQL> commit;

提交完成。

SQL> desc test_idx
 名称                                      是否为空? 类型
 ----------------------------------------- -------- ----------------------------

 ID                                                 NUMBER
 SYEAR                                              NUMBER
 OWNER                                              VARCHAR2(30)
 OBJECT_NAME                                        VARCHAR2(128)
 SUBOBJECT_NAME                                     VARCHAR2(30)
 OBJECT_ID                                          NUMBER
 DATA_OBJECT_ID                                     NUMBER
 OBJECT_TYPE                                        VARCHAR2(19)
 CREATED                                            DATE
 LAST_DDL_TIME                                      DATE
 TIMESTAMP                                          VARCHAR2(19)
 STATUS                                             VARCHAR2(7)
 TEMPORARY                                          VARCHAR2(1)
 GENERATED                                          VARCHAR2(1)
 SECONDARY                                          VARCHAR2(1)

SQL> create unique index idx_test on test_idx(syear,id) ;

索引已创建。

SQL>
SQL>   select segment_name , bytes/1024/1024 ,
  2             blocks, tablespace_name , extents
  3     from dba_segments
  4   where segment_name = 'IDX_TEST';

SEGMENT_NA BYTES/1024/1024     BLOCKS TABLESPACE_NAME                   EXTENTS
---------- --------------- ---------- ------------------------------ ----------
IDX_TEST                 4        512 USERS                                  19

SQL>

SQL> col object_name for a10
SQL>
SQL>  select object_name, object_id, data_object_id
  2                   from dba_objects
  3                 where object_NAME='IDX_TEST' ;

OBJECT_NAM  OBJECT_ID DATA_OBJECT_ID
---------- ---------- --------------
IDX_TEST        59545          59545 ---------Used to join X$BH table(从x$bh查询缓存blocks,要用DATA_OBJECT_ID)

SQL>

查看系统现在缓存多少,这个要用sysdba用户执行


SQL> select count(*) from x$bh where obj=59545 ;

  COUNT(*)
----------
        17

查看执行计划:
SQL> set autot trace exp
SQL> select syear,id from test_idx where syear>=2000 and syear<=2002;

执行计划
----------------------------------------------------------
Plan hash value: 285868359

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

-

| Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Time
|

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

-

|   0 | SELECT STATEMENT     |          |   181K|  4613K|   102   (5)| 00:00:02
|

|*  1 |  INDEX FAST FULL SCAN| IDX_TEST |   181K|  4613K|   102   (5)| 00:00:02
|

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

-


Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("SYEAR">=2000 AND "SYEAR"<=2002)

Note
-----
   - dynamic sampling used for this statement

SQL>


执行一次查询,让oracle缓存相应的索引block

SQL> set autot trace statis
SQL> select syear,id from test_idx where syear>=2000 and syear<=2002;

已选择152340行。


统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      10562  consistent gets
        411  physical reads
          0  redo size
    2964382  bytes sent via SQL*Net to client
     112105  bytes received via SQL*Net from client
      10157  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
     152340  rows processed

SQL> select syear,id from test_idx where syear>=2000 and syear<=2002;

已选择152340行。


统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      10562  consistent gets
          0  physical reads
          0  redo size
    2964382  bytes sent via SQL*Net to client
     112105  bytes received via SQL*Net from client
      10157  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
     152340  rows processed

SQL>


这个时候再看看oracle缓存了多少

SQL> select count(*) from x$bh where obj=59545 ;

  COUNT(*)
----------
       438

由原来的17增加到438

SQL> analyze index idx_test validate structure;

索引已分析

SQL> select height, blocks, lf_blks, lf_rows, br_blks, br_rows  , del_lf_rows
om index_stats;

    HEIGHT     BLOCKS    LF_BLKS    LF_ROWS    BR_BLKS    BR_ROWS DEL_LF_ROWS
---------- ---------- ---------- ---------- ---------- ---------- -----------
         2        512        418     152340          1        417           0

SQL>

这个索引idx_test共有418个叶块都已经被缓存里了,和预期的是一样的,下面删除三分之一的数据

SQL> delete from test_idx where syear=2001;

已删除50780行。

SQL> commit;

提交完成。

SQL>

清空数据缓存

SQL> alter system flush buffer_cache;

系统已更改。

SQL> alter system flush buffer_cache;

系统已更改。

SQL> alter system flush buffer_cache;

系统已更改。

再次查询,发现缓存数有所下降了,从438到396


SQL> select count(*) from x$bh where obj=59545 ;

  COUNT(*)
----------
       396

再次执行查询,让其缓存索引块


SQL> set autot trace stat
SQL> select syear,id from test_idx where syear>=2000 and syear<=2002;

已选择101560行。


统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       7186  consistent gets
        425  physical reads
          0  redo size
    1976416  bytes sent via SQL*Net to client
      74870  bytes received via SQL*Net from client
       6772  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
     101560  rows processed

SQL>

这次查询缓存的数量发现突然增加很多,从438增加到774
SQL> select count(*) from x$bh where obj=59545 ;

  COUNT(*)
----------
       774

突然增加这么多,推测是因为删除的那些空索引块需要重新从磁盘加载到buffer cache中,所以
缓存的会突然增加,用alter system flush buffer_cache不能完全清楚data cache,下面我reboot
数据库,再来查看下

重启数据库是为了完全清空缓存的索引

SQL> shutdown immediate;
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。

SQL> startup
ORACLE 例程已经启动。
Total System Global Area  574619648 bytes
Fixed Size                  1297944 bytes
Variable Size             192938472 bytes
Database Buffers          373293056 bytes
Redo Buffers                7090176 bytes
数据库装载完毕。
数据库已经打开。

执行查询,使索引缓存
SQL> select syear,id from test_idx where syear>=2000 and syear<=2002;

已选择101560行。

再来看缓存的多少
SQL> select count(*) from x$bh where obj=59545 ;

  COUNT(*)
----------
       425

我可以从查询结果中看到,缓存结果425和删除前的438,没有太大的变化,而我删除了三分之一的
数据,按理论说应该缓存的表很少了啊,我们在查看现在的叶块是多少

SQL> analyze index idx_test validate structure;

索引已分析

SQL> select height, blocks, lf_blks, lf_rows, br_blks, br_rows  , del_lf_rows fr
om index_stats;

    HEIGHT     BLOCKS    LF_BLKS    LF_ROWS    BR_BLKS    BR_ROWS DEL_LF_ROWS
---------- ---------- ---------- ---------- ---------- ---------- -----------
         2        512        418     152340          1        417       50780

SQL>

从结果来看,叶块和删除前一样418没有变化,这就进一步证明索引叶block虽然被删除了,但是并没有
释放空间,而查询语句并不会跳过这些删除的索引块,所以这些碎片对性能产生很多的影响。

那如何完全删除索引叶块呢?


SQL> alter index idx_test rebuild nologging online;

索引已更改。

SQL> analyze index idx_test validate structure;

索引已分析

SQL> select height, blocks, lf_blks, lf_rows, br_blks, br_rows  , del_lf_rows fr
om index_stats;

    HEIGHT     BLOCKS    LF_BLKS    LF_ROWS    BR_BLKS    BR_ROWS DEL_LF_ROWS
---------- ---------- ---------- ---------- ---------- ---------- -----------
         2        384        276     101560          1        275           0

SQL>


SQL> select count(*) from x$bh where obj=59545 ;

  COUNT(*)
----------
       139

SQL>

通过以上结果可以看到删除的索引叶块的空间被释放了

在删除了2001年后 在insert2003年的

SQL>  insert into test_idx  select seq_test.nextval id,2003 syear, a.* from dba_
objects a;

已创建50781行。

SQL> commit;

提交完成。

SQL>   select segment_name , bytes/1024/1024 ,
  2             blocks, tablespace_name , extents
  3     from dba_segments
  4   where segment_name = 'IDX_TEST';

SEGMENT_NAME
--------------------------------------------------------------------------------

BYTES/1024/1024     BLOCKS TABLESPACE_NAME                   EXTENTS
--------------- ---------- ------------------------------ ----------
IDX_TEST
              4        512 USERS                                  19


SQL> analyze index idx_test validate structure;

索引已分析

SQL> select height, blocks, lf_blks, lf_rows, br_blks, br_rows  , del_lf_rows fr
om index_stats;

    HEIGHT     BLOCKS    LF_BLKS    LF_ROWS    BR_BLKS    BR_ROWS DEL_LF_ROWS
---------- ---------- ---------- ---------- ---------- ---------- -----------
         2        512        403     152341          1        402           0

SQL>

从查询结果来看,索引的总的块数为512,在delete和insert后没有增长,说明索引删除的空间
被重用了啊


什么是半空叶块(Half Empty Leaf Blocks)

一个叶块( Leaf Block)是用索引键值初始化的,当某些键值被删除后,这个叶块即包含删除的
索引键值,也包含未删除的索引键值,这时这个块就被称为”Half Empty Leaf Blocks“。

下面还是以test_idx为例


SQL>  insert into test_idx  select seq_test.nextval id,2005 syear, a.* from dba_
objects a ;

已创建50781行。

SQL>  insert into test_idx  select seq_test.nextval id,2005 syear, a.* from dba_
objects a ;

已创建50781行。

SQL>  insert into test_idx  select seq_test.nextval id,2005 syear, a.* from dba_
objects a ;

已创建50781行。

SQL>  insert into test_idx  select seq_test.nextval id,2005 syear, a.* from dba_
objects a ;

已创建50781行。

SQL> commit;

提交完成。

SQL>   select segment_name , bytes/1024/1024 ,
  2             blocks, tablespace_name , extents
  3     from dba_segments
  4   where segment_name = 'IDX_TEST';

SEGMENT_NAME
--------------------------------------------------------------------------------

BYTES/1024/1024     BLOCKS TABLESPACE_NAME                   EXTENTS
--------------- ---------- ------------------------------ ----------
IDX_TEST
              9       1152 USERS                                  24


SQL> delete from test_idx where syear=2005 and mod(id,2)=0;

已删除101562行。

SQL> commit;

提交完成。

在重新插入101562行数据


SQL>  insert into test_idx  select seq_test.nextval id,2006 syear, a.* from dba_
objects a ;

已创建50781行。

SQL>  insert into test_idx  select seq_test.nextval id,2006 syear, a.* from dba_
objects a ;

已创建50781行。

SQL>  select segment_name , bytes/1024/1024 ,
  2             blocks, tablespace_name , extents
  3     from dba_segments
  4   where segment_name = 'IDX_TEST';

SEGMENT_NAME
--------------------------------------------------------------------------------

BYTES/1024/1024     BLOCKS TABLESPACE_NAME                   EXTENTS
--------------- ---------- ------------------------------ ----------
IDX_TEST
             11       1408 USERS                                  26


SQL>

删除了101562行数据,再重新添加101562行数据,可索引块却增加了1408-1152=256个数据块,所以说半空块
索引并没有被重用。从下面的trace也可以看出

SQL> select object_id from dba_objects where object_name='IDX_TEST';

 OBJECT_ID
----------
     59545

得到tree的dump的命令如下


SQL> alter session set events 'immediate trace name treedump level 59545';

会话已更改。

然后查看对应的trace文件,如下所示:

branch: 0x100972c 16815916 (0: nrow: 3, level: 2)
   branch: 0x1007fe5 16809957 (-1: nrow: 511, level: 1)
      leaf: 0x100972d 16815917 (-1: nrow: 378 rrow: 378)
      leaf: 0x100972e 16815918 (0: nrow: 378 rrow: 378)
      .
      .
      .
      leaf: 0x1007ff2 16809970 (14: nrow: 400 rrow: 400)
      leaf: 0x1007ff6 16809974 (15: nrow: 400 rrow: 332)
      leaf: 0x1007ffa 16809978 (16: nrow: 400 rrow: 200)
      leaf: 0x1007ffe 16809982 (17: nrow: 400 rrow: 200)-----------------Half empty blocks
      leaf: 0x1008002 16809986 (18: nrow: 400 rrow: 200)
      .
      .
      .
      leaf: 0x1009f86 16818054 (19: nrow: 400 rrow: 200)
      leaf: 0x1009f4b 16817995 (20: nrow: 400 rrow: 400)
      .
      .
      .
      leaf: 0x1009f4f 16817999 (21: nrow: 400 rrow: 400)
      leaf: 0x100a15f 16818527 (274: nrow: 56 rrow: 56)

leaf: 0x1007ffe 16809982 (17: nrow: 400 rrow: 200)
解释: leaf block包含400行,这个块已经删除了200行的键值

识别索引是否有碎片

获得关于索引的信息,用下面的命令


analyze index index_name validate structure 或validate index index_name

analyze index index1 validate structure:是用来分析索引的数据块是否有坏块,以及根据分析得到的
数据(存放在index_stats)來判断索引是否需要重新建立。


运行命令后,然后在视图 index_stats查询,这个视图记录索引的一些信息,这个视图只有一个记录,所以在同一时间
只能分析一个索引。

1.删除的行数如占总的行数的30%,即del_lf_rows / lf_rows > 0.3,那就考虑索引碎片整理
2.如果”hight“大于4,可以考虑碎片整理
3.如果索引的行数(LF_rows)远远小于‘LF_BLKS’ ,那就说明有了一个大的删除动作,需要整理碎片

索引碎片整理方法

1. recreate index
2. alter index skate.idx_test rebuild nologging parallel 4 online ;
3. 如果叶块是half-empty or near empty,可以运行“alter index coalesce”来合并


参考文档:

http://oracleact.com/papers/index_leaf_block_frag.html
http://www.devx.com/gethelpon/10MinuteSolution/16596/0/page/2

 

alter table move跟shrink space的区别

今天主要从两点说他们的区别:

1. 碎片的整理

2.空间的收缩

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bi
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production

TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SQL>

创建测试表
SQL> create table test3 as
  2     select rownum id,
  3     dbms_random.string('a', round(dbms_random.value(0,10))) col1,
  4     trunc(sysdate) - dbms_random.value(1, 365*2) col2
  5     from dual connect by rownum<=10000;

Table created

SQL> select count(1) from test3;

  COUNT(1)
----------
     10000


查看表test3的blocks使用情况:

SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................40
Total Bytes.............................327680
Unused Blocks...........................3
Unused Bytes............................24576
Last Used Ext FileId....................31
Last Used Ext BlockId...................481921
Last Used Block.........................5
 *************************************************
0% -- 25% free space blocks.............0
0% -- 25% free space bytes..............0
25% -- 50% free space blocks............0
25% -- 50% free space bytes.............0
50% -- 75% free space blocks............0
50% -- 75% free space bytes.............0
75% -- 100% free space blocks...........0
75% -- 100% free space bytes............0
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................32
Total bytes.............................262144

PL/SQL procedure successfully completed


制造碎片

SQL> DELETE FROM TEST3 WHERE MOD(ID,3)=1;

3334 rows deleted

SQL> commit;

Commit complete

发现有碎片了

SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................40
Total Bytes.............................327680
Unused Blocks...........................3
Unused Bytes............................24576
Last Used Ext FileId....................31
Last Used Ext BlockId...................481921
Last Used Block.........................5
 *************************************************
0% -- 25% free space blocks.............0
0% -- 25% free space bytes..............0
25% -- 50% free space blocks............31
25% -- 50% free space bytes.............253952
50% -- 75% free space blocks............1
50% -- 75% free space bytes.............8192
75% -- 100% free space blocks...........0
75% -- 100% free space bytes............0
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................0
Total bytes.............................0

PL/SQL procedure successfully completed

SQL>

消除碎片


SQL> alter table test3 move;

Table altered

查看碎片消除的效果


SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................32
Total Bytes.............................262144
Unused Blocks...........................6
Unused Bytes............................49152
Last Used Ext FileId....................31
Last Used Ext BlockId...................485065
Last Used Block.........................2
 *************************************************
0% -- 25% free space blocks.............0
0% -- 25% free space bytes..............0
25% -- 50% free space blocks............0
25% -- 50% free space bytes.............0
50% -- 75% free space blocks............0
50% -- 75% free space bytes.............0
75% -- 100% free space blocks...........0
75% -- 100% free space bytes............0
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................22
Total bytes.............................180224

PL/SQL procedure successfully completed

SQL>

从以上看,碎片整理的效果很好!!!

下面在测试用shrink整理碎片

重建测试环境


SQL> drop table test3;

Table dropped

SQL>
SQL> create table test3 as
  2     select rownum id,
  3     dbms_random.string('a', round(dbms_random.value(0,10))) col1,
  4     trunc(sysdate) - dbms_random.value(1, 365*2) col2
  5     from dual connect by rownum<=10000;

Table created

查看test3的blocks的使用


SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................40
Total Bytes.............................327680
Unused Blocks...........................3
Unused Bytes............................24576
Last Used Ext FileId....................31
Last Used Ext BlockId...................481921
Last Used Block.........................5
 *************************************************
0% -- 25% free space blocks.............0
0% -- 25% free space bytes..............0
25% -- 50% free space blocks............0
25% -- 50% free space bytes.............0
50% -- 75% free space blocks............0
50% -- 75% free space bytes.............0
75% -- 100% free space blocks...........0
75% -- 100% free space bytes............0
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................32
Total bytes.............................262144

PL/SQL procedure successfully completed

制造碎片


SQL> delete from test3 where mod(id,3)=1;

3334 rows deleted

SQL> commit;

Commit complete

查看碎片情况
SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................40
Total Bytes.............................327680
Unused Blocks...........................3
Unused Bytes............................24576
Last Used Ext FileId....................31
Last Used Ext BlockId...................481921
Last Used Block.........................5
 *************************************************
0% -- 25% free space blocks.............0
0% -- 25% free space bytes..............0
25% -- 50% free space blocks............31
25% -- 50% free space bytes.............253952
50% -- 75% free space blocks............0
50% -- 75% free space bytes.............0
75% -- 100% free space blocks...........0
75% -- 100% free space bytes............0
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................1
Total bytes.............................8192

PL/SQL procedure successfully completed

用oracle10g新功能整理碎片


SQL> alter table test3 shrink space compact cascade;

alter table test3 shrink space compact cascade

ORA-10636: ROW MOVEMENT is not enabled

SQL> alter table test3 enable row movement;

Table altered

SQL> alter table test3 shrink space compact cascade;

Table altered

再次查看碎片的情况,发现还有一些碎片,整理碎片效果不好


SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................40
Total Bytes.............................327680
Unused Blocks...........................3
Unused Bytes............................24576
Last Used Ext FileId....................31
Last Used Ext BlockId...................481921
Last Used Block.........................5
 *************************************************
0% -- 25% free space blocks.............1
0% -- 25% free space bytes..............8192
25% -- 50% free space blocks............2
25% -- 50% free space bytes.............16384
50% -- 75% free space blocks............0
50% -- 75% free space bytes.............0
75% -- 100% free space blocks...........12
75% -- 100% free space bytes............98304
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................17
Total bytes.............................139264

PL/SQL procedure successfully completed

上面是没降低HWM,如果载降低HWM,看看效果


SQL> alter table test3 shrink space cascade;

Table altered

SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................24
Total Bytes.............................196608
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................31
Last Used Ext BlockId...................481897
Last Used Block.........................8
 *************************************************
0% -- 25% free space blocks.............1
0% -- 25% free space bytes..............8192
25% -- 50% free space blocks............2
25% -- 50% free space bytes.............16384
50% -- 75% free space blocks............0
50% -- 75% free space bytes.............0
75% -- 100% free space blocks...........0
75% -- 100% free space bytes............0
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................17
Total bytes.............................139264

PL/SQL procedure successfully completed

看来用shrink space整理碎片不彻底,再来看看move的方式

SQL> alter table test3 move;

Table altered

SQL> exec show_space_t('TEST3','auto','table','Y');

Total Blocks............................32
Total Bytes.............................262144
Unused Blocks...........................6
Unused Bytes............................49152
Last Used Ext FileId....................31
Last Used Ext BlockId...................485081
Last Used Block.........................2
 *************************************************
0% -- 25% free space blocks.............0
0% -- 25% free space bytes..............0
25% -- 50% free space blocks............0
25% -- 50% free space bytes.............0
50% -- 75% free space blocks............0
50% -- 75% free space bytes.............0
75% -- 100% free space blocks...........0
75% -- 100% free space bytes............0
Unused Blocks...........................0
Unused Bytes............................0
Total Blocks............................22
Total bytes.............................180224

PL/SQL procedure successfully completed

效果很明显,整理的很彻底

测试结论:

虽然alter table move和shrink space,都是通过物理调整rowid来整理碎片的,但shrink space整理的不彻底,他好像不是重组,而是尽可能的合并,随意会残留一些block无法整理

注意:

1.再用alter table table_name move时,表相关的索引会失效,所以之后还要执行 alter index index_name rebuild online; 最后重新编译数据库所有失效的对象

2. 在用alter table table_name shrink space cascade时,他相当于alter table table_name move和alter index index_name rebuild online. 所以只要编译数据库失效的对象就可以

alter table move和shrink space除了碎片整理的效果有时不一样外,还有什么其他的不同呢


1. Move会移动高水位,但不会释放申请的空间,是在高水位以下(below HWM)的操作。
2. shrink space 同样会移动高水位,但也会释放申请的空间,是在高水位上下(below and above HWM)都有的操作。

下面通过实验来验证


SQL> drop table test3;

Table dropped

SQL>
SQL> create table test3 as
  2     select rownum id,
  3     dbms_random.string('a', round(dbms_random.value(0,10))) col1,
  4     trunc(sysdate) - dbms_random.value(1, 365*2) col2
  5     from dual connect by rownum<=10000;

Table created

SQL> analyze table test3 compute statistics;

Table analyzed

 

SQL> col segment_name for a10;
SQL> select us.segment_name,us.extents,us.blocks from user_segments us where us.segment_name=upper('test3');

SEGMENT_NA    EXTENTS     BLOCKS
---------- ---------- ----------
TEST3               5         40

SQL> col table_name for a10;
SQL> select table_name,blocks,t.empty_blocks  from user_tables t where t.table_name=upper('test3');

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST3              37            3

SQL>


从以上查询可以看出共分了5个extents,使用了37个blocks,这37也就是test3的HWM

SQL> delete from test3 where rownum<=5000;

5000 rows deleted

SQL> commit;

Commit complete

SQL> analyze table test3 compute statistics;

Table analyzed

SQL> col segment_name for a10;
SQL> select us.segment_name,us.extents,us.blocks from user_segments us where us.segment_name=upper('test3');

SEGMENT_NA    EXTENTS     BLOCKS
---------- ---------- ----------
TEST3               5         40

SQL> col table_name for a10;
SQL>  select table_name,blocks,t.empty_blocks  from user_tables t where t.table_name=upper('test3');

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST3              37            3

SQL> select count(distinct dbms_rowid.rowid_block_number(rowid)) used_blocks from test3;

USED_BLOCKS
-----------
         17


我们从查询中可以发现test3的HWM没有变换还是37blocks,tests总共空间为40blocks。经过删除后test3实际用的块是17个


下面我们用move降低下HWM

SQL> alter table test3 move;

Table altered

SQL> col segment_name for a10;
SQL> select us.segment_name,us.extents,us.blocks from user_segments us where us.segment_name=upper('test3');

SEGMENT_NA    EXTENTS     BLOCKS
---------- ---------- ----------
TEST3               3         24

SQL> col table_name for a10;
SQL> select table_name,blocks,t.empty_blocks  from user_tables t where t.table_name=upper('test3');

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST3              37            3

user_tables里的数据没有变化,哦,原来是忘记analyze了,从这里也可以看出user_segments是oracle自动维护的。

SQL> analyze table test3 compute statistics;

Table analyzed

SQL>  select us.segment_name,us.extents,us.blocks from user_segments us where us.segment_name=upper('test3');

SEGMENT_NA    EXTENTS     BLOCKS
---------- ---------- ----------
TEST3               3         24

SQL> select table_name,blocks,t.empty_blocks  from user_tables t where t.table_name=upper('test3');

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST3              20            4

SQL>


现在再来看hwm变为20了,已经降下来了啊,空间也收缩了,从40blocks降到24blocks(注意收缩到initial指定值)。
但shrink space就收缩到存储数据的最小值,下面测试说明

创建测试表:


SQL>   create table test5 (id number) storage (initial 1m next 1m);

Table created

初始化数据


SQL>
SQL>   begin
  2     for i in 1..100000 loop
  3     insert into test5 values(i);
  4    end loop;
  5    end;
  6  /

PL/SQL procedure successfully completed

SQL> analyze table test5 compute statistics;

Table analyzed

SQL> select SEGMENT_NAME,EXTENTS,BLOCKS,INITIAL_EXTENT/1024/1024 init from user_segments where SEGMENT_NAME='TEST5';

SEGMENT_NA    EXTENTS     BLOCKS       INIT
---------- ---------- ---------- ----------
TEST5              17        256          1

SQL> select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST5';

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST5             180           76

可以从查询数据看出,test5初始化1m即128个blocks,但数据比较多,所以又按next参数要求扩展了1m空间,扩展了17个extents。
这里的test5总空间大小为256个blocks,使用空间为180blocks,HWM也是180blocks


SQL> delete from test5 where rownum<=50000;

50000 rows deleted

SQL> analyze table test5 compute statistics;

Table analyzed

SQL> select SEGMENT_NAME,EXTENTS,BLOCKS,INITIAL_EXTENT/1024/1024 init from user_segments where SEGMENT_NAME='TEST5';

SEGMENT_NA    EXTENTS     BLOCKS       INIT
---------- ---------- ---------- ----------
TEST5              17        256          1

SQL> select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST5';

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST5             180           76

整理碎片,降低HWM


SQL>  alter table test5 move;

Table altered


SQL> analyze table test5 compute statistics;

Table analyzed

SQL> select SEGMENT_NAME,EXTENTS,BLOCKS,INITIAL_EXTENT/1024/1024 init from user_segments where SEGMENT_NAME='TEST5';

SEGMENT_NA    EXTENTS     BLOCKS       INIT
---------- ---------- ---------- ----------
TEST5              16        128          1

SQL> select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST5';

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST5              85           43

从上面的查询数据可以看出HWM已经从180降低到85,test5总大小从256blocks收缩到128个blocks(initial指定大小)。


下面看看用shrink space收缩空间的情况

SQL> alter table test5 enable row movement;

Table altered


SQL> alter table test5 shrink space;

Table altered

SQL> select SEGMENT_NAME,EXTENTS,BLOCKS,INITIAL_EXTENT/1024/1024 init from user_segments where SEGMENT_NAME='TEST5';

SEGMENT_NA    EXTENTS     BLOCKS       INIT
---------- ---------- ---------- ----------
TEST5              11         88          1

SQL> select TABLE_NAME,BLOCKS,EMPTY_BLOCKS from user_tables where table_name='TEST5';

TABLE_NAME     BLOCKS EMPTY_BLOCKS
---------- ---------- ------------
TEST5              85           43

SQL>


从上面的数据可以看到test5进一步从128个blocks降低到88个blocks

结论:


shrink space收缩到数据存储的最小值,alter table move(不带参数)收缩到initial指定值,也可以用alter table test5 move storage(initial 500k)指定收缩的大小,这样可以达到shrink space效果

经过以上测试,得出的两个结论,到底用哪一个命令来整理碎片,消除行迁移呢?这就要根据实际业务需要,如果你只是收缩空间,数据增长很慢,那用shrink可以但是如果数据增长很快的话,用move就比较合适,避免再重新分配空间啊

备注:
在10g之后,整理碎片消除行迁移的新增功能shrink space

alter table <table_name> shrink space [ <null> | compact | cascade ];

compact:这个参数当系统的负载比较大时可以用,不降低HWM。如果系统负载较低时,直接用alter table table_name shrink space就一步到位了


cascade:这个参数是在shrink table的时候自动级联索引,相当于rebulid index。

普通表:

shrink必须开启行迁移功能。
alter table table_name enable row movement ;

保持HWM,相当于把块中数据打结实了
alter table table_name shrink space compact;

回缩表与降低HWM
alter table table_name shrink space;

回缩表与相关索引,降低HWM
alter table table_name shrink space cascade;

回缩索引与降低HWM
alter index index_name shrink space

虽然在10g中可以用shrink ,但也有些限制:

1). 对cluster,cluster table,或具有Long,lob类型列的对象 不起作用。
2). 不支持具有function-based indexes 或 bitmap join indexes的表
3). 不支持mapping 表或index-organized表。
4). 不支持compressed 表

------end------

更多 0

你可能感兴趣的:(数据块,存储,结构)