I’ve just discovered that the space management bitmaps for the tablespace I normally use in my 21c tests are broken. In a tablespace that’s supposed to be completely empty a query of dba_free_space shows 4 gaps totalling several thousand blocks:
SQL> select * from dba_free_space where tablespace_name = 'TEST_8K_ASSM';
TABLESPACE_NAME FILE_ID BLOCK_ID BYTES BLOCKS RELATIVE_FNO
------------------------------ ---------- ---------- ---------- ---------- ------------
TEST_8K_ASSM 13 128 327680 40 13
TEST_8K_ASSM 13 216 1376256 168 13
TEST_8K_ASSM 13 1792 720896 88 13
TEST_8K_ASSM 13 1896 196608 24 13
TEST_8K_ASSM 13 9600 969932800 118400 13
Of course I ran my script to drop all segments and purge the recyclebin when I first saw this, but that didn’t help, and a query against dba_segments showed no segments, and a query against seg$ showed nothing in the file. So somehow the bits are bust.
Fortunately there’s a dbms_space_admin package with a procedure tablespace_verify() that I’ve wanted to test for some time – the documentation is a little sparse about how it works. So here’s a cut-and-paste of my first (and second) call to the procedure, executing from the SYS schema and passing in the tablespace name:
SQL> execute dbms_space_admin.tablespace_verify ('TEST_8K_ASSM')
BEGIN dbms_space_admin.tablespace_verify ('TEST_8K_ASSM'); END;
*
ERROR at line 1:
ORA-20000: BitMap entry partially used with no Extent Map entry
TSN 6: Range RelFno 13: ExtNo: 32702 BeginBlock: 0 EndBlock: 4194303
ORA-06512: at "SYS.DBMS_SPACE_ADMIN", line 83
ORA-06512: at line 1
SQL> execute dbms_space_admin.tablespace_verify ('TEST_8K_ASSM')
BEGIN dbms_space_admin.tablespace_verify ('TEST_8K_ASSM'); END;
*
ERROR at line 1:
ORA-20000: BitMap entry partially used with no Extent Map entry
TSN 6: Range RelFno 13: ExtNo: 32766 BeginBlock: 0 EndBlock: 4194303
ORA-06512: at "SYS.DBMS_SPACE_ADMIN", line 83
ORA-06512: at line 1
The output isn’t promising – but we can, at least, see that it’s the right RelFno, and the Extno: seems to have moved on by 64 (which is a nice number in an abstract, computational way), but what might the Extno: be? And I know that I’ve only got 128,000 blocks in the file and it’s not set to auto-extend so that EndBlock: value is a little worrying.
Just to add a little more confusion – the next few calls reported the ExtNo: as 0, then stuck at 32,766. So it probably wasn’t walking the files bitmap blocks as I first guessed.
What to do next? In my case I could throw the tablespace away – there was nothing in it, and even if there were I could have recreated it very easily – so I was happy to try the next dbms_space_admin feature: tablespace_fix_bitmaps(). Here’s the declaration:
procedure tablespace_fix_bitmaps(
tablespace_name in varchar2 ,
dbarange_relative_file in positive ,
dbarange_begin_block in positive ,
dbarange_end_block in positive ,
fix_option in positive
);
--
-- Marks the appropriate dba range (extent) as free/used in bitmap
-- Input arguments:
-- tablespace_name - name of tablespace
-- dbarange_relative_file - relative fileno of dba range (extent)
-- dbarange_begin_block - block number of beginning of extent
-- dbarange_end_block - block number (inclusive) of end of extent
-- fix_option - TABLESPACE_EXTENT_MAKE_FREE or
-- TABLESPACE_EXTENT_MAKE_USED
Again the documentation is a little sparse, so I’m just going to cross my fingers and hope for the best – proceeding a little cautiously. Looking at the report of free space I can infer from the first two lines that the bits for blocks 168 (128 + 40) to 215 (216 – 1) are marked as used. So I’ll try to pass that information into the procedure call:
set serveroutput on
set linesize 132
set trimspool on
set tab off
begin
dbms_space_admin.tablespace_fix_bitmaps(
tablespace_name => 'TEST_8K_ASSM',
dbarange_relative_file => 13,
dbarange_begin_block => 168,
dbarange_end_block => 215,
fix_option => dbms_space_admin.TABLESPACE_EXTENT_MAKE_FREE
);
end;
/
PL/SQL procedure successfully completed.
SQL> select * from dba_free_space where tablespace_name = 'TEST_8K_ASSM';
TABLESPACE_NAME FILE_ID BLOCK_ID BYTES BLOCKS RELATIVE_FNO
------------------------------ ---------- ---------- ---------- ---------- ------------
TEST_8K_ASSM 13 128 2097152 256 13
TEST_8K_ASSM 13 1792 720896 88 13
TEST_8K_ASSM 13 1896 196608 24 13
TEST_8K_ASSM 13 9600 969932800 118400 13
Comparing the new results from dba_free_space we can see that we’ve eliminated the “used” chunk that was between the first two free chunks and now have a single free chunk stretching from block 128 to block 383. So now we rinse and repeat – and we could use dba_free_space to help by generating a list of begin and end blocks – we might even consider writing a query to drive a cursor loop (being very careful to allow for multi-file tablespaces, which I haven’t done):
select
relative_fno, block_id, block_id + blocks begin_block,
lead(block_id) over (order by relative_fno, block_id) - 1 end_block
from
dba_free_space
where
tablespace_name = 'TEST_8K_ASSM'
order by
relative_fno, block_id
/
RELATIVE_NO BLOCK_ID BEGIN_BLOCK END_BLOCK
------------ ---------- ----------- ----------
13 128 384 1791
13 1792 1880 1895
13 1896 1920 9599
13 9600 128000
After three more calls to tablespace_fix_bitmaps() this is the result I got from my query against dba_free_space – followed by a call to tablespace_verify():
TABLESPACE_NAME FILE_ID BLOCK_ID BYTES BLOCKS RELATIVE_FNO
------------------------------ ---------- ---------- ---------- ---------- ------------
TEST_8K_ASSM 13 128 1047527424 127872 13
SQL> execute dbms_space_admin.tablespace_verify ('TEST_8K_ASSM')
PL/SQL procedure successfully completed.
Summary
After finding a tablespace that should have shown nothing but free space along its whole length (and checking the recyclebin, and the underlying seg$ table) I called dbms_space_admin.tablespace_verify() to see what it thought was going on and it reported an inconsistency between the tablespace (file) bitmap and segment bitmaps (in this case because there were no segment bitmaps when the file bitmap said there ought to be).
Starting from a query against dba_free_space I worked out the ranges of blocks that were marked in the file bitmap as used when they shouldn’t have been, and called dbms_space_admin.tablespace_fix_bitmaps() for each range.
After fixing all the bad ranges I called tablespace_verify() again to see if it had any more complaints,, and got an empty report.
Footnotes
The documentation is not user-friendly, and it would be nice to have some comments in the manaul (or dbmsspc.sql script) describing possible outputs. On the other hand I managed to avoid reading the documentation carefully enough anyway, because it wasn’t until I started searching MOS for better documentation that I realised I should have used the ASSM version of verify
execute dbms_space_admin.assm_tablespace_verify ('TEST_8K_ASSM', dbms_space_admin.ts_verify_bitmaps)
This procedure might have reported sensible information for the Extno, BeginBlock and EndBlock. But it was too late to find out – I’ll just wait for the next corruption to happen.
There is one circumstance where you might see multiple chunks in dba_free_space when there are no segments allocated, but with no gaps between chunks – if Oracle has to “grow” the bitmap for a file then the separate chunks of the bitmap report their freespace separately.
Another possibility for multiple free space chunks when there are no (ordinary) segments is if you’ve moved the tablespace bitmap or converted a dictionary managed tablespace to a locally managed tablespace – again a rare occurrence – in which case the tablespace bitmap will be in a “nearly-hidden” segment.
[…] Lost Space (Nov 2022): How dbms_space_admin fixed my bitmaps when dba_free_space “lost” some free space. […]
Pingback by Troubleshooting catalogue | Oracle Scratchpad — November 16, 2022 @ 1:00 pm GMT Nov 16,2022 |
[…] Lost Space (Nov 2022): How dbms_space_admin fixed my bitmaps when dba_free_space “lost” some free space. […]
Pingback by Infrastructure Catalogue | Oracle Scratchpad — November 16, 2022 @ 1:01 pm GMT Nov 16,2022 |