Perform Various Recovery Operations Using Flashback Technology

1. Documentación en Tahiti -> Masters Book List -> Backup and Recovery User’s Guide -> 7 Using Flashback Database and Restore Points

Documentación en Tahiti -> Masters Book List -> Backup and Recovery User’s Guide -> 18 Performing Flashback and Database Point-in-Time Recovery

Documentación en Tahiti -> Masters Book List -> Advanced Application Developer’s Guide -> 12 Using Oracle Flashback Technology

2. Volvemos a reparasar las tecnologías de Flashback que proporciona Oracle.

Flashback Technologies

3. Flashback Drop nos permite recuperar tablas borradas (DROP TABLE) practicamente de forma inmmediata. Si tenemos activada la papelera de reciclaje (RECYCLEBIN), cuando borramos un objeto no se libera el espacio automática, sino que se actualiza el diccionario de datos indicando que se ha borrado la tabla y el espacio será reutilizado cuando sea necesario. Los primeros objetos que se purgarán serán los primeros que se hayan eliminado.

-- Creamos un tabla para realizar las pruebas
CREATE TABLE TEST_EMPLOYEES TABLESPACE USERS AS SELECT * FROM HR.EMPLOYEES;

-- Borramos la tabla
DROP TABLE TEST_EMPLOYEES;

-- Consultamos la papelera de reciclaje para ver el objeto recién borrado
-- RECYCLEBIN = USER_RECYCLEBIN (También podríamos usar DBA_RECYCLEBIN)
SELECT * FROM RECYCLEBIN;

-- Recuperamos la tabla
FLASHBACK TABLE TEST_EMPLOYEES TO BEFORE DROP;

-- Consultamos la tabla
SELECT COUNT(*) FROM TEST_EMPLOYEES;

Cuando recuperamos la tabla, tenemos la opción de recuperarlo con un nombre distinto.

-- Borramos la tabla
DROP TABLE TEST_EMPLOYEES;

-- Recuperamos la tabla
FLASHBACK TABLE TEST_EMPLOYEES TO BEFORE DROP RENAME TO TEST_EMPLOYEES_OLD;

-- La renombramos con el nombre original
RENAME TEST_EMPLOYEES_OLD TO TEST_EMPLOYEES;

Una problemática que puede surgir es que hayamos borrado varias tablas con el mismo nombre y no sepamos cuál debemos recuperar. El procedimiento es el siguiente.

-- Borramos la tabla anterior
DROP TABLE TEST_EMPLOYEES;

-- Creamos una segunda tabla con el mismo nombre pero si registros
CREATE TABLE TEST_EMPLOYEES TABLESPACE USERS AS SELECT * FROM HR.EMPLOYEES WHERE 1=2;

-- Borramos la segunda tabla
DROP TABLE TEST_EMPLOYEES;

-- Comprobamos el contenido de la RECYCLEBIN y vemos que tenemos dos tablas "TEST_EMPLOYEES"
SELECT * FROM RECYCLEBIN WHERE ORIGINAL_NAME='TEST_EMPLOYEES';

-- Podemos consultar datos de las tablas dentro de la propia RECYCLEBIN
-- Consultamos el número de filas de las dos
SELECT COUNT(*) FROM "BIN$3qdKDJCYSGngQ2QBqMBRgw==$0";
SELECT COUNT(*) FROM "BIN$3qdKDJCdSGngQ2QBqMBRgw==$0";

-- La primera es la que tiene 107 registros luego es la correcta
-- Recuperar la primera tabla de la RECYCLEBIN
FLASHBACK TABLE "BIN$3qdKDJCYSGngQ2QBqMBRgw==$0" TO BEFORE DROP;

Cuando borramos un objeto, podemos indicar que no se registre en la papelera de reciclaje. También tenemos la opción de purgar toda la papelera de reciclaje entre otras.

-- Borramos la tabla TEST_EMPLOYEES de manera que no se pueda recuperar con Flasback DROP
DROP TABLE TEST_EMPLOYEES PURGE;

-- Limpiamos la papelera de reciclaje
PURGE RECYCLEBIN;

-- Purgamos el contenido de la papelera de un tablespace
PURGE TABLESPACE USERS;

-- Purgamos los objetos de un usuario dentro de un tablesapce
PURGE TABLESPACE USERS USER HR;

-- Purgamos el contenido de todas las papeleras de reciclaje
PURGE DBA_RECYCLEBIN;

Una consideración a tener en cuenta que es que cuando recuperamos un objeto con dependencias de la RECYCLEBIN, los objetos dependientes no recuperan el nombre original, así que tenemos que hacerlo “manualmente”.

-- Creamos una tabla de ejemplo
CREATE TABLE TEST_EMPLOYEES TABLESPACE USERS AS SELECT * FROM HR.EMPLOYEES;

-- Creamos un índice
CREATE INDEX IDX_EMPLOYEES ON TEST_EMPLOYEES(EMPLOYEE_ID) TABLESPACE USERS;

-- Borramos la tabla
DROP TABLE TEST_EMPLOYEES;

-- IMPORTANTE! Consultamos los nombres que tienen los objetos borrados
SELECT * FROM RECYCLEBIN;

-- Recuperamos la tabla
FLASHBACK TABLE TEST_EMPLOYEES TO BEFORE DROP;

-- Consulta el nombre del índice
SELECT INDEX_NAME FROM DBA_INDEXES WHERE TABLE_NAME='TEST_EMPLOYEES';

-- Renombramos el índice
ALTER INDEX "BIN$3qdKDJCiSGngQ2QBqMBRgw==$0" RENAME TO "IDX_EMPLOYEES";

