ORA-03297 When Resizing a Datafile by Finding the Table Highwatermark

APPLIES TO:

Oracle Database - Enterprise Edition - Version 8.1.5.0 and later
Information in this document applies to any platform.



GOAL

In a number of cases it is desired to shrink an oversized datafile. The challenge is then up to find to which size the datafile can be shrunk? Using the trial and error approach will likely give:

SQL> alter database datafile '/oradata/v112/users01.dbf' resize 117248K;
alter database datafile '/oradata/v112/users01.dbf' resize 117248K
*
ERROR at line 1:
ORA-03297: file contains used data beyond requested RESIZE value


The error is due to fact that there are allocated extents between the size to resize/shrink to and the end-of-file (might be space management blocks, so not necessary belonging to a segment).

SOLUTION

Two options do exist to find the highwater mark of a datafile:

- checking backwards for free space at end of file (proven to be the fastest)

- check highest allocated extent in datafile (it might be that space management blocks are above last allocated extend)

 

In case there are blocking segments for resizing/shrinking the datafiles the attached SQL-script: show_segment_above_size.sql will show the segments which do have extents above the desired size.

The script will show the 'blocking' segments for the resize, no block_id/location is shown due to fact that whole segments needs to be 'moved/cleaned up/...' in order to free up the extents thus enabling the desized resize.

 

1) checking backwards for free space at end of file

Due to added functionality (different blocksizes for tablespaces) the script has been split into:

- Oracle version 9 and higher

- Oracle version 8 and lower

REM Script is meant for Oracle version 9 and higher
REM -----------------------------------------------

set serveroutput on
exec dbms_output.enable(1000000);

declare

cursor c_dbfile is
select f.tablespace_name,f.file_name,f.file_id,f.blocks,t.block_size
,decode(t.allocation_type,'UNIFORM',t.initial_extent/t.block_size,0) uni_extent
,decode(t.allocation_type,'UNIFORM',(128+(t.initial_extent/t.block_size)),128) file_min_size
from dba_data_files f,
dba_tablespaces t
where f.tablespace_name = t.tablespace_name
and t.status = 'ONLINE'
order by f.tablespace_name,f.file_id;

cursor c_freespace(v_file_id in number) is
select block_id, block_id+blocks max_block
from dba_free_space
where file_id = v_file_id
order by block_id desc;

/* variables to check settings/values */
dummy number;
checkval varchar2(10);
block_correction number;

/* running variable to show (possible) end-of-file */
file_min_block number;

/* variables to check if recycle_bin is on and if extent as checked is in ... */
recycle_bin boolean:=false;
extent_in_recycle_bin boolean;

/* exception handler needed for non-existing tables note:344940.1 */
sqlstr varchar2(100);
table_does_not_exist exception;
pragma exception_init(table_does_not_exist,-942);

/* variable to spot space wastage in datafile of uniform tablespace */
space_wastage number;

begin

/* recyclebin is present in Oracle 10.2 and higher and might contain extent as checked */
begin
select value into checkval from v$parameter where name = 'recyclebin';
if checkval = 'on'
then
recycle_bin := true;
end if;
exception
when no_data_found
then
recycle_bin := false;
end;

/* main loop */
for c_file in c_dbfile
loop
/* initialization of loop variables */
dummy :=0;
extent_in_recycle_bin := false;
file_min_block := c_file.blocks;

begin

space_wastage:=0; /* reset for every file check */

<>

for c_free in c_freespace(c_file.file_id)
loop
/* if blocks is an uneven value there is a need to correct 
with -1 to compare with end-of-file which is even */
block_correction := (0-mod(c_free.max_block,2));
if file_min_block = c_free.max_block+block_correction
then

/* free extent is at end so file can be resized */
file_min_block := c_free.block_id;

/* Uniform sized tablespace check if space at end of file
is less then uniform extent size */
elsif (c_file.uni_extent !=0) and ((c_file.blocks - c_free.max_block) < c_file.uni_extent) 
then

/* uniform tablespace which has a wastage of space in datafile 
due to fact that space at end of file is smaller than uniform extent size */

space_wastage:=c_file.blocks - c_free.max_block;
file_min_block := c_free.block_id;

