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]

Cambiar la base de datos del repositorio de el OEM12c

Hoy vamos a ver en una entrada rápida como cambiaríamos la base de datos del OEM12c de un equipo a otro.
Los pasos necesarios para llevar a cabo este cambio son:

  • Parar el OMS
  • Hacer un backup de la base de datos
  • Restaurar la base de datos en la nueva ubicacion
  • Reconfigurar el OEM12c
  • Arrancar el OEM12c

Veamos uno a uno los pasos

Parar el OMS

$AGENT_HOME/bin/emctl stop agent
$OMS_HOME/bin/emctl stop oms

Backup de la base de datos
Aquí llevaríamos a cabo un backup normal de la base de datos con RMAN

Recuperación de la base de datos
Al igual que el backup, se trata de un restore standard mediante RMAN

Reconfigurar el OEM12c
Este es el paso mas dificil de hacer, debemos de conocer los datos:

  • LISTENER_PORT Puerto del listener de la nueva BBDD
  • NUEVOSID SID de la base de datos (no tiene por que cambiar)
  • NUEVOHOST nombredel nuevo host (conviene que esté en el /etc/hosts del servidor)
  • NUEVOPASWD Password del respositorio ( si no se ha cambiado es el mismo que el de sysman anterior)

y con esto,simplemente debemos de ejecutar el cambio de repositorio con el comando

emctl config oms -store_repos_details -repos_port LISTENER_PORT -repos_sid NUEVOSID -repos_host NUEVOHOST -repos_user SYSMAN -repos_pwd NUEVOPASSWD
Oracle Enterprise Manager Cloud Control 12c Release 3
Copyright (c) 1996, 2013 Oracle Corporation.  All rights reserved.
Successfully updated datasources and stored repository details in Credential Store.
If there are multiple OMSs in this environment, run this store_repos_details command on all of them.
And finally, restart all the OMSs using 'emctl stop oms -all' and 'emctl start oms'.
 

Arrancar el OMS

$AGENT_HOME/bin/emctl start agent
$OMS_HOME/bin/emctl start oms

Recuperacion con RMAN desde Dataprotector desde linea de comandos

Muchas veces tenemos el backup integrado por scripts propietarios del software de backup.
Esta integración nos garantiza el pode recuperar con «botón derecho», pero , puede darse el caso de querer recuperar manualmente, bien por que queremos tener el control total sobre el proceso o bien por que es en otra maquina o por que queramos hacer una recuperación mas especifica del RMAN que la que nos ofrezcan los botones del software de backup.

En este caso vamos a hacer una recuperación total de una base de datos que se ha copiado con dataprotector. Entre las cosas que necesitaremos son:

  • Init.ora de la base de datos, deberíamos de hacer una copia del mismo junto con el backup,con lo que podemos sacarlo de ahi
  • DBID de la base de datos, este DBID aparece en el log de rman, con lo que podremos sacarlo del ultimo log del backup
  • Cadena de configuración de la cinta. Esta en las propiedades avanzadas de la política de backup que usamos para copiar nuestra base de datos

Además, necesitaremos ser capaces de llegar al log de la ultima copia de rman, esto se hace desde dataprotector, mirando en las siguientes pestañas

Internal Database
     -> "log del backup"  (tiene el formato fecha/backup)
           -> Propierties (boton derecho)
               --> Messages (el log completo del rman)

Para clarificar un poco los logs, tendremos en este caso:

  • instancia=pruebas
  • Servidor=serveroracle.pamplona.name
  • DBID=3751694031 (obtenido del log del backup desde dataprotector)

Si no tuviésemos el init.ora podríamos recuperarlo también del backup ya que la 11g hace copia del init.ora con el controlfile autobackup, pero, es una buena practica el tener una copia del init.ora en modo texto, ya que, nos evita uno de los pasos mas engorrosos.
Con estas 3 cosas, podemos comenzar la recuperación de la base de datos.

Lo primero que recuperaremos será el controlfile, para ello haremos un script al que llamaremos restore_controlfile.cmd tal que

startup nomount;
set DBID=3751694031
run {
allocate channel 'dev_0' type 'sbt_tape'
 parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=Online Diaria)';
restore controlfile from autobackup;
}

Al que llamaremos con

rman target / cmdfile restore_controlfile.cmd

En este punto, podemos llevarnos la sorpresa de que obtenemos un RMAN-06172

