Bloques coruptos en una base de datos

Uno de los problemas a los que tenemos que enfrentarnos dia a dia es el de lidiar con bloques corruptos en la base de datos.

Vayamos al supuesto en el que alguna de nuestras herramientas (RMNA,EMC,dbvf, el propio motor en algun acceso) ha detectado que tenemos estos bloques corruptos. La manera de encontarlos es mediante la vista V$DATABASE_BLOCK_CORRUPTION

SQL> select * from V$DATABASE_BLOCK_CORRUPTION;
	 FILE#     BLOCK#     BLOCKS CORRUPTION_CHANGE# CORRUPTIO
---------- ---------- ---------- ------------------ ---------
	 2      88231          1            6821577 NOLOGGING
	 2      58442          1            6821577 NOLOGGING

Recuperacion de bloques corruptos

La manera de recuperarlos es tan sencillo como decirle al RMAN que lo haga, para esto ejecutaremos el comando de rman RECOVER CORRUPTION LIST;:
RMAN> RECOVER CORRUPTION LIST;
Starting recover at 14-JAN-18
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=206 device type=DISK
starting media recovery
media recovery complete, elapsed time: 00:00:01
Finished recover at 14-JAN-18

Comprobacion de que han sido recuperados

Aunque rman nos haya dicho OK, deberemos revisar que los bloques estan correctos, para ello usaremos la opcion VALIDATE DATAFILE de RMAN aplicado al FILE# que nos indicaba la vista de bloques corruptos
RMAN>  validate datafile 2;

Starting validate at 14-JAN-18
using channel ORA_DISK_1
channel ORA_DISK_1: specifying datafile(s) for validation
input datafile file number=2 name=/u01/app/oracle/ORASID/sysaux01.dbf
channel ORA_DISK_1: validation complete, elapsed time: 00:00:01

List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
2    OK     0              21603        336384          41455048 

Finished validate at  14-JAN-18

Y si no lo ha solucionado?

Que ocurre si RMAN no ha podido recuperar el bloque corrupto?
En este caso el comando validate nos devolveria algo similar a

RMAN> validate datafile 2;
Starting validate at 14-JAN-18
using channel ORA_DISK_1
channel ORA_DISK_1: starting validation of datafile
channel ORA_DISK_1: specifying datafile(s) for validation
input datafile file number=2 name=/u01/app/oracle/ORASID/sysaux01.dbf
channel ORA_DISK_1: validation complete, elapsed time: 00:00:01
List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
2    OK     2              20225        99863           49937565
File Name: /u01/app/oracle/ORASID/sysaux01.dbf
Block Type Blocks Failing Blocks Processed
---------- -------------- ----------------
Data       0              26483
Index      0              24051
Other      0              29081
Finished validate at 14-JAN-18

Por que puede ser esto?
En el caso en que no resolvamos el problema, echaremos un vistazo al alert.log,


