Recuerdo a mi primer Jefe cuando empecé a desempeñar mi trabajo como DBA, lo que más me recalcó fue mi responsabilidad no sólo de poder realizar backups, sino de poder restaurar la información de mis backups. Esta responsabilidad es la que me gusta transmitir a todos los DBAs con los cuáles he interactuado.
De nada sirve tener un backup si no vas a poder recuperar la información.
A menudo me preguntan sobre las formas de respaldos de bases de datos oracle, y siempre respondo que lo mejor es el uso de RMAN. ¿Qué tan complejo puede ser RMAN?, la realidad es que ya configurado es muy sencillo, o en su forma más simple, sacar un respaldo es cuestión de un comando en 2 palabras:
C:\Documents and Settings\Hugo\Escritorio>rman target /
Recovery Manager : Release 10.2.0.3.0 - Production on Mié Jul 23 13:21:30 2008
Copyright (c) 1982, 2005, Oracle. All rights reserved.
conectado a la base de datos destino: ORCL (DBID=524232147)
RMAN> backup database;
¿Y el restore?
C:\Documents and Settings\Hugo\Escritorio>rman target /
Recovery Manager : Release 10.2.0.3.0 - Production on Mié Jul 23 13:22:35 2008
Copyright (c) 1982, 2005, Oracle. All rights reserved.
conectado a la base de datos destino: ORCL (DBID=524232147)
RMAN> shutdown abort;
RMAN> startup mount;
RMAN> restore database;
RMAN> recover database;
RMAN> alter database open;
Claro es sencillo porque la base de datos está en modo archive, a pesar que no estoy usando un catálogo de RMAN (que sería lo más recomendable), el restore es muy sencillo.
La realidad es que no siempre tenemos este esquema "utópico" a nivel base de datos. En alguna ocasión me tocó clonar la base de datos de un cliente con RMAN, tenían la base de datos en modo Archive, hicieron un hotbackup y al final del día, la información que se restauró sobre la instancia de desarrollo estaba corrupta.
Lo que sucedió fue lo siguiente:
El cliente estaba realizando algunas cargas de información sobre sus tablas.
El "DBA" para realizar las cargas más rápidas, decidió usar el método de "Direct Load" (que me pareció muy buena opción), sobre la tabla alterada con nologging (que me pareció una pésima opción).
A la hora de realizar el respaldo, e incluso los archives, no se tenía rastro de los insert sobre la tabla; se tenían rastros de los incrementos de extents y del movimiento del HWM. Para ejemplificar lo sucedido podemos hacer lo siguiente con dos sesiones:
Primera sesión en SQL*Plus:
SQL> alter table prueba nologging;
Tabla modificada.
SQL> begin
2 for a in 1..100
3 loop
4 insert /*+APPEND*/ into prueba (select substr(object_name,1,3),CREATED,substr(owner,1,3)from dba_objects);
5 commit;
6 dbms_lock.sleep(2);
7 end loop;
8 end;
9 /
y en una sesión de RMAN
C:\Documents and Settings\Hugo\Escritorio>rman target /
Recovery Manager : Release 10.2.0.3.0 - Production on Mié Jul 23 15:58:03 2008
Copyright (c) 1982, 2005, Oracle. All rights reserved.
conectado a la base de datos destino: HECG (DBID=524232147)
RMAN> backup database plus archivelog;
Con el comando plus archivelog, nos aseguramos de que todos los archives necesarios para restaurar este backup se incluyan, e incluso realiza un switch log al finalizar y respalda los archives nuevamente.
La sesión de SQL*Plus termina, el backup termina y empezamos con el restore:
Ahora, pensando en que tuviéramos una falla en ese momento y queremos recuperar, podemos empezar un restore a cierta fecha (que es cuando el respaldo terminó):
RMAN> shutdown abort;
RMAN> startup mount
instancia Oracle iniciada
Total del Área Global del Sistema 289406976 bytes
Fixed Size 1290184 bytes
Variable Size 272629816 bytes
Database Buffers 8388608 bytes
Redo Buffers 7098368 bytes
base de datos montada
RMAN> run {
2> set until time "to_date('23-07-2008 16:01:12','dd-mm-yyyy hh24:mi:ss')";
3> restore database;
4> recover database;
5> }
ejecutando el comando: SET until clause
Iniciando restore en 21-JUL-08
canal asignado: ORA_DISK_1
canal ORA_DISK_1: sid=156 devtype=DISK
canal ORA_DISK_1: iniciando restauraci+¦n del juego de copias de seguridad de archivos de datos
canal ORA_DISK_1: especificando archivo(s) de datos para restaurar del juego de copias de seguridad
restaurando el archivo de datos 00001 en C:\ORACLE\PRODUCT\ORADATA\ORCL\SYSTEM01.DBF
restaurando el archivo de datos 00002 en C:\ORACLE\PRODUCT\ORADATA\ORCL\UNDOTBS01.DBF
restaurando el archivo de datos 00003 en C:\ORACLE\PRODUCT\ORADATA\ORCL\SYSAUX01.DBF
restaurando el archivo de datos 00004 en C:\ORACLE\PRODUCT\ORADATA\ORCL\USERS01.DBF
restaurando el archivo de datos 00005 en C:\ORACLE\PRODUCT\ORADATA\ORCL\EXAMPLE01.DBF
restaurando el archivo de datos 00006 en C:\CRYPTO\ORCL\DATAFILE\O1_MF_ORCL_42K6N37T_.DBF
restaurando el archivo de datos 00007 en C:\PRUEBA01.DBF
canal ORA_DISK_1: leyendo desde la parte de copia de seguridad C:\ORACLE\PRODUCT\FLASH_RECOVERY_AREA\ORCL\BACKUPSET\2008_07_21\O1_MF_NNNDF_TAG20080721T093726_4897S8PY_.BKP
RMAN> alter database open resetlogs;
Se debe de utilizar un resetlogs ya se usó una recuperación incompleta.
Ahora entramos a SQL*Plus y ejecutamos un sql contra la tabla prueba
SQL> select count(1) from prueba;
select count(1) from prueba
*
ERROR en línea 1:
ORA-01578: bloque de datos ORACLE corrupto (archivo número 4, bloque número 10251)
ORA-01110: archivo de datos 4: 'C:\ORACLE\PRODUCT\ORADATA\ORCL\USERS01.DBF'
ORA-26040: Se ha cargado el bloque de datos utilizando la opción NOLOGGING
La tabla queda en un estado inconsistente porque el diccionario de datos (que sí genera redo) registró el crecimiento de bloques en la tabla, pero el objeto como tal no.
Hay algunas formas de recuperar algo de información si es que los índices estaban en modo logging, pero creanme... estamos en el peor caso de recuperación.
Dado todo lo anterior, creo que sólo se me ocurre una posibilidad para evitar esto durante un backup. Y es uno de los comandos que se suelen usar en dataguard
RMAN> run {
2> sql "alter database force logging";
3> backup database include current controlfile plus archivelog;
4> sql "alter database no force logging";
5> }
Iniciando backup en 23-JUL-08
log actual archivado
usando el canal ORA_DISK_1
canal ORA_DISK_1: iniciando juego de copias de seguridad de archive log
canal ORA_DISK_1: especificando archive log(s) en el juego de copias de seguridad
thread de archive log de entrada=1 secuencia=1 recid=122 marca=660762972
thread de archive log de entrada=1 secuencia=2 recid=123 marca=660763141
thread de archive log de entrada=1 secuencia=3 recid=124 marca=660763187
canal ORA_DISK_1: iniciando parte 1 en 22-JUL-08
...
canal ORA_DISK_1: especificando archivo(s) de datos en el juego de copias de seguridad
incluyendo el archivo de control actual en el juego de copias de seguridad
manejador de parte=C:\ORACLE\PRODUCT\FLASH_RECOVERY_AREA\ORCL\BACKUPSET\2008_07_23\O1_MF_NCNNF_TAG20080723T172009_48DQF74D_.BKP etiqueta=TAG20080723T172009 comentario=NONE
canal ORA_DISK_1: juego de copias de seguridad terminado, tiempo transcurrido: 00:00:06
backup terminado en 23-JUL-08
thread de archive log de entrada=1 secuencia=4 recid=125 marca=660763348
thread de archive log de entrada=1 secuencia=5 recid=126 marca=660763352
backup terminado en 23-JUL-08
En este caso estoy incluyendo el controlfile para hacer una especie de recuperación de desastre (que fue lo que hice realmente como clonado). Al mismo tiempo lancé el insert sobre la tabla prueba a través del ciclo y con la tabla en nologging.
Ya que es una recuperación de desastre, simularemos la pérdida del controlfile, y para poder recuperarlo, necesitamos el DBID de nuestra base de datos (se puede obtener conectándonos a rman o desde v$database).
Hay que tener en cuenta el nombre del archivo que guarda el controlfile para poder extraerlo, así como las secuencias de archives que se tienen registradas en el bacjup para restaurar y recuperar.
RMAN> shutdown abort;
instancia Oracle cerrada
RMAN> startup nomount;
conectado a la base de datos destino (no iniciada)
instancia Oracle iniciada
Total del Área Global del Sistema 289406976 bytes
Fixed Size 1290184 bytes
Variable Size 226492472 bytes
Database Buffers 54525952 bytes
Redo Buffers 7098368 bytes
RMAN> SET DBID 524232147;
ejecutando el comando: SET DBID
RMAN> RUN
2> {
3> RESTORE CONTROLFILE FROM 'C:\ORACLE\PRODUCT\FLASH_RECOVERY_AREA\ORCL\BACKUPSET\2008_07_23\O1_MF_NCNNF_TAG20080723T172009_48DQF74D_.BKP
4> }
Iniciando restore en 22-JUL-08
canal asignado: ORA_DISK_1
canal ORA_DISK_1: sid=156 devtype=DISK
canal ORA_DISK_1: restaurando archivo de control
canal ORA_DISK_1: restauración terminada, tiempo transcurrido: 00:00:04
archivo de salida=C:\ORACLE\PRODUCT\ORADATA\ORCL\CONTROL01.CTL
archivo de salida=C:\ORACLE\PRODUCT\ORADATA\ORCL\CONTROL02.CTL
archivo de salida=C:\ORACLE\PRODUCT\ORADATA\ORCL\CONTROL03.CTL
restore terminado en 22-JUL-08
RMAN> alter database mount;
base de datos montada
canal liberado: ORA_DISK_1
RMAN> RESTORE DATABASE UNTIL SEQUENCE 5;
RMAN> RECOVER DATABASE UNTIL SEQUENCE 5;
RMAN> ALTER DATABASE OPEN RESETLOGS;
Y ahora podemos conectarnos a SQL*Plus
SQL> select count(1) from prueba;
COUNT(1)
----------
9813490
Este ejemplo es muy raro que se encuentre en un ambiente productivo, y más que poder lanzar los comandos sql de "alter database force logging", como DBAs debemos de analizar que nuestros objetos tengan activado el logging. Habrá quien diga que los índices pueden ir en nologging, pero les preguntaría... ¿Cuánto tiempo tardarían en recrear los índices que no tuvieran activado el logging?
En algún otro Post pondré los scripts que se pueden usar para extraer datos de índices correspondientes a una tabla perdida.