Some Thoughts on the Problems of ORACLE Locating Database Objects by file_id and block_id

Keywords: Oracle SQL Database

In ORACLE, we can locate a database object through file_id (file#) and block_id (block#). For example, if we have file#=4 block#=266 blocks=8 in the trace file generated in 10046, then I can locate the object through the following two SQL s

 

SQL 1: This SQL is inefficient and takes a long time to execute.

 

SELECT OWNER, 
       SEGMENT_NAME, 
       SEGMENT_TYPE, 
       TABLESPACE_NAME 
FROM   DBA_EXTENTS 
WHERE  FILE_ID =&FILE_ID
       AND &BLOCK_ID BETWEEN BLOCK_ID AND BLOCK_ID + BLOCKS - 1;

 

SQL 2: This SQL is more efficient (there is no CACHEHINT field in ORACLE 10g)

SELECT OBJD, 
       FILE#, 
       BLOCK#, 
       CLASS#, 
       TS#, 
       CACHEHINT, 
       STATUS, 
       DIRTY 
FROM   V$BH 
WHERE  FILE# = &FILE_ID 
       AND BLOCK# = &BLOCK_ID; 
 
 
SELECT OWNER, OBJECT_NAME FROM DBA_OBJECTS WHERE OBJECT_ID=&OBJECT_ID;

Here's an example to illustrate it. The details are as follows

SQL> COL OWNER FOR A12;
SQL> COL SEGMENT_NAME FOR A32;
SQL> SELECT OWNER       ,
  2         SEGMENT_NAME ,
  3         HEADER_FILE  ,
  4         HEADER_BLOCK
  5  FROM DBA_SEGMENTS          
  6  WHERE OWNER='TEST' AND SEGMENT_NAME='EMPLOYEE';
 
OWNER        SEGMENT_NAME                     HEADER_FILE HEADER_BLOCK
------------ -------------------------------- ----------- ------------
TEST         EMPLOYEE                                   4          266
 
SQL> 
SQL> SELECT OWNER, 
  2         SEGMENT_NAME, 
  3         SEGMENT_TYPE, 
  4         TABLESPACE_NAME 
  5  FROM   DBA_EXTENTS 
  6  WHERE  FILE_ID = 4 
  7         AND 266 BETWEEN BLOCK_ID AND BLOCK_ID + BLOCKS - 1;
 
OWNER        SEGMENT_NAME                     SEGMENT_TYPE       TABLESPACE_NAME
------------ -------------------------------- ------------------ -----------------
TEST         EMPLOYEE                         TABLE              USERS
 
SQL> 
SQL> SELECT OBJD, 
  2         FILE#, 
  3         BLOCK#, 
  4         CLASS#, 
  5         TS#, 
  6         CACHEHINT, 
  7         STATUS, 
  8         DIRTY 
  9  FROM   V$BH 
 10  WHERE  FILE# = 4 
 11         AND BLOCK# = 266; 
 
      OBJD      FILE#     BLOCK#     CLASS#        TS#  CACHEHINT STATUS     D
---------- ---------- ---------- ---------- ---------- ---------- ---------- -
     76090          4        266          4          4         15 cr         N
     76090          4        266          4          4         15 cr         N
     76090          4        266          4          4         15 cr         N
 
SQL> SELECT OWNER, OBJECT_NAME FROM DBA_OBJECTS WHERE OBJECT_ID=76090;
 
OWNER        OBJECT_NAME
------------ ------------------------------------------------------------
TEST         EMPLOYEE

 

Yesterday, when I was discussing a question about free blocks in the group, when I validated the test, I found a strange phenomenon, using the following SQL to find the largest free block.

SELECT UPPER(F.TABLESPACE_NAME)           AS "Table space name",
       D.TOT_GROOTTE_MB                   AS "Table space size(M)",
       D.TOT_GROOTTE_MB  - F.TOTAL_BYTES  AS "Used space(M)",
       TO_CHAR(ROUND((D.TOT_GROOTTE_MB - F.TOTAL_BYTES) / D.TOT_GROOTTE_MB * 100,2),'990.99')
                                          AS "Usage ratio",
       F.TOTAL_BYTES                      AS "Free space(M)",
       F.MAX_BYTES                        AS "Maximum free block(M)"
FROM
  (SELECT TABLESPACE_NAME,
    ROUND(SUM(BYTES) / (1024 * 1024), 2) TOTAL_BYTES,
    ROUND(MAX(BYTES) / (1024 * 1024), 2) MAX_BYTES
  FROM SYS.DBA_FREE_SPACE
  GROUP BY TABLESPACE_NAME
  ) F,
  (SELECT DD.TABLESPACE_NAME,
    ROUND(SUM(DD.BYTES) / (1024 * 1024), 2) TOT_GROOTTE_MB
  FROM SYS.DBA_DATA_FILES DD
  GROUP BY DD.TABLESPACE_NAME
  ) D
WHERE D.TABLESPACE_NAME = F.TABLESPACE_NAME;
 
SELECT FILE_ID,BLOCK_ID, BYTES,BLOCKS 
FROM DBA_FREE_SPACE  
WHERE TABLESPACE_NAME=&TABLESPACE_NAME  
ORDER BY BYTES DESC;

 

Then I found that I could not find the corresponding object using the above two SQL s. As shown in the following screenshot:

 

 

After looking up the data, we found that after Oracle Database 10g introduced the recycle bin function, the space in the recycle bin (RECYCLEBIN$) would be calculated as free space and added to the dba_free_space dictionary. In $ORACLE_HOME/rdbms/admin/catspace.sql, you can find the definition of view DBA_FREE_SPACE. The script is as follows:

Definition of DBA_FREE_SPACE in ORACLE 10g:

create or replace view DBA_FREE_SPACE
    (TABLESPACE_NAME, FILE_ID, BLOCK_ID,
     BYTES, BLOCKS, RELATIVE_FNO)
as
select ts.name, fi.file#, f.block#,
       f.length * ts.blocksize, f.length, f.file#
from sys.ts$ ts, sys.fet$ f, sys.file$ fi
where ts.ts# = f.ts#
  and f.ts# = fi.ts#
  and f.file# = fi.relfile#
  and ts.bitmapped = 0
union all
select /*+ ordered use_nl(f) use_nl(fi) */
       ts.name, fi.file#, f.ktfbfebno,
       f.ktfbfeblks * ts.blocksize, f.ktfbfeblks, f.ktfbfefno
from sys.ts$ ts, sys.x$ktfbfe f, sys.file$ fi
where ts.ts# = f.ktfbfetsn
  and f.ktfbfetsn = fi.ts#
  and f.ktfbfefno = fi.relfile#
  and ts.bitmapped <> 0 and ts.online$ in (1,4) and ts.contents$ = 0
union all
select /*+ ordered use_nl(u) use_nl(fi) */
       ts.name, fi.file#, u.ktfbuebno,
       u.ktfbueblks * ts.blocksize, u.ktfbueblks, u.ktfbuefno
from sys.recyclebin$ rb, sys.ts$ ts, sys.x$ktfbue u, sys.file$ fi
where ts.ts# = rb.ts#
  and rb.ts# = fi.ts#
  and u.ktfbuefno = fi.relfile#
  and u.ktfbuesegtsn = rb.ts#
  and u.ktfbuesegfno = rb.file#
  and u.ktfbuesegbno = rb.block#
  and ts.bitmapped <> 0 and ts.online$ in (1,4) and ts.contents$ = 0
union all
select ts.name, fi.file#, u.block#,
       u.length * ts.blocksize, u.length, u.file#
from sys.ts$ ts, sys.uet$ u, sys.file$ fi, sys.recyclebin$ rb
where ts.ts# = u.ts#
  and u.ts# = fi.ts#
  and u.segfile# = fi.relfile#
  and u.ts# = rb.ts#
  and u.segfile# = rb.file#
  and u.segblock# = rb.block#
  and ts.bitmapped = 0
/

Definition of DBA_FREE_SPACE in ORACLE 11g:

create or replace view DBA_FREE_SPACE
    (TABLESPACE_NAME, FILE_ID, BLOCK_ID,
     BYTES, BLOCKS, RELATIVE_FNO)
as
select ts.name, fi.file#, f.block#,
       f.length * ts.blocksize, f.length, f.file#
from sys.ts$ ts, sys.fet$ f, sys.file$ fi
where ts.ts# = f.ts#
  and f.ts# = fi.ts#
  and f.file# = fi.relfile#
  and ts.bitmapped = 0
union all
select /*+ ordered use_nl(f) use_nl(fi) */
       ts.name, fi.file#, f.ktfbfebno,
       f.ktfbfeblks * ts.blocksize, f.ktfbfeblks, f.ktfbfefno
from sys.ts$ ts, sys.x$ktfbfe f, sys.file$ fi
where ts.ts# = f.ktfbfetsn
  and f.ktfbfetsn = fi.ts#
  and f.ktfbfefno = fi.relfile#
  and ts.bitmapped <> 0 and ts.online$ in (1,4) and ts.contents$ = 0
union all
select /*+ ordered use_nl(u) use_nl(fi) */
       ts.name, fi.file#, u.ktfbuebno,
       u.ktfbueblks * ts.blocksize, u.ktfbueblks, u.ktfbuefno
from sys.recyclebin$ rb, sys.ts$ ts, sys.x$ktfbue u, sys.file$ fi
where ts.ts# = rb.ts#
  and rb.ts# = fi.ts#
  and u.ktfbuefno = fi.relfile#
  and u.ktfbuesegtsn = rb.ts#
  and u.ktfbuesegfno = rb.file#
  and u.ktfbuesegbno = rb.block#
  and ts.bitmapped <> 0 and ts.online$ in (1,4) and ts.contents$ = 0
union all
select ts.name, fi.file#, u.block#,
       u.length * ts.blocksize, u.length, u.file#
from sys.ts$ ts, sys.uet$ u, sys.file$ fi, sys.recyclebin$ rb
where ts.ts# = u.ts#
  and u.ts# = fi.ts#
  and u.segfile# = fi.relfile#
  and u.ts# = rb.ts#
  and u.segfile# = rb.file#
  and u.segblock# = rb.block#
  and ts.bitmapped = 0
/

 

So is it possible that the largest free block found in DBA_FREE_SPACE was an object in the recycle bin? So let's test it.

SQL> show parameter recyclebin;
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
recyclebin                           string      on
 
SQL> CREATE TABLE ESCMOWNER.TTT
  2  AS
  3  SELECT * FROM DBA_OBJECTS;
 
Table created.
 
SQL> COL OWNER FOR A12;
SQL> COL SEGMENT_NAME FOR A32;
SQL> SELECT OWNER,SEGMENT_NAME, HEADER_FILE, HEADER_BLOCK
  2  FROM DBA_SEGMENTS
  3  WHERE OWNER='ESCMOWNER' AND SEGMENT_NAME='TTT' ;
 
OWNER        SEGMENT_NAME                     HEADER_FILE HEADER_BLOCK
------------ -------------------------------- ----------- ------------
ESCMOWNER    TTT                                       97       113025
 
SQL> 
SQL> SELECT * FROM X$KTFBFE WHERE KTFBFEFNO=97;
 
ADDR                   INDX    INST_ID  KTFBFETSN  KTFBFEFNO  KTFBFEBNO KTFBFEBLKS
---------------- ---------- ---------- ---------- ---------- ---------- ----------
00007F57B2388CA0        222          1          9         97     524169        120
 
SQL> DROP TABLE ESCMOWNER.TTT;
 
Table dropped.
 
SQL> COL ORIGINAL_NAME FOR A16;
SQL> SELECT OBJ#,OWNER#,ORIGINAL_NAME,FILE#,BLOCK# ,FLAGS,SPACE FROM RECYCLEBIN$; 
 
      OBJ#     OWNER# ORIGINAL_NAME         FILE#     BLOCK#      FLAGS      SPACE
---------- ---------- ---------------- ---------- ---------- ---------- ----------
    805429         73 TTT                      97     113025         30        896
 
SQL> PURGE DBA_RECYCLEBIN;
 
DBA Recyclebin purged.
 
SQL> SELECT * FROM X$KTFBFE WHERE KTFBFEFNO=97 ;
 
ADDR                   INDX    INST_ID  KTFBFETSN  KTFBFEFNO  KTFBFEBNO KTFBFEBLKS
---------------- ---------- ---------- ---------- ---------- ---------- ----------
00007F57B2388CA0        222          1          9         97     113025          8
00007F57B2388CA0        225          1          9         97     524169        120
 
SQL> 

 

As shown above, after emptying the Recycle Bin object, you will find an additional record in X$KTFBFE, KTFBFEFNO and KTFBFEBNO are 97 and 113025, respectively. This value is obviously to delete the FILE_ID (97) and BLOCK_ID (113025) values of the object TTT.

 

In addition, during the testing process, it was found that not every test result was an additional record in X$KTFBFE, sometimes the record would not change, but the KTFBFEBNO of a record in X$KTFBFE would change, and the change was related to the emptying of the recycling bin. As shown in the following case:

SQL> show parameter recyclebin;
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
recyclebin                           string      on
 
SQL> CREATE TABLE TEST.TTT
  2  AS
  3  SELECT * FROM DBA_OBJECTS;
 
Table created.
 
SQL> COL OWNER FOR A12;
SQL> COL SEGMENT_NAME FOR A32;
SQL> SELECT OWNER,SEGMENT_NAME, HEADER_FILE, HEADER_BLOCK
  2  FROM DBA_SEGMENTS
  3  WHERE OWNER='TEST' AND SEGMENT_NAME='TTT' ;
 
OWNER        SEGMENT_NAME                     HEADER_FILE HEADER_BLOCK
------------ -------------------------------- ----------- ------------
TEST         TTT                                        5          130
 
SQL> SELECT * FROM X$KTFBFE WHERE KTFBFEFNO=5 ;
 
ADDR                   INDX    INST_ID  KTFBFETSN  KTFBFEFNO  KTFBFEBNO KTFBFEBLKS
---------------- ---------- ---------- ---------- ---------- ---------- ----------
00002BA829B19558        150          1          6          5       1280     506752
00002BA829B19558        151          1          6          5     508032      16256
 
SQL> DROP TABLE TEST.TTT;
 
Table dropped.
 
SQL> 
SQL> COL ORIGINAL_NAME FOR A16;
SQL> SELECT OBJ#,OWNER#,ORIGINAL_NAME,FILE#,BLOCK# ,FLAGS,SPACE FROM RECYCLEBIN$; 
 
      OBJ#     OWNER# ORIGINAL_NAME         FILE#     BLOCK#      FLAGS      SPACE
---------- ---------- ---------------- ---------- ---------- ---------- ----------
     82820         85 TTT                       5        130         30       1152
 
SQL> SELECT * FROM X$KTFBFE WHERE KTFBFEFNO=5 ;
 
ADDR                   INDX    INST_ID  KTFBFETSN  KTFBFEFNO  KTFBFEBNO KTFBFEBLKS
---------------- ---------- ---------- ---------- ---------- ---------- ----------
00002BA829B159D8        150          1          6          5       1280     506752
00002BA829B159D8        151          1          6          5     508032      16256
 
SQL> PURGE DBA_RECYCLEBIN;
 
DBA Recyclebin purged.
 
SQL> SELECT * FROM X$KTFBFE WHERE KTFBFEFNO=5 ;
 
ADDR                   INDX    INST_ID  KTFBFETSN  KTFBFEFNO  KTFBFEBNO KTFBFEBLKS
---------------- ---------- ---------- ---------- ---------- ---------- ----------
00002BA829B159D8        150          1          6          5        128     507904
00002BA829B159D8        151          1          6          5     508032      16256
 
SQL> 

 

As shown above, after clearing the recycle bin table, you query X$KTFBFE, and you will find that one of the records changes in KTFBFEBNO. Their relationship is

1280 -1152 = 128

So you can see that the value of KTFBFEBNO has changed from 1280 to 128. When you look at DBA_FREE_SPACE, you will see this. So when the recycle bin is emptied, it may be that the database marks the space of the table as an idle block, or that the idle block is merged into other idle blocks.

X$KTFBFE is actually the initials of these words [k]ernel [t]ablespace [f]ile [b]itmapped [f]ree [e]xtents. The most in-depth introduction to this system view is this article. Talking about Oracle dba_free_space If you are interested, you can verify and test it.

 

Reference material:

http://www.cnblogs.com/princessd8251/p/3868487.html

http://dbzone.iteye.com/blog/1020219

Posted by omerta on Thu, 03 Jan 2019 18:33:09 -0800