-- Limpiamos el entorno
DROP TABLE TEST_EMPLOYEES PURGE;

4. Flashback Database es una funcionalidad muy potente para llevar la BD atras en el tiempo. Antes de todo vamos a crear un punto de restauración, que nos permite “etiquetar” un punto en el tiempo para poder llevar la BD al principio de todos los ejercicios.

-- La clausula GUARANTEE indica que se guarden los Flashback Logs más alla de la Retención
CREATE RESTORE POINT BEFORE_EXERCISES GUARANTEE FLASHBACK DATABASE;

-- Vemos los puntos de restauración de la BD
SELECT * FROM V$RESTORE_POINT;

Probamos a borrar todo un esquema y recuperaremos la BD.

-- Borramos el esquema HR
DROP USER HR CASCADE;

-- Realizamos un FLASHBACK completo de la BD al punto de restauración
-- Tenemos que tener la BD en estado MOUNT
STARTUP MOUNT FORCE
FLASHBACK DATABASE TO RESTORE POINT BEFORE_EXERCISES;

-- Abrimos la BD
ALTER DATABASE OPEN RESETLOGS;

Hagamos un ejercicio más complicado para ver como podemos ir hacia detrás y hacia delante con FLASHBACK y RECOVER.

-- Creamos un segundo punto de restauración
CREATE RESTORE POINT AFTER_EXERCISES GUARANTEE FLASHBACK DATABASE;

-- Consultamos los puntos de restauración para obtener los SCN
SELECT * FROM V$RESTORE_POINT;

-- En mi caso esta es la salida
--        SCN NAME
-- ---------- ------------------
--    3124322 BEFORE_EXERCISES
--    3124333 AFTER_EXERCISES
--
-- Llevamos la BD al 2º punto (AFTER EXERCISES)
STARTUP FORCE MOUNT
FLASHBACK DATABASE TO RESTORE POINT AFTER_EXERCISES;

-- Abrimos la BD en modo lectura para comprobar si nos interesate este punto
ALTER DATABASE OPEN READ ONLY;

-- Supongamos que no nos interesa y queremos volver al punto anterior
-- Pero esta vez, en vez de usar la etiquita "BEFORE_EXERCISES", usaremos el SCN
STARTUP FORCE MOUNT
FLASHBACK DATABASE TO SCN 3124322;

-- Si decidimos que no queremos ir marcha atrás, podemos hacer un RECOVER
RECOVER DATABASE;

-- Levantamos la BD (Fijaros que no usamos RESETLOGS)
ALTER DATABASE OPEN;

-- Borramos los puntos de restauración.
DROP RESTORE POINT BEFORE_EXERCISES;
DROP RESTORE POINT AFTER_EXERCISES;

No lo haremos pero tenemos estas otras opciones para hacer FLASHBACK.

-- Flashack Database a un tiempo concreto
FLASHBACK DATABASE TO TIMESTAMP (TO_TIMESTAMP('2013/06/08 19:14:35','YYYY/MM/DD HH24:MI:SS'));

-- Flashback hasta una secuencia (dentro de RMAN)
FLASHBACK DATABASE TO SEQUENCE=1;

Si la BD tiene que recuperar muchos bloques y la operación se alarga en el tiempo, podemos monitorizar el progreso con la siguiente consulta (V$SESSION_LONGOPS)

SELECT SOFAR, TOTALWORK, UNITS FROM V$SESSION_LONGOPS WHERE OPNAME='Flashback Database';

5. En ocasiones nos puede interesar hacer marchá atrás de una serie de transacciones sobre una tabla. No es una operación frecuente, pero es interesante saber cómo se puede hacer. Para ello vamos a utilizar las funcionalidades de Flasbach Transaction Query y Flashback Version Query.

-- Creamos una tabla de ejemplo
CREATE TABLE TEST_EMPLOYEES TABLESPACE USERS AS SELECT * FROM HR.EMPLOYEES;

-- Borramos dos registros de la tabla TEST_EMPLOYEES
DELETE FROM TEST_EMPLOYEES WHERE EMPLOYEE_ID=100;
COMMIT;
DELETE FROM TEST_EMPLOYEES WHERE EMPLOYEE_ID=101;
COMMIT;

-- Consultamos las transacciones realizados a partir de la vista FLASHBACK_TRANSACTION_QUERY
SELECT * FROM FLASHBACK_TRANSACTION_QUERY WHERE TABLE_NAME='TEST_EMPLOYEES';

-- También podemos obtener información de las transacciones realizadas a través de las pseudocolumnas de Version Query
-- Obtenemos sólo aquellos cambios que provegan de un DELETE (VERSIONS_OPERATION='D')
SELECT VERSIONS_XID, VERSIONS_STARTSCN, VERSIONS_ENDSCN, VERSIONS_OPERATION, EMPLOYEE_ID, FIRST_NAME
FROM TEST_EMPLOYEES VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE
WHERE VERSIONS_OPERATION='D'
ORDER BY 2;

-- A través de las Id de las transacciones (XID), podemos hacer marcha atrás de los DELETEs realizados
-- Para ello utilizamos el procedimiento TRANSACTION_BACKOUT del paquete DBMS_FLASHBACK
EXEC DBMS_FLASHBACK.TRANSACTION_BACKOUT(2, xid_array('01000C004A030000','09001F0020030000'));

-- Consultamos que volvemos a tener los registros
SELECT EMPLOYEE_ID, FIRST_NAME FROM TEST_EMPLOYEES WHERE EMPLOYEE_ID IN (100,101);

-- Limpiamos el entorno
DROP TABLE TEST_EMPLOYEES PURGE;