Al igual que vimos en el post RMAN-06172: no AUTOBACKUP found or specified handle is not a valid copy or piece tendremos que decirle exactamente cual es el nombre del controlfile que queremos recuperar, para ello, nos iremos al dataprotector y exploramos el log hasta la última línea en la que encontremos la palabra controlfile

[Normal] From: OB2BAR_DMA@serveroracle.pamplona.name "pruebas"  Time: 06/03/2013 21:21:19
	Starting OB2BAR Backup:serveroracle.pamplona.name: pruebas DP Managed Control File Backup "Oracle8"

La línea que estamos buscando es pruebas DP Managed Control File Backup «Oracle8», que es el fichero dentro de dataprotector donde se encuentra nuestro controlfile.
Así pues, modificaremos el script de backup y ahora será:

startup nomount;
set DBID=3751694031
run {
allocate channel 'dev_0' type 'sbt_tape'
 parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=Online Diaria)';
restore controlfile from 'pruebas DP Managed Control File Backup "Oracle8"';
}

Con esto conseguiremos tener nuestro controlfile restaurado. Ahora, ya tenemos una restauracion standard de RMAN típica de manual.

Error RMAN-06172 recuperando del autobackup

En la entrada anterior  vimos como recuperar un spfile desde el backup con rman.

Desgraciadamente no siempre todo funciona a la primera, y , como Murphy nunca falla, el error inusual siempre tiene que tocarnos a nosotros. La teoría nos dice que  para recuperar un spfile o un controlfile de rman solamente hay que hacer

RMAN> restore spfile from autobackup;

Pero que ocurre si no funciona?

La recuperacion tanto del controlfile como del spfile puede complicarse si recibimos un RMAN-06172 . La salida del comando será algo similar a esto :

Starting restore at 28/09/12
allocatedchannel: ORA_DISK_1
channel ORA_DISK_1: sid=85 instance=pruebas devtype=DISK
recovery area destination: +DG_FRA
database name (ordatabaseuniquename) used for search: PRUEBAS
channel ORA_DISK_1: no autobackupsfound in the recovery area
channel ORA_DISK_1: looking for autobackup on day: 20120928
channel ORA_DISK_1: looking for autobackup on day: 20120927
channel ORA_DISK_1: looking for autobackup on day: 20120926
channel ORA_DISK_1: looking for autobackup on day: 20120925
channel ORA_DISK_1: looking for autobackup on day: 20120924
channel ORA_DISK_1: looking for autobackup on day: 20120923
channel ORA_DISK_1: looking for autobackup on day: 20120922
Channel ORA_DISK_1: no autobackup in 7 daysfound
RMAN-00571:   =================================
RMAN-00569: == ERROR MESSAGE STACK FOLLOWS
RMAN-00571: ===================================
RMAN-03002: failure of restorecommand at 09/28/2012 11:40:37
RMAN-06172: no autobackup found or  specified handle is not a valid copy or piece

Llegados a este punto lo primero que tenemos que comprobar es si reamente tenemos algún backup de nuestro spfile.

RMAN por defecto hace una copia de controlfile y spfile cada vez que se lleva a backup el tablespace system, así pues, si tenemos un backup válido en cinta de este tablespace deberemos de tener un backup del spfile.  Para consultar donde está esta backup, nos conectaremos desde  rman y ejecutaremos la consulta

RMAN >List backup;

Este comando nos volcará mucha información ordenada cronológicamente, con lo que nosotros tendremos que buscar en la parte inferior el registro del último día de backup donde esté el spfile.

El registro dirá algo como:

BS Key  Type LV SizeDeviceTypeElapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
797503  Incr 0  5.44G      SBT_TAPE    00:03:52     27/09/12
        BP Key: 797556   Status: AVAILABLE  Compressed: NO  Tag: TAG20120926T210038
Handle: intranet_backup<pruebas_8084:795042038:1>.dbf   Media:
List of Datafiles in backup set 797503
  File LV TypeCkp SCN    Ckp Time Name
  ---- -- ---- ---------- -------- ----
  1    0  Incr 139540429  26/09/12 +DG_DAT/pruebas/datafile/system.257.713298945
  2    0  Incr 139540429  26/09/12 +DG_DAT/pruebas/datafile/undotbs1.262.713298945
  3    0  Incr 139540429  26/09/12 +DG_DAT/pruebas/datafile/sysaux.261.713298945
  4    0  Incr 139540429  26/09/12 +DG_DAT/pruebas/datafile/undotbs2.264.713298947
  5    0  Incr 139540429  26/09/12 +DG_DAT/pruebas/datafile/users.345.713298947
  6    0  Incr 139540429  26/09/12 +DG_DAT/pruebas/datafile/pruebas.260.730901167