else
/* no more free extent at end of file, file cannot be further resized */
exit check_free;
end if;
end loop;
end;

/* check if file can be resized, minimal size of file 128 {+ initial_extent} blocks */
if (file_min_block = c_file.blocks) or (c_file.blocks <= c_file.file_min_size)
then

dbms_output.put_line('Tablespace: '||c_file.tablespace_name||' Datafile: '||c_file.file_name);
dbms_output.put_line('cannot be resized no free extents found');
dbms_output.put_line('.');

else

/* file needs minimal no of blocks which does vary over versions, 
using safe value of 128 {+ initial_extent} */
if file_min_block < c_file.file_min_size
then
file_min_block := c_file.file_min_size;
end if;


dbms_output.put_line('Tablespace: '||c_file.tablespace_name||' Datafile: '||c_file.file_name);
dbms_output.put_line('current size: '||(c_file.blocks*c_file.block_size)/1024||'K'||' can be resized to: '||round((file_min_block*c_file.block_size)/1024)||'K (reduction of: '||round(((c_file.blocks-file_min_block)/c_file.blocks)*100,2)||' %)');


/* below is only true if recyclebin is on */
if recycle_bin
then
begin
sqlstr:='select distinct 1 from recyclebin$ where file#='||c_file.file_id;
execute immediate sqlstr into dummy;

if dummy > 0
then

