Thursday, January 17, 2013

How to identify all the Corrupted Objects in the Database


ORA-01578: ORACLE data block corrupted (file # 377, block # 4205)
ORA-01110: data file 392: '/u01/app/oracle/db/apps_st/data/a_tx_data01.dbf'
ORA-26040: Data block was loaded using the NOLOGGING option

For Refer :

&AFN      is  : 392       (from the ORA-1110 portion of the error)
&RFN      is  : 377      (from the "file #" in the ORA-1578)
&BL       is  : 4205  (from the "block #" in the ORA-1578)
&FILENAME is  : '/d01/app/oracle/db/apps_st/data/a_tx_data01.dbf'
 

Step 1 .The corrupted blocks are listed in the view v$database_block_corruption:

SQL> select * from v$database_block_corruption;

Step 2:  The corrupted blocks are listed with Tablsspace
SQL> select b.tablespace_name, count(BLOCK#) from v$database_block_corruption a, dba_data_files b where a.FILE#=b.FILE_ID group by b.tablespace_name

Step 3: Identify the corrupt segments It will map each block from v$database_block_corruption to either a segment or if the block is free.

SQL> set pagesize 2000
         set linesize 250
     SELECT e.owner, e.segment_type, e.segment_name, e.partition_name, c.file#
     , greatest(e.block_id, c.block#) corr_start_block#
     , least(e.block_id+e.blocks-1, c.block#+c.blocks-1) corr_end_block#
     , least(e.block_id+e.blocks-1, c.block#+c.blocks-1)
       - greatest(e.block_id, c.block#) + 1 blocks_corrupted
     , null description
  FROM dba_extents e, v$database_block_corruption c
 WHERE e.file_id = c.file#
   AND e.block_id <= c.block# + c.blocks - 1
   AND e.block_id + e.blocks - 1 >= c.block#
UNION
SELECT s.owner, s.segment_type, s.segment_name, s.partition_name, c.file#
     , header_block corr_start_block#
     , header_block corr_end_block#
     , 1 blocks_corrupted
     , 'Segment Header' description
  FROM dba_segments s, v$database_block_corruption c
 WHERE s.header_file = c.file#
   AND s.header_block between c.block# and c.block# + c.blocks - 1
UNION
SELECT null owner, null segment_type, null segment_name, null partition_name, c.file#
     , greatest(f.block_id, c.block#) corr_start_block#
     , least(f.block_id+f.blocks-1, c.block#+c.blocks-1) corr_end_block#
     , least(f.block_id+f.blocks-1, c.block#+c.blocks-1)
       - greatest(f.block_id, c.block#) + 1 blocks_corrupted
     , 'Free Block' description
  FROM dba_free_space f, v$database_block_corruption c
 WHERE f.file_id = c.file#
   AND f.block_id <= c.block# + c.blocks - 1
   AND f.block_id + f.blocks - 1 >= c.block#
order by file#, corr_start_block#;

An output example is:
OWNER SEGMENT_TYPE       SEGMENT_NAME PARTITION_ FILE# CORR_START_BLOCK# CORR_END_BLOCK# BLOCKS_CORRUPTED DESCRIPTION
----- ------------------ ------------ ---------- ----- ----------------- --------------- ---------------- -------------
SCOTT TABLE              EMP                         6                10              10                1
SCOTT TABLE PARTITION    ORDER        ORDER_JAN      6                26              28                3
                                                     6                29              29                1 Free Block

Step 4:To find the Corrupt Tablespace Name and details ..

 SQL> select b.tablespace_name,count(BLOCK#) from v$database_block_corruption a, dba_data_files b where a.FILE#=b.FILE_ID group by b.tablespace_name;

An output example is:
TABLESPACE_NAME                     FILE#     BLOCK#     BLOCKS CORRUPTIO
------------------------------ ---------- ---------- ---------- ---------
APPS_TS_TX_DATA                        06     57997          3 CORRUPT
APPS_TS_TX_DATA                        31    16067          1 CORRUPT
APPS_TS_TX_DATA                        34    173076          4 CORRUPT
APPS_TS_TX_DATA                       392      620         13 CORRUPT
APPS_TS_NOLOGGING                      401     2090         5 CORRUPT

Step 5: For each file# need to fire below query and find the objects that are corrupt & make a note of them. (Replace 06 with file number)

SELECT e.owner, e.segment_type, e.segment_name, e.partition_name, c.file#
     , greatest(e.block_id, c.block#) corr_start_block#
     , least(e.block_id+e.blocks-1, c.block#+c.blocks-1) corr_end_block#
     , least(e.block_id+e.blocks-1, c.block#+c.blocks-1)
       - greatest(e.block_id, c.block#) + 1 blocks_corrupted
     , null description
  FROM dba_extents e, v$database_block_corruption c
WHERE e.file_id = c.file#
   AND e.block_id <= c.block# + c.blocks - 1
   AND e.block_id + e.blocks - 1 >= c.block#
   and c.file#=06
UNION
SELECT s.owner, s.segment_type, s.segment_name, s.partition_name, c.file#
     , header_block corr_start_block#
     , header_block corr_end_block#
     , 1 blocks_corrupted
     , 'Segment Header' description
  FROM dba_segments s, v$database_block_corruption c
WHERE s.header_file = c.file#
   AND s.header_block between c.block# and c.block# + c.blocks - 1
   and c.file#=06
UNION
SELECT null owner, null segment_type, null segment_name, null partition_name, c.file#
     , greatest(f.block_id, c.block#) corr_start_block#
     , least(f.block_id+f.blocks-1, c.block#+c.blocks-1) corr_end_block#
     , least(f.block_id+f.blocks-1, c.block#+c.blocks-1)
       - greatest(f.block_id, c.block#) + 1 blocks_corrupted
     , 'Free Block' description
  FROM dba_free_space f, v$database_block_corruption c
WHERE f.file_id = c.file#
   AND f.block_id <= c.block# + c.blocks - 1
   AND f.block_id + f.blocks - 1 >= c.block#
   and c.file#=06
order by file#, corr_start_block#;


Solution : 
Import the identified tables & rebuilt the indexes:
Drop the Tablespace ,Import  from Source to Target:

Source :
 expdp system/manager@PRD tablespaces=APPS_TS_TX_DATA directory=txdata dumpfile=appstxdt.dmp logfile=appstxdt.log

Target :
impdp system/manager@DEV  tablespaces=APPS_TS_TX_DATA directory=txdata dumpfile=appstxdt.dmp logfile=appstxdt.log

Step 6:   Retest the issue


To find the details …. Please check bellow :

1) An absolute FILE NUMBER (&AFN) of the file containing the corrupt block

SQL> SELECT name FROM v$datafile WHERE file#=&AFN;
Note : The file name of the file containing the corrupt block (Ex : file # 377)
                The BLOCK NUMBER of the corrupt block in that file     (Ex : block # 4205) 
2). The tablespace number and name containing the affected block.
SQL>   SELECT ts# "TSN" FROM v$datafile WHERE file#=&AFN;
SQL> SELECT tablespace_name FROM dba_data_files WHERE file_id=&AFN;

3). The block size of the tablespace where the corruption lies
SQL> SELECT block_size FROM dba_tablespaces
     WHERE tablespace_name =
       (SELECT tablespace_name FROM dba_data_files WHERE file_id=&AFN);

4).Identify the Corrupt Object
    The database must be open in order to use this query:
    The following query will tell you the object TYPE , OWNER and NAME of a segment
SQL> SELECT tablespace_name, segment_type, owner, segment_name
          FROM dba_extents   WHERE file_id = &AFN
           and &BL between block_id AND block_id + blocks – 1;
SQL> select  *  from dba_extents where file_id = &AFN
and &BL between block_id AND block_id + blocks - 1;

Note : If the block is in a TEMPFILE the above query will return no data

5). If the above query does not return rows, it can also be that the corrupted block is a segment header
  in a Locally Managed Tablespace (LMT).  When the corrupted block is a segment 
  header block in a LMT,  the above query produces a corruption message in the alert.log 
  but the query does not not fail.  In that case use this query:
SQL> SELECT owner, segment_name, segment_type, partition_name 
         FROM dba_segments WHERE header_file = &AFN and header_block = &BL;

Reference Note 819533.1(case 5)

6). If the block belongs to a free extent it should appear in DBA_FREE_SPACE:       
SQL> select * from  dba_free_space
where file_id = &AFN and &BL between block_id AND block_id + blocks - 1;

7). If the segment is a CLUSTER determine which tables it contains.
SQL> SELECT owner, table_name FROM dba_tables 
     WHERE owner='&OWNER' AND cluster_name='&SEGMENT_NAME';

8). If the segment is an INDEX PARTITION 
SQL> SELECT partition_name FROM dba_extents    WHERE file_id = &AFN
     AND &BL BETWEEN block_id AND block_id + blocks – 1;

Note : then continue below as if the segment was an INDEX segment.
                    Options:Index partitions can be rebuilt using:
EX: ALTER INDEX xxx REBUILD PARTITION ppp; 

INDEX :
                    - If the segment is an INDEX then if the OWNER is "SYS" contact 
                      Oracle support with all details.
 Note: For a non-dictionary INDEX or INDEX PARTITIONs find out which table 
                      the INDEX is on:
                      Eg:SQL>SELECT table_owner, table_name FROM dba_indexes 
                                      WHERE owner='&OWNER' AND index_name='&SEGMENT_NAME';
Note:  and determine if the index supports a CONSTRAINT:
                      Eg:SQL>SELECT owner, constraint_name, constraint_type, table_name 
                                           FROM dba_constraints WHERE owner='&TABLE_OWNER' 
                                           AND constraint_name='&INDEX_NAME';
Note: Possible values for CONSTRAINT_TYPE are:
                                          P                  The index supports a primary key constraint.
                                         U                 The index supports a unique constraint.
 Note :  If the INDEX supports a PRIMARY KEY constraint (type "P") then
               check if the primary key is referenced by any foreign key constraints:
                 Eg: 
                SQL> SELECT owner, constraint_name, constraint_type, table_name 
                                 FROM dba_constraints WHERE r_owner='&TABLE_OWNER' 
                                   AND r_constraint_name='&INDEX_NAME';
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ID 28814.,472231.1,336133.1,819533.1 & 369076.1
+++++++++++++++++++++++++++++++++++++++++++++++++++++
Step 1:
SQL> select * from v$database_block_corruption;
SQL> select * from v$datafile where file#='&file#no';
SQL> select distinct file# from  v$database_block_corruption;
SQL> select distinct file#,CORRUPTION_TYPE from V_$DATABASE_BLOCK_CORRUPTION order by file#;
SQL> select file_id,file_name from dba_data_files where file_id in (no1,n02,no3,no4);
SQL> select owner, segment_name,tablespace_name from dba_extents where FILE_ID=&id and <Block> between BLOCK_ID and BLOCK_ID+BLOCKS;
Step 2: Check whether block is part of any object
Query dba_extents and cross check the block doesnot belong to any object.
SQL> select segment_name,segment_type, owner from dba_extents
      where file_id = <Absolute filenumber> and <corrupted block number>
between block_id and block_id + blocks -1;
Note : If it doesn't belong to an object, double check if itdoes exists in dba_free_space to check if the block belongs to file space usage bitmap.
SQL> Select * from dba_free_space where file_id= <Absolute file number>
    and <corrupted block number> between block_id and block_id + blocks -1;
Step 3:  We cannot run a query on dba_extents to find out what the object is on the standby, so we ran the following query on the Primary to find the corrupted block:
SQL> SELECT tablespace_name, segment_type, owner, segment_name
FROM dba_extents WHERE file_id = &no-start and &no-end between block_id AND block_id + blocks - 1;
Step 4: The repair data block of table in a given tablespace:
SQL> BEGIN 
DBMS_REPAIR.ADMIN_TABLES (TABLE_NAME => 'REPAIR_TABLE',
TABLE_TYPE => dbms_repair.repair_table,
ACTION => dbms_repair.create_action,
TABLESPACE => '&tablespace_name');
END;
/
Step 5: Identify corrupted blocks for schema.object:
SQL> set serveroutput on
SQL> DECLARE num_corrupt INT;
BEGIN
num_corrupt := 0;
DBMS_REPAIR.CHECK_OBJECT (
SCHEMA_NAME => '&schema_name',
OBJECT_NAME => '&object_name',
REPAIR_TABLE_NAME => 'REPAIR_TABLE',
corrupt_count => num_corrupt);
DBMS_OUTPUT.PUT_LINE('number corrupt: ' || TO_CHAR (num_corrupt));
END;
Step 6:  Optionally display any corrupted block identified by check_object:
SQL> select BLOCK_ID,CORRUPT_TYPE, CORRUPT_DESCRIPTION from REPAIR_TABLE;
Step 7: Mark the identified blocks as corrupted
SQL> DECLARE num_fix INT;
BEGIN
num_fix := 0;
DBMS_REPAIR.FIX_CORRUPT_BLOCKS (
SCHEMA_NAME => '&schema_name',
OBJECT_NAME=> '&object_name',
OBJECT_TYPE => dbms_repair.table_object,
REPAIR_TABLE_NAME => 'REPAIR_TABLE',
FIX_COUNT=> num_fix);
DBMS_OUTPUT.PUT_LINE('num fix: ' || to_char(num_fix));
END;
/
Step 8 : To find the corruption objects
SQL> select count(*) from v$database_block_corruption;
SQL> select 'SELECT e.file_id,e.block_id,e.owner,e.segment_name,
e.segment_type FROM dba_extents e WHERE
file_id='|| file#||' and '|| block#||' BETWEEN block_id AND block_id + blocks - 1; '
from v$database_block_corruption order by file#;
Note :Allow future DML statements to skip the corrupted blocks
Note : If it is needed to stop skipping corrupt blocks in the object -- ID 556733.1:
SQL> BEGIN
DBMS_REPAIR.SKIP_CORRUPT_BLOCKS (
SCHEMA_NAME => '&schema_name',
OBJECT_NAME => '&object_name',
OBJECT_TYPE => dbms_repair.table_object,
FLAGS => dbms_repair.SKIP_FLAG);
END;
/
Note : if not required :
SQL> execute dbms_backup_restore.resetCfileSection(file#); /** clear v$backup_corruption   
To verify :
SQL>  SELECT OBJECT_NAME, BLOCK_ID, CORRUPT_TYPE, MARKED_CORRUPT,CORRUPT_DESCRIPTION,EPAIR_DESCRIPTION FROM REPAIR_TABLE;
SQL> select * from  V$DATABASE_BLOCK_CORRUPTION ;
RMAN Verify
To identify both Physical and Logical Block Corruptions use the "CHECK LOGICAL" option. The next command checks the complete database for both corruptions without actually doing a backup:
$ rman target /
RMAN>  backup check logical validate database;
Note : The next command checks the complete database for both corruptions in a backup:
$ rman target /
RMAN> backup check logical database
DBVerify - Identify Datafile Block Corruptions
DBVERIFY identify Physical and Logical Intra Block
Corruptions by default. Dbverify cannot be run for the whole database in a single command. It does not need a database connection either:
dbv file=<datafile name> blocksize=<datafile Block size>
SQL> connect internal
 Connected.
SQL> show parameter db_block_size
SQL>select file#,name,bytes/2048 from v$datafile;
SQL> EXIT
$dbv file=/u02/oradata/R815/oradata/R815/users01.dbf blocksize=2048 logfile=users01_dbv.log feedback=100

++++++++++++++++++++++++++++++++++++++++++++++

No comments:

Post a Comment