BS Key  Type LV SizeDeviceTypeElapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
797504Incr 0  15.00M     SBT_TAPE    00:00:06     27/09/12
        BP Key: 797557   Status: AVAILABLE  Compressed: NO  Tag: TAG20120926T210038
Handle:pruebas_backup<pruebas_8085:795042273:1>.dbf   Media:
  Control File Included: Ckp SCN: 139541276    Ckp time: 27/09/12
SPFILE Included: Modification time: 27/09/12

Aquí podemos ver como el 27 del 9 hicimos backup del tablespace system en el backupset 797504 lo que provoca que el RMAN haga backup de el SPFILE en el backupset  797504 en un fichero que el software de backup tiene identificado como  “pruebas_backup<pruebas_8085:795042273:1>.dbf”

Luego, si que tenemos respaldado el spfile.

¿Que hacemos ahora para recuperarlo?

Simplemente tendremos que ejecutar el comando de restauración indicando exactamente desde el fichero en el que queremos hacer la copia.

Vamos a rizar un poco mas el rizo y hagamos que la copia no esté ya en  la FRA,sino que el contenido ya está en cinta. Lo primero que tendremos que hacer es localizar un canal a cinta, para ello lo más sencillo es buscar el script de backup y copiar los parámteros específicos de  la línea del allocate channel .

En nuestro caso el resultado será :

allocate channel 'dev_0' type 'sbt_tape'   
parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=pruebas_backup)';

Así pues, ya tenemos la línea que nos conectará con el software de backup, ahora tenemos que indicarle el nombre del fichero del cual queremos sacar el spfile, en nuestro caso hemos visto  que era  “pruebas_backup<pruebas_8085:795042273:1>.dbf” .

Con esto solamente nos queda hacer un bloque run de RMAN que nos recupere el spfile a una ubicación alternativa:

run {
allocate channel 'dev_0' type 'sbt_tape'
parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=pruebas_backup)';
restore  spfile to '/tmp/spfile_restaurado_cinta.ora' from 'pruebas_backup<pruebas_8085:795042273:1>.dbf';
 }

Nos conectaremos a  nuestra base de datos target  y catálogo y ejecutamos nuestro bloque run,con el resultado:

run {
allocate channel 'dev_0' type 'sbt_tape'
parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=pruebas_backup)';
restore  spfile to '/tmp/spfile_restaurado_cinta.ora' from 'pruebas_backup<pruebas_8085:795042273:1>.dbf';
}
3>allocatechannel 'dev_0' type 'sbt_tape'
parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=pruebas_backup)';
4>restorespfileto '/tmp/spfile_restaurado_cinta.ora' from 'pruebas_backup<pruebas_8085:795042273:1>.dbf';
5> }
allocatedchannel: dev_0
channel dev_0: sid=147 instance=pruebas1 devtype=SBT_TAPE
channel dev_0: Data Protector A.06.11/PHSS_40470/PHSS_40471/DPSOL_00391/DPLNX_
Startingrestore at 28/09/12
channel dev_0: autobackupfound: pruebas_backup<pruebas_8085:795042273:1>.dbf
[Normal] From: OB2BAR_SBT_CHANNEL@server.local "pruebas"  Time: 09/28/12 12:29:09
Starting OB2BAR Restore: server.local:pruebas_backup<pruebas_8085:795042273:1>.dbf "Oracle8"
[Normal] From: OB2BAR_SBT_CHANNEL@server.local "pruebas"  Time: 09/28/12 12:29:09
Starting OB2BAR Restore: server.local:pruebas_backup<pruebas_8085:795042273:1>.dbf "Oracle8"
[Normal] From: OB2BAR_SBT_CHANNEL@server.local "pruebas"  Time: 09/28/12 12:29:10
Completed OB2BAR Restore: server.local:pruebas_backup<pruebas_8085:795042273:1>.dbf "Oracle8"
[Normal] From: OB2BAR_SBT_CHANNEL@server.local "pruebas"  Time: 09/28/12 12:29:10
Completed OB2BAR Restore: server.local:pruebas_backup<pruebas_8085:795042273:1>.dbf "Oracle8"
channel dev_0: SPFILE restorefromautobackup complete
Finishedrestore at 28/09/12
releasedchannel: dev_0
Recovery Manager complete..

Ahora solamente tenemos que buscar el fichero /tmp/spfile_restaurado_cinta.ora donde tenemos la imagen del  spfile recuperada del backup.