dbms_output.put_line('Extents found in recyclebin for above file/tablespace');
dbms_output.put_line('Implying that purge of recyclebin might be needed in order to resize');
dbms_output.put_line('SQL> purge tablespace '||c_file.tablespace_name||';');
end if;
exception
when no_data_found
then null;
when table_does_not_exist
then null;
end;
end if;
dbms_output.put_line('SQL> alter database datafile '''||c_file.file_name||''' resize '||round((file_min_block*c_file.block_size)/1024)||'K;');

if space_wastage!=0
then
dbms_output.put_line('Datafile belongs to uniform sized tablespace and is not optimally sized.');
dbms_output.put_line('Size of datafile is not a multiple of NN*uniform_extent_size + overhead');
dbms_output.put_line('Space that cannot be used (space wastage): '||round((space_wastage*c_file.block_size)/1024)||'K');
dbms_output.put_line('For optimal usage of space in file either resize OR increase to: '||round(((c_file.blocks+(c_file.uni_extent-space_wastage))*c_file.block_size)/1024)||'K');
end if;

dbms_output.put_line('.');

end if;

end loop;

end;
/


*) The absolute minimum size of a datafile varies per version/tablespace type. 
     In above case 128*8K (=1Mb) is used, but this might be too low.

Example output for Oracle version 9 and higher:

Tablespace: TEST Datafile: +DG1/test01.dbf
current size: 11264K can be resized to: 4096K (reduction of: 63.64 %)
Extents found in recyclebin for above file/tablespace
Implying that purge of recyclebin might be needed in order to resize
SQL> purge tablespace TEST;
SQL> alter database datafile '+DG1/test01.dbf' resize 4096K;
Datafile belongs to uniform sized tablespace and is not optimally sized.
Size of datafile is not a multiple of NN*uniform_extent_size + overhead
Space that cannot be used (space wastage): 1024K
For optimal usage of space in file either resize OR increase to: 13312K
.
Tablespace: TEST Datafile: +DG2/test02.dbf
current size: 10240K can be resized to: 4096K (reduction of: 60 %)
SQL> alter database datafile '+DG2/test02.dbf' resize 4096K;
.
Tablespace: UNDOTBS1 Datafile: /oradata/v1122/undotbs01.dbf
current size: 660480K can be resized to: 103424K (reduction of: 84.34 %)
SQL> alter database datafile '/oradata/v1122/undotbs01.dbf' resize 103424K;
.
Tablespace: USERS Datafile: /oradata/v1122/users01.dbf
cannot be resized no free extents found


 

REM Script is meant for Oracle version 8 and lower
REM ----------------------------------------------

set serveroutput on
exec dbms_output.enable(1000000);

declare

cursor c_dbfile is
select f.tablespace_name,f.file_name,f.file_id,f.blocks
from dba_data_files f,
dba_tablespaces t
where f.tablespace_name = t.tablespace_name
and t.status = 'ONLINE'
order by f.tablespace_name,f.file_id;

cursor c_freespace(v_file_id in number) is
select block_id, block_id+blocks max_block
from dba_free_space
where file_id = v_file_id
order by block_id desc;

/* variables to check settings/values */
block_correction number;
block_size number;

/* running variable to show (possible) end-of-file */
file_min_block number;

begin

select value into block_size from v$parameter where name='db_block_size';

/* main loop */
for c_file in c_dbfile
loop
/* initialization of loop variables */
file_min_block := c_file.blocks;

begin

<
for c_free in c_freespace(c_file.file_id)
loop
/* if blocks is an uneven value there is a need to correct with -1 to compare with end-of-file which is even */
block_correction := (0-mod(c_free.max_block,2));
if file_min_block = c_free.max_block+block_correction
then

/* free extent is at end so file can be resized */
file_min_block := c_free.block_id;

else
/* no more free extent at end of file, file cannot be further resized */
exit check_free;
end if;
end loop;
end;

/* check if file can be resized, minimal size of file 16 blocks */
if (file_min_block = c_file.blocks) or (c_file.blocks <= 16)
then

dbms_output.put_line('Tablespace: '||c_file.tablespace_name||' Datafile: '||c_file.file_name);
dbms_output.put_line('cannot be resized no free extents found');
dbms_output.put_line('.');

else

/* file needs minimal no of blocks which does vary over versions */
if file_min_block < 16
then
file_min_block := 16;
end if;

dbms_output.put_line('Tablespace: '||c_file.tablespace_name||' Datafile: '||c_file.file_name);
dbms_output.put_line('current size: '||(c_file.blocks*block_size)/1024||'K'||' can be resized to: '||round((file_min_block*block_size)/1024)||'K (reduction of: '||round(((c_file.blocks-file_min_block)/c_file.blocks)*100,2)||' %)');
dbms_output.put_line('SQL> alter database datafile '''||c_file.file_name||''' resize '||round((file_min_block*block_size)/1024)||'K;');
dbms_output.put_line('.');

end if;

end loop;

end;
/

Example output for Oracle version 8 and lower:

Tablespace: SYSTEM Datafile:
/refresh/64bit/app/oracle/oradata/REF817U6/system01.dbf
cannot be resized no free extents found
.
Tablespace: TEMP Datafile: /refresh/64bit/app/oracle/oradata/REF817U6/temp01.dbf
current size: 69632K can be resized to: 128K (reduction of: 99.82 %)
SQL> alter database datafile
'/refresh/64bit/app/oracle/oradata/REF817U6/temp01.dbf' resize 128K;


2. Check highest allocated extent in datafile (likely slow when having many extents)

column file_name format a50; 
column tablespace_name format a15; 
column highwater format 9999999999; 
set pagesize 9999 

select a.tablespace_name 
,a.file_name 
,(b.maximum+c.blocks-1)*d.db_block_size highwater 
from dba_data_files a 
,(select file_id,max(block_id) maximum 
from dba_extents 
group by file_id) b 
,dba_extents c 
,(select value db_block_size 
from v$parameter 
where name='db_block_size') d 
where a.file_id = b.file_id 
and c.file_id = b.file_id 
and c.block_id = b.maximum 
order by a.tablespace_name,a.file_name 

Output from option Number 2:

TABLESPACE_NAME FILE_NAME HIGHWATER 
--------------- -------------------------------------------------- ----------- 
BIGPARTYWEEK1 /ots1/app/oracle/product/8.1.7/dbs/bw1.dbf 49152 
IFS_LOB_N /ots3/oradata/v817/oradata/v817/ifs_lob_n.dbf 157294592 
IFS_MAIN /ots3/oradata/v817/oradata/v817/ifs_main.dbf 23027712 
OEM_REPOSITORY /ots1/oradata/v817/oradata/v817/oem_repository.dbf 56492032 
RBS /ots1/oradata/v817/oradata/v817/rbs01.dbf 230170624 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/12798004/viewspace-1990633/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/12798004/viewspace-1990633/

你可能感兴趣的:(ORA-03297 When Resizing a Datafile by Finding the Table Highwatermark)