表空間(資料檔案shrink)收縮示例

達芬奇的夢發表於2018-04-23
正常的資料庫管理中會碰到空間不足的情況,一些表空間因為總的磁碟空間有限影響了其正常的擴充套件,但還有些表空間空餘空間很大卻不能分給別的表空間使用,這裡進行一個表空間的收縮測試。
以收縮資料檔案方式為例,吧資料檔案裡空餘的空間shrink出來給磁碟,供其他表空間擴充套件使用。

參考


#################################################################################################################
測試環境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的部分在高水位線一下則無明顯效果。
####################################################################################################################################


 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31444259/viewspace-2153198/,如需轉載,請註明出處,否則將追究法律責任。

相關文章