lunes, 16 de abril de 2018

Reparar tablas con bloques de datos corruptos ORA-01578:

Si al realizar consultas de tablas o en los logs de la base de datos se te presentan el mensaje de ORA-01578, esto quiere decir que se han corrompido bloques de datos en o los datafiles, esto se puede originar por daños físico o lógicos de los datos.

ORA-01578: ORACLE data block corrupted (file # , block # )
ORA-01578: ORACLE data block corrupted (file # 2, block # 88580)

1-Identificar el segmento corrupto

Lo primero que debemos de realizar es identificar cual es el datafile y segmento que tiene el o los bloques corruptos. Podemos usar el database verify "dbv" o analizar los objetos hasta dar con los que den el error.

DBV
Sabiendo el datafile con el problema lo analizaremos con el dbv para determinar los bloques corruptos
//Desde un CMD ejecutamos el DBV
>dbv userid=control/oracle10g file =E:\oracle\data\D7IPROD\datafile\USER01.DBF blocksize=8192

//El resultado va a ser una lista de los bloques corruptos con un resumen
DBV-00201: Block, DBA 38428646, marked corrupt for invalid redo application
DBV-00201: Block, DBA 38428647, marked corrupt for invalid redo application

DBVERIFY - Verification complete
Total Pages Examined : 782088
Total Pages Processed (Data) : 777097
Total Pages Failing (Data) : 0
Total Pages Processed (Index): 1
Total Pages Failing (Index): 0
Total Pages Processed (Lob) : 21
Total Pages Failing (Lob) : 0
Total Pages Processed (Other): 4905
Total Pages Processed (Seg) : 34
Total Pages Failing (Seg) : 0
Total Pages Empty : 30
Total Pages Marked Corrupt : 2
Total Pages Influx : 0
Total Pages Encrypted : 0
Highest block SCN : 1709553936 (1293.1709553936)

Una vez obtenido los bloques corruptos vamos a determinar de que segmento proviene.

Crear el procedimiento CDBA para a partir del bloque sel SO obtener el bloque de la base de datos.

//Crear en la base de datos
SQLPLUS>CREATE OR REPLACE PROCEDURE cdba ( iblock VARCHAR2, imode VARCHAR2 ) AS
x NUMBER;
digits# NUMBER;
results NUMBER := 0;
file# NUMBER := 0;
block# NUMBER := 0;
cur_digit CHAR(1);
cur_digit# NUMBER;
/*Creavo por VEM
 TFTS: Converting DBA's (Database Addresses) to File # and Block # (Doc ID 113005.1)
*/
BEGIN
 IF upper(imode) = 'H' THEN
 digits# := length( iblock );
 FOR x IN 1..digits# LOOP
 cur_digit := upper(substr( iblock, x, 1 ));
 IF cur_digit IN ('A','B','C','D','E','F') THEN
 cur_digit# := ascii( cur_digit ) - ascii('A') +10;
 ELSE
 cur_digit# := to_number(cur_digit);
 END IF;
 results := (results *16) + cur_digit#;
 END LOOP;
 ELSE
 IF upper(imode) = 'D' THEN
 results := to_number(iblock);
 ELSE
 dbms_output.put_line('H = Hex Input ... D = Decimal Input');
 RETURN;
 END IF;
 END IF;

 file# := dbms_utility.data_block_address_file(results);
 block# := dbms_utility.data_block_address_block(results);

 dbms_output.put_line('.');
 dbms_output.put_line( 'The file is ' || file# );
 dbms_output.put_line( 'The block is ' || block# );
END;
/

Una vez creado lo invocamos con los datos del DBV, para ello necesitamos saber el id del datafile

//Identificar el ID del datafile
SQLPLUS> select file#, name from v$datafile where name='E:\ORACLE\DATA\D7IPROD\DATAFILE\USER01.DBF';

 FILE# NAME 
------------------------------------------------------------------------------------------
 4     E:\ORACLE\DATA\D7IPROD\DATAFILE\USER01.DBF 

//Tomamos del resultado de DBV los numero de bloques
SQLPLUS begin cdba(50513350,'D'); end;\

Result
.
The file is 12
The block is 181702

//Una vez obtenido el numero de bloque vamos a ver a que segmento pertenece
SQLPLUS>select segment_name, segment_type, owner
from dba_extents
where file_id = 9
and 181702 between block_id
and block_id + blocks -1;

Results
segment_name   segment_type  owner
-----------------------------------------------------------------------------------MI_TABLA_MALA  TABLE         HR     


Identificar con el ANALIZE

//Con esto validamos la tabla y nos retornara error si tiene bloques corruptos
BEGIN
 SYS.DBMS_STATS.GATHER_TABLE_STATS (OwnName => 'HR',
 TabName => 'MI_TABLA_MALA',
 Estimate_Percent => 10,
 Method_Opt => 'FOR ALL COLUMNS SIZE 1',
 Degree => 4,
 Cascade => FALSE,
 No_Invalidate => FALSE);
END;

ORA-01578: bloque de datos ORACLE corrupto (archivo número 4, bloque número 679744)
ORA-01110: archivo de datos 9: 'E:\ORACLE\DATA\D7IPROD\DATAFILE\USERS01.DBF'
ORA-26040: Se ha cargado el bloq

Listo, a tenemos identificado el segmento


2 Realizar la recuperación o eliminación del bloque corrupto

Lo que mejor podemos hacer es realizar una recuperación con el RMAN ya sea a nivel del datafile o siendo mas especifico a nivel de bloque. Lo mas fácil, restore y recover. Para esto es necesario e imprensindible tener la base de datos en archivelog y tener un respaldo indoneo para realizar la recuperación.

RMAN

//Ponemos el datafile offline antes de iniciar la recuperación
RMAN> alter database datafile '/u01/app/oracle/oradata/orcl/user01.dbf' offline;
Statement processed
//Para saber el id del datafile en un sqlplus y ver el resultado select file#, name from v$datafile
RMAN> restore datafile 5;
channel ORA_DISK_1: starting datafile backup set restore
channel ORA_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_DISK_1: restoring datafile 00005 to /u01/app/oracle/oradata/orcl/user01.dbf
channel ORA_DISK_1: reading from backup piece /backup/18rb3b6l_1_1
channel ORA_DISK_1: piece handle=/backup/18rb3b6l_1_1 tag=TAG20160719T124301
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
Finished restore at 
 
RMAN> recover datafile 5;
Starting recover at 
using channel ORA_DISK_1
starting media recovery
media recovery complete, elapsed time: 00:00:00
Finished recover at 
//Regresamos el datafile online una vez finalizado la recuperación
RMAN> alter database datafile '/u01/app/oracle/oradata/orcl/user01.dbf' online;
using target database control file instead of recovery catalog
Statement processed


Pero que pasa si no tenemos una estrategia de respaldo con RMAN?, bueno no hay que echarse a llorar, si hay una forma de salir de esos errores, pero segun el caso podemos tener una leve perdida de información ya que vamos a transferir los datos a una nueva estructura pero omitiendo lo datos relacionados a los bloques corruptos.

SIN RMAN 

//Modificamos el nivel de registro de eventos
SQL> ALTER SYSTEM SET EVENTS='10231 trace name context forever,level 10' ;

//Procedemos a crear una nueva tabla a partir de la que presenta los errores de corrupción
SQL> CREATE TABLE mi_tabla_nueva AS SELECT * FROM mi_tabla_mala;

//Revisamos los datos creaado en mi_Tabla_nueba y luego borramos la tabla mi_tabla_mala.
SQL> DROP TABLE bad_table;
//realizamos el renombre de mi_Tabla_nueva a mi_tabla_mala
SQL> RENAME good_table TO bad_table;

//Desactivamos el nivel de eventos
ALTER SYSTEM SET EVENTS '10231 trace name context off';





2 comentarios: