最近导一个空库到数据库后,发现占用的表空间非常大,执行表收缩(SHRINK SPACE CASCADE)后,发现实际占用的空间不到1%。
但是收缩表空间大小提示错误
    ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 5000M;
    提示:ORA-03297: file contains used data beyond requested RESIZE value

   原因1:压缩表空间大小,最小必须是该表空间目前最大块段的大小
   原因2:表的初始大小分配的太大

   一、征对原因1,可以采用移动表空间的方法来减小block_id
--找出目前GCOMM2表空间最大的block_id
SELECT MAX(block_id)
  FROM dba_extents
 WHERE tablespace_name = 'GCOMM2';
MAX(BLOCK_ID)
-------------
       994816

--计算目前最大块的段所占用的空间(该数据库的block大小是8092)
SQL> show parameter db_block_size ;
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_block_size                        integer     8192
 
SELECT 994816*8192/1024/1024 FROM dual;
994816*8192/1024/1024
---------------------
                 7772
因此目前该表空间只能收缩到7772M左右
ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 7772M;

--通过移动移动表空间来收缩表空间大小步骤
1.创建表空间用于将较大block_id的表移到此表空间
CREATE TABLESPACE "gcomm_bk" DATAFILE 'D:\ora_tablespace\gcomm_bk.dbf' SIZE 2048M
LOGGING EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;
2.找出需要移动表空间的表并将这些表移到刚创建的表空间

--收缩表

SELECT DISTINCT 'alter table ' || segment_name ||
                ' SHRINK SPACE CASCADE; '
  FROM dba_extents
 WHERE tablespace_name = 'GCOMM2'
   AND segment_type = 'TABLE'
   AND file_id = 11
   AND block_id > 30336;
 --移动表空间

SELECT DISTINCT 'alter table ' || segment_name ||
                ' move tablespace "gcomm_bk"; '
  FROM dba_extents
 WHERE tablespace_name = 'GCOMM2'
   AND file_id = 11
   AND block_id > 30336/*872704*/
   AND segment_type = 'TABLE';
移动表空间过程中出现了这个错误:ORA-01658: 无法为表空间 gcomm_bk 中的段创建 INITIAL 区
原因:表空间不够。
解决:将该表空间设为自动扩展或者直接扩大该表空间
注:如果是smallfile 表空间无法设为自动扩展,则必须手动将这些表空间先加大
ALTER DATABASE DATAFILE 'D:\ora_tablespace\gcomm_bk.dbf' RESIZE 7000M;

3.重建该空间的索引
SELECT DISTINCT 'alter INDEX ' || segment_name ||' REBUILD TABLESPACE "gcomm_bk"; '  FROM dba_extents WHERE tablespace_name = 'GCOMM2' AND segment_type = 'INDEX';

4.再次查看此时gcomm2表空间最大块的段
SELECT MAX(block_id)
  FROM dba_extents
 WHERE tablespace_name = 'GCOMM2';

SELECT 30336*8192/1024/1024 FROM dual;
5.收缩表空间(执行成功)
ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 500M;

6.移动表空间下的对象
允许表移动
SELECT DISTINCT 'alter table '||owner||',' || segment_name || ' enable row movement; '  FROM dba_extents WHERE tablespace_name = 'gcomm_bk'   AND segment_type = 'TABLE';
释放存贮--针对delte
SELECT DISTINCT 'alter table '||owner||',' || segment_name || ' shrink space cascade; '  FROM dba_extents WHERE tablespace_name = 'gcomm_bk'   AND segment_type = 'TABLE';
移动表到新的表空间
SELECT DISTINCT 'alter table '||owner||',' || segment_name || ' move tablespace GCOMM2; '  FROM dba_extents WHERE tablespace_name = 'gcomm_bk'   AND segment_type = 'TABLE';
重建索引到新的表空间
SELECT DISTINCT 'alter INDEX  '||owner||',' || segment_name ||' REBUILD TABLESPACE GCOMM2; '  FROM dba_extents WHERE tablespace_name = '"gcomm_bk"'  AND segment_type = 'INDEX';
移动大对象到新的表空间
select 'alter table '||owner||','||table_name||' move lob('||colmn_name||') store as (tablespace GCOMM2)' from dba_lobs  
where segment_name in(select segment_name
                         from dba_extents
                         where tablespace_name ='gcomm_bk' and  segment_type like 'LOB%')

二、征对原因2可通过减小表或索引的初始化大小来收缩表空间
--征对块大的表 减小其初始分配大小
SELECT DISTINCT 'alter table ' || segment_name ||
                ' MOVE STORAGE(INITIAL 1M) ;'
  FROM dba_extents
 WHERE tablespace_name = 'GCOMM2'
   AND segment_type = 'TABLE'
   AND file_id = 11
   AND block_id > 30336;

--征对块大的索引减小其初始分配大小
SELECT DISTINCT 'alter index ' || segment_name ||
                ' REBUILD  tablespace GCOMM2 storage(initial 64K next 1M pctincrease 0);'
  FROM dba_extents
 WHERE tablespace_name = 'GCOMM2'
   AND segment_type = 'INDEX'
   AND file_id = 11
   AND block_id > 30336;

然后再收缩表空间
ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 1000M;
执行成功,原先为7G多。

 最近导一个空库到数据库后,发现占用的表空间非常大,执行表收缩(SHRINK SPACE CASCADE)后,发现实际占用的空间不到1%。

