表空间(数据文件shrink)收缩示例

正常的数据库管理中会碰到空间不足的情况,一些表空间因为总的磁盘空间有限影响了其正常的扩展,但还有些表空间空余空间很大却不能分给别的表空间使用,这里进行一个表空间的收缩测试。
以收缩数据文件方式为例,吧数据文件里空余的空间shrink出来给磁盘,供其他表空间扩展使用。

参考
http://www.oracleonlinux.cn/2011/05/25/10g_rac_shrink_tablespace/
http://www.34hap.com/database/space/how_to_shrink_datafile.html
#################################################################################################################
测试环境oracle10g2

表空间收缩测试

建立测试表空间test
分配3个数据文件,每个数据文件100M

SQL> create tablespace test datafile 'D:\oracle\product\10.2.0\rldb\rldb\test01.dbf' size 100M;
表空间已创建。
SQL> alter tablespace test add datafile 'D:\oracle\product\10.2.0\rldb\rldb\test02.dbf' size 100M;
表空间已更改。
SQL> alter tablespace test add datafile 'D:\oracle\product\10.2.0\rldb\rldb\test03.dbf' size 100M;
表空间已更改。
--------------------------------------------------------------------------------------------------------
建立测试用的表5张

SQL> create table test1 tablespace TEST as select * from dba_objects;
表已创建。
SQL> create table test2 tablespace TEST as select * from dba_objects;
表已创建。
SQL> create table test3 tablespace TEST as select * from dba_objects;
表已创建。
SQL> create table test4 tablespace TEST as select * from dba_objects;
表已创建。
SQL> create table test5 tablespace TEST as select * from dba_objects;
表已创建。

表1:

SQL> insert into test1 select * from test1;
已创建50316行。
SQL> /
已创建100632行。
SQL> /
已创建201264行。
SQL> commit;
提交完成。
-----------------------------------------------------------------------------------------------
查询test表空间用量,每个数据文件用23M左右,加起来有80M左右。

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';

   FILE_ID BYTES/1024/1024
---------- ---------------
         8         76.9375
         6         76.9375
         7         77.9375


采集该表信息
SQL> exec dbms_stats.gather_table_stats(user,'test1',estimate_percent=>100);
PL/SQL 过程已成功完成。

查询表的行数和记录长度用的块多少。
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST1';

AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     402560       5556

表2:

SQL> insert into test2 select * from test1;
已创建402560行。
SQL> insert into test2 select * from test2;
已创建452881行。
SQL> commit;
提交完成。


SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';

   FILE_ID BYTES/1024/1024
---------- ---------------
         8         48.9375
         6         41.9375
         7         42.9375

SQL> exec dbms_stats.gather_table_stats(user,'test2',estimate_percent=>100);

PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST2';

AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     905762      12665
         
表3:

SQL> insert into test3 select * from test2;
已创建905762行。
SQL> insert into test3 select * from test1;
insert into test3 select * from test1
            *
第 1 行出现错误:
ORA-01653: 表 SYS.TEST3 无法通过 1024 (在表空间 TEST 中) 扩展

表空间不够了,新增加记录失败,先提交前面插入的数据。

SQL> commit;
提交完成。

增加表空间
SQL> alter tablespace test add datafile 'D:\oracle\product\10.2.0\rldb\rldb\test04.dbf' size 100M;
表空间已更改。
SQL> insert into test3 select * from test1;
已创建402560行。
SQL> commit;

提交完成。

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='T
EST';

   FILE_ID BYTES/1024/1024
---------- ---------------
         6          6.9375
         7          7.9375
         8          4.9375
         9         67.9375

SQL> exec dbms_stats.gather_table_stats(user,'test3',estimate_percent=>100);

PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST3
';

AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93    1358644      18785
        
表4;

SQL> insert into test4 select * from test1;
已创建402560行。
SQL> commit;
提交完成。

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';

   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9         40.9375

SQL> exec dbms_stats.gather_table_stats(user,'test4',estimate_percent=>100);

PL/SQL 过程已成功完成。

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

AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     452883       6248

表5:

SQL> alter tablespace test add datafile 'D:\oracle\product\10.2.0\rldb\rldb\test05.dbf' size 100M;
表空间已更改。
SQL>  insert into test5 select * from test1;
已创建402560行。
SQL>  insert into test5 select * from test1;
已创建402560行。
SQL> commit;

提交完成。

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';

   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9          3.9375
        10          6.9375