ORA-01578: ORACLE data block corrupted (file # ORA-01578: ORACLE data block corrupted (file # 2, block # 58442)
ORA-01110: data file 2: '/u01/app/oracle/ORASID/sysaux01.dbf'
ORA-26040: Data block was loaded using the NOLOGGING option

Lo que nos esta indicando este error es que el objeto se creo en modo nologging por lo que la base de datos no tiene informacion de como recuperarla, si queremos recuperar esta informacion deberemos de recrear el objeto ( y cruzar los dedos para que este objeto sea un indice y no datos de negocio)

COmo siempre podemos encontrar mas informacion al respecto en soporte Oracle en las notas:

  • How to identify the corrupt Object reported by ORA-1578 / RMAN / DBVERIFY (Doc ID 819533.1)
  • ORA-1578 / ORA-26040 Corrupt blocks by NOLOGGING - Error explanation and solution (Doc ID 794505.1)
  • How to identify all the Corrupted Objects in the Database with RMAN (Doc ID 472231.1)

Rman DUPLICATE en 12c y Windows

Hoy vamos a ver una sencilla entrada en la que indicaremos como duplicar una base de datos en windows mediante RMAN DUPLICATE.
Este método está muy documentado en un montón de webs, pero , en entornos windows suele tener la pega ( como todo lo de Oracle+Windows) del servicio de windows.

Supongamos que tenemos una base de datos llanada PROD y que queremos clonarla para su uso en desarrollo en otra llamada DESA.
Además de esto, queremos modificar el path de los ficheros de la base de datos, ya que,en la máquina de desarrollo queremos que todo lo que esté en D: y E: pase a estar en Z:

Así pues, los pasos serán:

1- Creamos el init.ora de la instancia a partir de la clonada

Aunque hay documentos que te explican como crear un init mínimo, lo mejor y mas cómodo segun mi opinion es crear un sencillo pfile en texto dsde el spfile del origen

create pfile='initDESA.ora' from spfile;

2- Editamos el initSID.ora y cambiamos TARGET por SID en todas las lineas

Abriremos con cualquier editor el fichero que acabamos de generar y substituiremos la cadena PROD por DESA

3- Creamos el fichero de passwd

Tendremos que ir al $ORACLE_HOME/database crear nuestro fichero de password.

orapwd file=PWDDESA.ora password=mipasswd entries=6 force=y

4- Cramos el servicio de windows

Este es el paso que podemos evitarnos en unix, pero que es inevitable en windows

oradim -NEW -SID DESA -SYSPWD mipasswd -SRVC OracleServiceDESA -STARTMODE auto -SRVCSTART system -SPFILE

5- paramos y arrancamos en modo nomount

El proceso de creacion de servicio habrá arrancado la base de datos , pro lo que tendremos que entrar , pararla y dejarla arrancada en modo nomount.

6- añadimos las líneas de cambio de path en el nuevo INIT.ora

Este es el punto mas extraño ya que, las opciones de cambio de nombre del path del fichero no están en el fichero de comandos de rman, sino en el init.ora de la nueva base de datos.

Con estos comandos movemos tanto los logfiles como los datafiles normales a su nuevo path


LOG_FILE_NAME_CONVERT =('D:\oracle\ORADATA\PROD','Z:\oracle\ORADATA\DESA','E:\oracle\ORADATA\PROD','Z:\oracle\ORADATA\DESA')
DB_FILE_NAME_CONVERT = ('D:\oracle\ORADATA\PROD','Z:\oracle\ORADATA\DESA','E:\oracle\ORADATA\PROD','Z:\oracle\ORADATA\DESA')

7- Creamos el fichero de comandos rman


connect target sys/XXXX.@PROD;
connect catalog user/pass@CATALOG;
connect auxiliary /

CONFIGURE DEFAULT DEVICE TYPE TO 'SBT_TAPE';
CONFIGURE DEVICE TYPE SBT_TAPE PARALLELISM 4;
CONFIGURE CHANNEL DEVICE TYPE SBT_TAPE parms 'ENV=(NSR_SERVER=backupserver, NSR_DATA_VOLUME_POOL=pollduplicados)';

RUN{
DUPLICATE TARGET DATABASE TO "DESA" ;

}

8-Lanzamos el duplicado

Ahora solamente queda lanzar el rman


rman cmdfile=comandos.crv logfile=duplicacion.log

Y un un tiempo record tendréis duplicada vuestra base de datos PROD renombrada a DESA y en el nuevo path Z:\oracle\ORADATA\DESA

Vuelta tras de una base de datos a una fecha con rman

Revisando las entradas de RMAN veo que nos falta una entrada para el caso mas común y mas sencillo de todos, volver la base de datos a una determinada fecha.
La manera mas cómoda de hacer esto desde la versión 11g es hacerlo con un flashback database pero, por si no pudiese hacerse, vamos a explicar la recuperación mas sencilla que hay.

Supuesto

Nos encontramos en el caso en el que no hemos perdido nada en la base de datos pero debemos de hacer una marcha atrás en el tiempo de la base de datos a un momento anterior a 7 días(o la etencion del backup del controlfile).
En este caso disponemos en el servidor de todos los elementos de la base de datos ( passwd,spfile,controlfile….) pero los datos no no son válidos.

Pasos previos

En este caso y para garantizar que si fallamos en el proceso podemos repetirlo guardaremos el controlfile.
Este paso es de suma importancia ya que, al no tener base de datos de catálogo de RMAN si perdiésemos el controlfile perderíamos toda la información del RMAN
Para ello haremos dos acciones:

Copia del controlfile a texto

alter database backup controlfile to  trace as ‘….\CONTROLFILE.TXT’;

Copia física del controlfile

Como decíamos anteriormente, el controlfile el único elemento de la base de datos en el que mantenemos la información de donde esta el catálogo de rman, así que, pararemos la base de datos y copiaremos los 3 controlfiles desde su ubicación en los discos a un directorio dedicado creado para esta copia
El contenido de los 3 controlfiles es exactamente el mismo, con lo que, al copiar los 3 estamos haciendo 3 backups

Recuperacion

Una vez hemos guardado nuestros controlfiles para tener las espaldas cubiertas, procederemos a recuperar la base de datos a el momento en que queremos.
Para ello, crearemos un script de rman llamado recuperacion.cmd con el contenido :

startup mount;
RUN {
SET until time="TO_DATE('23/02/15 21:00:00','DD/MM/YY hh24:mi:ss')";
allocate channel DEV0 type SBT_TAPE PARMS 'ENV=(XXXXXXXXXXXXXXX)';
restore database;
recover database;
 }

Donde ENV=(XXXXXXXXXXXXXXXXXX) dependerá de la integración de backup que se use.
Y lo ejecutaremos con el comando

ORACLE_SID=XXX
rman cmdfile restauracion.cmd log=estado_restauracion.log

Apertura

Al estar haciendo una recuperación de la base de datos incompleta deberemos de abrir la base de datos en modo resetlogs, para ello, desde la línea de comandos

 ORACLE_SID=XXX
Sqlplus “/as sysdba”
ALTER DATABASE OPEN RESETLOGS;

Y con esto tendremos la base de datos recuperada a la fecha que buscábamos.
Como veis, al no tener que conocer DBIDs, ni recuperar controlfiles u spfiles, el

RMAN por entorno de red de windows (ORA-27040 )

Hoy veremos un error bastante comun cuando intentamos hacer un rman remoto en un entorno windows

Supongamos tenemos un servidor windows A y queremos hacer un backup de RMAN depositado en otro servidor windows B

rman target / nocatalog cmdfile rman_windows.cmd

Donde el fichero rman_windows.cmd es

run {
  allocate channel c1 type disk format '\\B\BCKORACLE\rman_pruebas%U';
  backup database;
}

En función de los permisos que tengamos en el servidor B, obtendremos un error:

canal c1: iniciando parte 1 en 18/08/13
canal liberado: c1
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03009: fallo del comando backup en el canal c1 en 08/18/2013 09:12:55
ORA-19504: fallo al crear el archivo "\\B\BCKORACLE\rman_pruebas08OH_1"
ORA-27040: error de creacion de archivo, no se ha podido crear
OSD-04002: no se ha podido abrir el archivo
O/S-Error: (OS 5) Acceso denegado.
Recovery Manager terminado.

Como podemos ver en el error, leyendo siempre de abajo a arriba es un error de acceso de sistema operativo, tal y como indica la nota (Doc ID 145843.1) How to Configure RMAN to Write to Shared Drives on Windows NT/2000/2003 , Oracle necesita tener permisos de SYSTEM sobre el punto de red en el que va a escribir, por tanto tendremos que dar full control al usuario con el que se este ejecutando la base de datos ( o el script de lanzamiento) sobre el recurso remoto.

Un consejo para los windows 2003 en adelante es el no utilizar nombres locales de redes mapeadas, sino que es mas conveniente el uso de nombres de red genérico.

NOTA: En caso de ser una carpeta compartida por entorno de red de windows habrá que dar control total a el system de la máquina donde se ejecuta el Oracle, esto se puede haer con permisos a MI_DOMINIO\A$ donde MI_DOMINIO es el dominio y A el nombre del servidor

Detectando bloques corruptos en Oracle

Hoy vamos a ver como detectar que se nos ha roto en casos de problemas de bloques corruptos de Oracle.

Lo primero que tenemos que tener en mente es tener la base de datos en modo archivelog y hacer copias periódicas con RMAN, pero , ¿que ocurre si no es así y tenemos una corrupción de datos?
Seguramente habremos perdido esos bloques. Pero, lo primero va a ser el saber que objetos lógicos tenemos corruptos .

Tenemos dos maneras de detectar la corrupción de datos

RMAN

Si tenemos la base de datos en modo archivelog ejecutaremos

 rman target / nocatalog
run {
allocate channel d1 type disk;
backup check logical validate database;
release channel d1;
}

El comando validate de RMAN hará un chequeo de los bloques inválidos y nos los dejará en la vista v$database_block_corruption;

DBVERIFY

Si no tenemos la base de datos en modo archivelog, no podemos usar RMAN (a no ser que esté en modo MOUNT), asi que,usaermos dbv.

Oracle provee en todas las plataformas el comando de sistema operativo DBVERIFY (dbv) que nos permite el llevar a cabo una comprobación de la integridad física de los ficheros de la base de datos.Su gran ventaja es que no necesita corte de servicio y podemos ejecutarlo tranquilamente mientras los usuarios trabajan en la base de datos.

La sintaxis la podemos ver en http://docs.oracle.com/cd/A97630_01/server.920/a96652/ch13.htm , y su principal inconveniente es que debe lanzarse para cada uno de los datafiles de la base de datos.

Una manera rápida de automatizar el comando para toda la base de datos es mediante el siguiente script ejecutado como sys o system

spool /tmp/datafiles_corruptos.sh
set linesize 200
set heading off
set pagesize 200
select 'dbv  file=' || name || ' blocksize=' || block_size || 
       ' feedback=' || round(blocks*.10,0)||
       '  logfile=' || file# || '.log'
          from v$datafile;

Este script nos dejara un fichero /tmp/datafiles_corruptos.sh con tantas líneas como datafiles tenga la base de datos de la forma

dbv  file=/opt/oracle/oradata/pruebas/tablespace_indices001.dbf blocksize=8192 feedback=3840  logfile=2

El comando dbv nos va a generar un fichero de log de nombre DATAFILE#.log, con lo que, mi consejo es crear un subdirectorio y lanzar el script /tmp/datafiles_corruptos.sh desde este subdirectorio, asi no llenaremos el path donde nos encontremos de ficheros de log.

EL detectar lo bloques inválidos es muy sencillo, solamente tenemos que ordenar por tamaño de fichero en el subdirectorio que tenemos los logs. Los ficheros que no tienen bloques corruptos tendrás un tamaño aproximado de 1 Km y su contenido será similar a :

DBVERIFY: Release 11.2.0.3.0 - Production on Jue Jun 6 13:14:28 2013
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
DBVERIFY - Iniciando verificación : FILE =/opt/oracle/oradata/pruebas/tablespace_indices018.dbf
DBVERIFY - Verificación terminada
Total de Páginas Examinadas     : 153600
Total de Páginas Procesadas (Datos): 91682
Total de Páginas con Fallos (Datos): 0
Total de Páginas Procesadas (Índice): 28488
Total de Páginas con Fallos (Índice): 0
Total de Páginas Procesadas (Otras): 17288
Total de Páginas Procesadas (Seg): 1
Total de Páginas con Fallos (Seg): 0
Total de Páginas Vacías         : 16142
Total de Páginas Marcadas como Corruptas: 0
Total de Páginas de Entrada     : 0
Total de Páginas Cifradas        : 0
SCN de Bloque Superior            : 1961026896 (9.1961026896)

Los datafiles con bloques corruptos tendrán un tamaño mayor, y su contenido será

DBVERIFY: Release 11.2.0.3.0 - Production on Jue Jun 6 13:16:14 2013
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
DBVERIFY - Iniciando verificación : FILE = /opt/oracle/oradata/pruebas/SYSAUX01.DBF
La página 45847 es de entrada - probablemente el medio físico esté corrupto
Corrupt block relative dba: 0x00c0b317 (file 3, block 45847)
Fractured block found during dbv: 
Data in bad block:
 type: 6 format: 2 rdba: 0x00c0b317
 last change scn: 0x0009.5a58966b seq: 0x1 flg: 0x06
 spare1: 0x0 spare2: 0x0 spare3: 0x0
 consistency value in tail: 0x00000000
 check value in block header: 0x5708
 computed block checksum: 0x48fd

La página 45849 está marcada como corrupta
Corrupt block relative dba: 0x00c0b319 (file 3, block 45849)
Completely zero block found during dbv: 

La página 45850 está marcada como corrupta
Corrupt block relative dba: 0x00c0b31a (file 3, block 45850)
Completely zero block found during dbv: 
.
.
.
.
.
.

La página 117911 es de entrada - probablemente el medio físico esté corrupto
Corrupt block relative dba: 0x00c1cc97 (file 3, block 117911)
Fractured block found during dbv: 
Data in bad block:
 type: 0 format: 0 rdba: 0x00000000
 last change scn: 0x0000.00000000 seq: 0x0 flg: 0x00
 spare1: 0x0 spare2: 0x0 spare3: 0x0
 consistency value in tail: 0xc90c0601
 check value in block header: 0x0
 block checksum disabled

DBVERIFY - Verificación terminada
Total de Páginas Examinadas     : 141312
Total de Páginas Procesadas (Datos): 41849
Total de Páginas con Fallos (Datos): 0
Total de Páginas Procesadas (Índice): 46801
Total de Páginas con Fallos (Índice): 0
Total de Páginas Procesadas (LOB)  : 3100
Total de Páginas con Fallos (LOB)  : 0
Total de Páginas Procesadas (Otras): 27154
Total de Páginas Procesadas (Seg): 0
Total de Páginas con Fallos (Seg): 0
Total de Páginas Vacías         : 22352
Total de Páginas Marcadas como Corruptas: 56
Total de Páginas de Entrada     : 6
Total de Páginas Cifradas        : 0
SCN de Bloque Superior            : 1961026953 (9.1961026953)

Con esto tendremos también la información en la vista v$database_block_corruption;, pero ¿que objetos son los que tenemos con problemas?
Si ejecutáis la consulta :

set linesize 200
set pagesize 200
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#;

Obtendréis un resultado similar a este


OWNER        SEGMENT_TYPE       SEGMENT_NAME                   PARTITION_NAME   CORR_START_BLOCK# CORR_END_BLOCK# BLOCKS_CORRUPTED DESCRIPTION
----------- ------------------ ------------------------------------------------- ---------- ----------------- ---
SYS           TABLE              WRH$_PGASTAT                       3             45847         45847                1
SYSMAN        TABLE              MGMT_TARGET_ASSOC_ERROR            3             45851          45851             1 Segment Header

Como siempre, tenemos mas información y mas precisa en soporte oracle en :

  • OERR: ORA-1578 «ORACLE data block corrupted (file # %s, block # %s)» Master Note [ID 1578.1]
  • Note 28814.1 Handling Oracle Block Corruptions in Oracle7/8/8i/9i/10g
  • Note 556733.1 DBMS_REPAIR script y Note 68013.1 DBMS_REPAIR example
  • Physical and Logical Block Corruptions. All you wanted to know about it. [ID 840978.1]