但是收缩表空间大小提示错误


    ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 5000M;


    提示:ORA-03297: file contains used data beyond requested RESIZE value



   原因1:压缩表空间大小,最小必须是该表空间目前最大块段的大小


   原因2:表的初始大小分配的太大



   一、征对原因1,可以采用移动表空间的方法来减小block_id


--找出目前GCOMM2表空间最大的block_id


SELECT MAX(block_id)


  FROM dba_extents


 WHERE tablespace_name = 'GCOMM2';


MAX(BLOCK_ID)


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


       994816



--计算目前最大块的段所占用的空间(该数据库的block大小是8092)


SQL> show parameter db_block_size ;


 


NAME                                 TYPE        VALUE


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


db_block_size                        integer     8192


 


SELECT 994816*8192/1024/1024 FROM dual;


994816*8192/1024/1024


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


                 7772


因此目前该表空间只能收缩到7772M左右


ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 7772M;



--通过移动移动表空间来收缩表空间大小步骤


1.创建表空间用于将较大block_id的表移到此表空间


CREATE TABLESPACE "gcomm_bk" DATAFILE 'D:\ora_tablespace\gcomm_bk.dbf' SIZE 2048M


LOGGING EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;


2.找出需要移动表空间的表并将这些表移到刚创建的表空间



--收缩表



SELECT DISTINCT 'alter table ' || segment_name ||


                ' SHRINK SPACE CASCADE; '


  FROM dba_extents


 WHERE tablespace_name = 'GCOMM2'


   AND segment_type = 'TABLE'


   AND file_id = 11


   AND block_id > 30336;


 --移动表空间



SELECT DISTINCT 'alter table ' || segment_name ||


                ' move tablespace "gcomm_bk"; '


  FROM dba_extents


 WHERE tablespace_name = 'GCOMM2'


   AND file_id = 11


   AND block_id > 30336/*872704*/


   AND segment_type = 'TABLE';


移动表空间过程中出现了这个错误:ORA-01658: 无法为表空间 gcomm_bk 中的段创建 INITIAL 区


原因:表空间不够。


解决:将该表空间设为自动扩展或者直接扩大该表空间


注:如果是smallfile 表空间无法设为自动扩展,则必须手动将这些表空间先加大


ALTER DATABASE DATAFILE 'D:\ora_tablespace\gcomm_bk.dbf' RESIZE 7000M;



3.重建该空间的索引


SELECT DISTINCT 'alter INDEX ' || segment_name ||' REBUILD TABLESPACE "gcomm_bk"; '  FROM dba_extents WHERE tablespace_name = 'GCOMM2' AND segment_type = 'INDEX';



4.再次查看此时gcomm2表空间最大块的段


SELECT MAX(block_id)


  FROM dba_extents


 WHERE tablespace_name = 'GCOMM2';



SELECT 30336*8192/1024/1024 FROM dual;


5.收缩表空间(执行成功)


ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 500M;



6.移动表空间下的对象


允许表移动


SELECT DISTINCT 'alter table '||owner||',' || segment_name || ' enable row movement; '  FROM dba_extents WHERE tablespace_name = 'gcomm_bk'   AND segment_type = 'TABLE';


释放存贮--针对delte


SELECT DISTINCT 'alter table '||owner||',' || segment_name || ' shrink space cascade; '  FROM dba_extents WHERE tablespace_name = 'gcomm_bk'   AND segment_type = 'TABLE';


移动表到新的表空间


SELECT DISTINCT 'alter table '||owner||',' || segment_name || ' move tablespace GCOMM2; '  FROM dba_extents WHERE tablespace_name = 'gcomm_bk'   AND segment_type = 'TABLE';


重建索引到新的表空间


SELECT DISTINCT 'alter INDEX  '||owner||',' || segment_name ||' REBUILD TABLESPACE GCOMM2; '  FROM dba_extents WHERE tablespace_name = '"gcomm_bk"'  AND segment_type = 'INDEX';


移动大对象到新的表空间


select 'alter table '||owner||','||table_name||' move lob('||colmn_name||') store as (tablespace GCOMM2)' from dba_lobs  


where segment_name in(select segment_name


                         from dba_extents


                         where tablespace_name ='gcomm_bk' and  segment_type like 'LOB%')



二、征对原因2可通过减小表或索引的初始化大小来收缩表空间


--征对块大的表 减小其初始分配大小


SELECT DISTINCT 'alter table ' || segment_name ||


                ' MOVE STORAGE(INITIAL 1M) ;'


  FROM dba_extents


 WHERE tablespace_name = 'GCOMM2'


   AND segment_type = 'TABLE'


   AND file_id = 11


   AND block_id > 30336;



--征对块大的索引减小其初始分配大小


SELECT DISTINCT 'alter index ' || segment_name ||


                ' REBUILD  tablespace GCOMM2 storage(initial 64K next 1M pctincrease 0);'


  FROM dba_extents


 WHERE tablespace_name = 'GCOMM2'


   AND segment_type = 'INDEX'


   AND file_id = 11


   AND block_id > 30336;



然后再收缩表空间


ALTER DATABASE DATAFILE 'D:\ora_tablespace\GCOMM2.dbf' RESIZE 1000M;


执行成功,原先为7G多。