SQL>  exec dbms_stats.gather_table_stats(user,'test5',estimate_percent=>100);

PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST5';

AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     855444      17257      
        
至此已经创立5个表和5个数据文件,我们检查一下每个数据文件上分布的数据段(表)的情况。
先查询表空间里的对象,可以看出有的表已经大于100M(单个数据文件
SQL> select owner,segment_name,segment_type,bytes/1024/1024 as Mbtys from dba_se
gments where tablespace_name='TEST';

OWNER      SEGMENT_NA SEGMENT_TYPE            MBTYS
---------- ---------- ------------------ ----------
SYS        TEST1      TABLE                      44
SYS        TEST2      TABLE                     104
SYS        TEST3      TABLE                     152
SYS        TEST4      TABLE                      50
SYS        TEST5      TABLE                     136


查看数据文件的里包含的段及其大小
        
SQL> select SEGMENT_NAME,PARTITION_NAME,sum(blocks) 
  2  from dba_extents where file_id = 6             
  3  group by SEGMENT_NAME,PARTITION_NAME           
  4  order by segment_name;                         
                                                    
SEGMENT_NA PARTITION_NAME                 SUM(BLOCKS)
---------- ------------------------------ -----------
TEST1                                            1920
TEST2                                            4736
TEST3                                            4736
TEST4                                            1024
TEST5                                             256  


SQL> select file_id,SEGMENT_NAME,PARTITION_NAME,sum(blocks)
  2  from dba_extents where file_id in (6,7,8,9,10)
  3  group by file_id,SEGMENT_NAME,PARTITION_NAME
  4  order by file_id,SEGMENT_NAME;

   FILE_ID SEGMENT_NA PARTITION_NAME                 SUM(BLOCKS)
---------- ---------- ------------------------------ -----------
         6 TEST1                                            1920
         6 TEST2                                            4736
         6 TEST3                                            4736
         6 TEST4                                            1024
         6 TEST5                                             256
         7 TEST1                                            1792
         7 TEST2                                            4736
         7 TEST3                                            4736
         7 TEST4                                            1152
         7 TEST5                                             256
         8 TEST1                                            1920

   FILE_ID SEGMENT_NA PARTITION_NAME                 SUM(BLOCKS)
---------- ---------- ------------------------------ -----------
         8 TEST2                                            3840
         8 TEST3                                            5888
         8 TEST4                                             768
         8 TEST5                                             256
         9 TEST3                                            4096
         9 TEST4                                            3456
         9 TEST5                                            4736
        10 TEST5                                           11904

已选择19行。



现在删除5张表的一部分数据,造成一些数据碎片。

表1:
SQL> delete test1 where mod(object_id,3) in (0,1);
已删除268600行。
SQL> commit;
提交完成。

SQL> select count(*) from test1;
  COUNT(*)
----------
    133960
   
SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';
   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9          3.9375
        10          6.9375

SQL> exec dbms_stats.gather_table_stats(user,'test1',estimate_percent=>100);
PL/SQL 过程已成功完成。

SQL>
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST1';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     133960       5556
        
表2:

SQL> delete test2 where mod(object_id,3) in (0,1);
已删除604350行。
SQL> commit;
提交完成。

SQL> select count(*) from test2;
  COUNT(*)
----------
    301412

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';
   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9          3.9375
        10          6.9375

SQL> exec dbms_stats.gather_table_stats(user,'test2',estimate_percent=>100);
PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST2';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     301412      12665
        
表3:
SQL> delete test3 where mod(object_id,3) in (0,1);
已删除906526行。
SQL> commit;
提交完成。

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';
   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9          3.9375
        10          6.9375


SQL> exec dbms_stats.gather_table_stats(user,'test3',estimate_percent=>100);
PL/SQL 过程已成功完成。
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST3';

AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     452118      18785


表4:
SQL> delete test4 where mod(object_id,3) in (0,1);
已删除302177行。
SQL> commit;
提交完成。

SQL>  select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';
   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9          3.9375
        10          6.9375

SQL> exec dbms_stats.gather_table_stats(user,'test4',estimate_percent=>100);
PL/SQL 过程已成功完成。
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST4';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     150706       6248

表5:

SQL> delete test5 where mod(object_id,3) in (0,1);
已删除570777行。
SQL> commit;
提交完成。

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';
   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9          3.9375
        10          6.9375

SQL> exec dbms_stats.gather_table_stats(user,'test5',estimate_percent=>100);
PL/SQL 过程已成功完成。
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST5';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     284667      17257
 
       
检查发现,虽然表删除了数据但是,总体来看表空间使用量并未减少。

SQL> select a.tablespace_name,total,free,total-free used, (total-free)/total*100
 "USED(%)" from
  2  ( select tablespace_name,sum(bytes)/1024/1024 total from dba_data_files

  3  group by tablespace_name) a,
  4  ( select tablespace_name,sum(bytes)/1024/1024 free from dba_free_space
  5  group by tablespace_name) b
  6  where a.tablespace_name=b.tablespace_name
  7  order by (total-free)/total*100 desc ;

TABLESPACE_NAME                     TOTAL       FREE       USED    USED(%)
------------------------------ ---------- ---------- ---------- ----------
SYSAUX                                240      .0625   239.9375 99.9739583
SYSTEM                                480       3.25     476.75 99.3229167
UNDOTBS1                              430     6.5625   423.4375 98.4738372
TEST                                  500    13.6875   486.3125    97.2625
USERS                                   5       1.75       3.25         65
EXAMPLE                           146.875    69.1875    77.6875  52.893617

已选择6行。

数据文件一直保持原有的使用

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';
   FILE_ID BYTES/1024/1024
---------- ---------------
         6           .9375
         7           .9375
         8           .9375
         9          3.9375
        10          6.9375
       

以下把删除数据前和数据后的数据库使用对比如下:

表1
删除前
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST1';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     402560       5556
删除后
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST1';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     133960       5556
使用块数不变,但是行数减少。

表2;
删除前:
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST2';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     905762      12665
删除后
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST2';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     301412      12665
        
表3:
删除前
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST3';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93    1358644      18785
删除后
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST3';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     452118      18785
        
表4
删除前
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST4';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     452883       6248
删除后
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST4';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     150706       6248

表5:
删除前
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST5';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     855444      17257
删除后
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST5';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     284667      17257

看得出来,删除之后数据库并没有释放。

SQL> select file_id,SEGMENT_NAME,PARTITION_NAME,sum(blocks)
  2  from dba_extents where file_id in (6,7,8,9,10)
  3  group by file_id,SEGMENT_NAME,PARTITION_NAME
  4  order by file_id,SEGMENT_NAME;
 
   FILE_ID SEGMENT_NA PARTITION_NAME                 SUM(BLOCKS)
---------- ---------- ------------------------------ -----------
         6 TEST1                                            1920
         6 TEST2                                            4736
         6 TEST3                                            4736
         6 TEST4                                            1024
         6 TEST5                                             256
         7 TEST1                                            1792
         7 TEST2                                            4736
         7 TEST3                                            4736
         7 TEST4                                            1152
         7 TEST5                                             256
         8 TEST1                                            1920

   FILE_ID SEGMENT_NA PARTITION_NAME                 SUM(BLOCKS)
---------- ---------- ------------------------------ -----------
         8 TEST2                                            3840
         8 TEST3                                            5888
         8 TEST4                                             768
         8 TEST5                                             256
         9 TEST3                                            4096
         9 TEST4                                            3456
         9 TEST5                                            4736
        10 TEST5                                           11904

已选择19行。

 
SQL> /

   FILE_ID SEGMENT_NAME         PARTI SUM(BLOCKS)
---------- -------------------- ----- -----------
         6 TEST1                             1920
         6 TEST2                             4736
         6 TEST3                             4736
         6 TEST4                             1024
         6 TEST5                              256
         7 TEST1                             1792
         7 TEST2                             4736
         7 TEST3                             4736
         7 TEST4                             1152
         7 TEST5                              256
         8 TEST1                             1920

   FILE_ID SEGMENT_NAME         PARTI SUM(BLOCKS)
---------- -------------------- ----- -----------
         8 TEST2                             3840
         8 TEST3                             5888
         8 TEST4                              768
         8 TEST5                              256
         9 TEST3                             4096
         9 TEST4                             3456
         9 TEST5                             4736
        10 TEST5                            11904

已选择19行。

现在查询可收回的空间有多少:

SQL> select FILE_ID,BLOCK_ID,shrink_mb
  2  from (select FILE_ID,BLOCK_ID,BYTES/1024/1024 shrink_mb,
  3  row_number() over(partition by file_id order by BLOCK_ID desc) rn
  4  from dba_free_space where file_id in (6,7,8,9,10) ) t
  5  where rn =1 ;

   FILE_ID   BLOCK_ID  SHRINK_MB
---------- ---------- ----------
         6      12681      .9375
         7      12681      .9375
         8      12681      .9375
         9      12297     3.9375
        10      11913     6.9375

和剩余的数据文件空间一样,虽然删除了但是并没有可以收回的空间,因为占用的数据块并没有释放。

查询每个数据文件空闲块和起始位置
 
SQL> select FILE_ID,BLOCK_ID start_block,BLOCK_ID+BLOCKS-1 end_block,BLOCKS
  2  from dba_free_space
  3  where file_id in (6,7,8,9,10)
  4  order by 1;
 
   FILE_ID START_BLOCK  END_BLOCK     BLOCKS
---------- ----------- ---------- ----------
         6       12681      12800        120
         7       12681      12800        120
         8       12681      12800        120
         9       12297      12800        504
        10       11913      12800        888


先对数据段(表)进行整理,收缩这些表。
表1:
SQL> alter table test1 enable row movement;
表已更改。
SQL> alter table test1 shrink space;
表已更改。

SQL> exec dbms_stats.gather_table_stats(user,'TEST1',estimate_percent=>100);
PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST1';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     133960       1652      
        
对比以前数据快减少了,空间收缩了。
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST1';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     133960       5556
      
表2:

SQL> alter table test2 enable row movement;
表已更改。
SQL> alter table test2 shrink space;
表已更改。

SQL> exec dbms_stats.gather_table_stats(user,'TEST2',estimate_percent=>100);
PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST2';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     301412       3711
        
对比之前数据快减少了,空间收缩了。               
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST2';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     301412      12665
        
表3:
SQL> alter table test3 enable row movement;
表已更改。
SQL> alter table test3 shrink space;
表已更改。

SQL> exec dbms_stats.gather_table_stats(user,'TEST3',estimate_percent=>100);
PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST3';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     452118       5565
        
对比之前数据快减少了,空间收缩了。     
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST3';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     452118      18785
        
表4:
SQL> alter table test4 enable row movement;
表已更改。

SQL> alter table test4 shrink space;
表已更改。

SQL> exec dbms_stats.gather_table_stats(user,'TEST4',estimate_percent=>100);
PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST4';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     150706       1858

对比之前数据快减少了,空间收缩了。        
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST4';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     150706       6248
        
表5:
SQL> alter table test5 enable row movement;
表已更改。
SQL> alter table test5 shrink space;
表已更改。

SQL> exec dbms_stats.gather_table_stats(user,'TEST5',estimate_percent=>100);
PL/SQL 过程已成功完成。

SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST5';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     284667       3505
        
对比之前数据快减少了,空间收缩了
SQL> select avg_row_len,num_rows,blocks from user_tables where table_name='TEST5';
AVG_ROW_LEN   NUM_ROWS     BLOCKS
----------- ---------- ----------
         93     284667      17257
        
5张表都收缩完了,
以下是每个数据文件数据库空闲和起始位置
SQL> select FILE_ID,BLOCK_ID start_block,BLOCK_ID+BLOCKS-1 end_block,BLOCKS
  2  from dba_free_space
  3  where file_id=6;

   FILE_ID START_BLOCK  END_BLOCK     BLOCKS
---------- ----------- ---------- ----------
         6        1569       2952       1384
         6        3977       7432       3456
         6        9097      11912       2816
         6       12169      12800        632

SQL> select FILE_ID,BLOCK_ID start_block,BLOCK_ID+BLOCKS-1 end_block,BLOCKS
  2  from dba_free_space
  3  where file_id=7;

   FILE_ID START_BLOCK  END_BLOCK     BLOCKS
---------- ----------- ---------- ----------
         7        1545       2824       1280
         7        3793       7304       3512
         7        8873      11784       2912
         7       12041      12800        760

SQL> select FILE_ID,BLOCK_ID start_block,BLOCK_ID+BLOCKS-1 end_block,BLOCKS
  2  from dba_free_space
  3  where file_id=8;

   FILE_ID START_BLOCK  END_BLOCK     BLOCKS
---------- ----------- ---------- ----------
         8        1673       2952       1280
         8        3977       6536       2560
         8        8201      12168       3968
         8       12529      12800        272

SQL> select FILE_ID,BLOCK_ID start_block,BLOCK_ID+BLOCKS-1 end_block,BLOCKS
  2  from dba_free_space
  3  where file_id=9;

   FILE_ID START_BLOCK  END_BLOCK     BLOCKS
---------- ----------- ---------- ----------
         9           9       4104       4096
         9        4361       7560       3200
         9        8969      12800       3832

SQL> select FILE_ID,BLOCK_ID start_block,BLOCK_ID+BLOCKS-1 end_block,BLOCKS
  2  from dba_free_space
  3  where file_id=10;

   FILE_ID START_BLOCK  END_BLOCK     BLOCKS
---------- ----------- ---------- ----------
        10        1409      12800      11392


可以看得出来空闲数据块都不连续。


我们看一下表空间的数据文件的空余使用量。 

SQL> select file_id,bytes/1024/1024 from dba_free_space where tablespace_name='TEST';

   FILE_ID BYTES/1024/1024
---------- ---------------
         6         10.8125
         6              27
         6              22
         6          4.9375
         7              10
         7         27.4375
         7           22.75
         7          5.9375
         8              10
         8              20
         8              31

   FILE_ID BYTES/1024/1024
---------- ---------------
         8           2.125
         9              32
         9              25
         9         29.9375
        10              89

已选择16行。

可以看出空余的量很多了,但是都是分散的。

查看每个数据文件里表占用的数据块   
SQL> select file_id,SEGMENT_NAME,PARTITION_NAME,sum(blocks)              
  2  from dba_extents where file_id in (6,7,8,9,10)                  
  3  group by file_id,SEGMENT_NAME,PARTITION_NAME                    
  4  order by file_id,SEGMENT_NAME;                                  
                                                                     
   FILE_ID SEGMENT_NAME         PARTI SUM(BLOCKS)                    
---------- -------------------- ----- -----------                    
         6 TEST1                              536                    
         6 TEST2                             1280                    
         6 TEST3                             1920                    
         6 TEST4                              512                    
         6 TEST5                              256                    
         7 TEST1                              512                    
         7 TEST2                             1224                    
         7 TEST3                             1824                    
         7 TEST4                              512                    
         7 TEST5                              256                    
         8 TEST1                              640                    
                                                                     
   FILE_ID SEGMENT_NAME         PARTI SUM(BLOCKS)                    
---------- -------------------- ----- -----------                    
         8 TEST2                             1280                    
         8 TEST3                             1920                    
         8 TEST4                              616                    
         8 TEST5                              256                    
         9 TEST4                              256                    
         9 TEST5                             1408                    
        10 TEST5                             1400                    
                                                                     
已选择18行。                                                         


计算查看每个数据文件可以收回的空间大小                                                                     
SQL>                                                                 
SQL> select FILE_ID,BLOCK_ID,shrink_mb                               
  2  from (select FILE_ID,BLOCK_ID,BYTES/1024/1024 shrink_mb,        
  3  row_number() over(partition by file_id order by BLOCK_ID desc) rn
  4  from dba_free_space where file_id in (6,7,8,9,10) ) t           
  5  where rn =1 ;                                                   
                                                                     
   FILE_ID   BLOCK_ID  SHRINK_MB                                     
---------- ---------- ----------                                     
         6      12169     4.9375                                     
         7      12041     5.9375                                     
         8      12529      2.125                                     
         9       8969    29.9375                                     
        10       1409         89                                     
 
除了文件10外其他收回的量都比较小,这个应该是因为数据文件高水位线的原因,数据文件只能收缩最高水位线以上的数据块,在文件最高水位线以下各个分布的空闲块无法收回。

问题来了,怎么能让数据文件里高水位线以下的很多空闲数据库合在一起呢?让他们连续呢?

网上看到的一个计算生成收缩数据文件的查询。                                                                  

SQL> select a.file#,a.name,a.bytes/1024/1024 CurrentMB,
  2         ceil(HWM * a.block_size)/1024/1024 ResizeTo,
  3         (a.bytes - HWM * a.block_size)/1024/1024 ReleaseMB,
  4         'alter database datafile '''||a.name||''' resize '||
  5         ceil(HWM * a.block_size/1024/1024) || 'M;' ResizeCMD
  6  from v$datafile a,
  7       (select file_id,max(block_id+blocks-1) HWM
  8         from dba_extents
  9         group by file_id) b
 10  where a.file# = b.file_id(+)
 11  and (a.bytes - HWM *block_size)>0
 12  order by 5 ;

FILE# NAME                                                CURRENTMB   RESIZETO RELEASEMB RESIZECMD
-----------------------------------------------------------------------------------------------------------------------------------------------
    4 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\USERS01.DBF              5       3.251.75    alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\USERS01.DBF' resize 4M;
    8 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST03.DBF             100     97.8752.125   alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST03.DBF' resize 98M;
    1 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\SYSTEM01.DBF           480   477.06252.9375  alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\SYSTEM01.DBF' resize 478M;
    6 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST01.DBF             100    95.06254.9375  alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST01.DBF' resize 96M;
    7 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST02.DBF             100    94.06255.9375  alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST02.DBF' resize 95M;
    3 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\SYSAUX01.DBF           250   240.43759.5625  alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\SYSAUX01.DBF' resize 241M;
    9 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST04.DBF             100    70.062529.9375 alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST04.DBF' resize 71M;
    5 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\EXAMPLE01.DBF      146.875    80.187566.6875 alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\EXAMPLE01.DBF' resize 81M;
   10 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST05.DBF             100              1189 alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST05.DBF' resize 11M;

已选择9行。



如果只是想对某个表个间的datafile resize,可采用:

SQL> select a.file#,a.name,a.bytes/1024/1024 CurrentMB,                        
  2         ceil(HWM * a.block_size)/1024/1024 ResizeTo,                       
  3         (a.bytes - HWM * a.block_size)/1024/1024 ReleaseMB,                
  4         'alter database datafile '''||a.name||''' resize '||               
  5         ceil(HWM * a.block_size/1024/1024) || 'M;' ResizeCMD               
  6  from v$datafile a,                                                        
  7       (select file_id,max(block_id+blocks-1) HWM                           
  8         from dba_extents where file_id in                                  
  9                (select b.file#  From v$tablespace a ,v$datafile b          
 10                  where a.ts#=b.ts# and a.name='TEST')                      
 11         group by file_id) b                                                
 12  where a.file# = b.file_id(+)                                              
 13  and (a.bytes - HWM *block_size)>0                                         
 14  order by 5                                                                
 15  ;                                                                         
                                                                               
FILE# NAME                                                CURRENTMB   RESIZETO         RELEASEMB RESIZECMD                                                            
    8 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST03.DBF             100     97.8752.125    alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST03.DBF' resize 98M;                                                                  
    6 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST01.DBF             100    95.06254.9375   alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST01.DBF' resize 96M;                                                                  
    7 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST02.DBF             100    94.06255.9375   alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST02.DBF' resize 95M;                                                                  
    9 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST04.DBF             100    70.062529.9375  alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST04.DBF' resize 71M;                                                                  
   10 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST05.DBF             100             1189   alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST05.DBF' resize 11M;        


根据以上生产的执行语句执行对数据文件的收缩操作

SQL> alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST03.DBF' resize 98M;
数据库已更改。

SQL> alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST01.DBF' resize 96M;
数据库已更改。

SQL> alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST02.DBF' resize 95M;
数据库已更改。

SQL> alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST04.DBF' resize 71M;
数据库已更改。

SQL> alter database datafile 'D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST05.DBF' resize 11M;
数据库已更改。

收缩成功无报错

查看数据文件大小
SQL> select file_id,file_name,bytes/1024/1024 from dba_data_files where tablespace_name='TEST';

   FILE_ID FILE_NAME                                          BYTES/1024/1024
---------- -------------------------------------------------- ---------------
         6 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST01.DBF                   96
         7 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST02.DBF                   95
         8 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST03.DBF                   98
         9 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST04.DBF                   71
        10 D:\ORACLE\PRODUCT\10.2.0\RLDB\RLDB\TEST05.DBF                   11

数据文件成功收缩至目标大小。

查看数据正常
SQL> select count(*) from test1;

  COUNT(*)
----------
    133960

SQL> select count(*) from test2;

  COUNT(*)
----------
    301412

SQL> select count(*) from test3;

  COUNT(*)
----------
    452118

SQL> select count(*) from test4;

  COUNT(*)
----------
    150706

SQL> select count(*) from test5;

  COUNT(*)
----------
    284667


至此测试完成,数据文件收缩完毕。
结论,数据文件收缩大大小取决于高水位线(HWM)的位置,前期对数据表文件里所在表的shrink有一定的作用,但是如果表shrink的部分在高水位线一下则无明显效果。
####################################################################################################################################

猜你喜欢

转载自blog.csdn.net/demonson/article/details/80505836