Recover de tabela a partir de backup RMAN em Oracle 12C

12 MHG quadra thiago morgado2 Recover de tabela a partir de backup RMAN em Oracle 12CUma das features mais aguardadas da versão 12C do Oracle era justamente a possibilidade de fazer um recover de tabela a partir do backup de RMAN. Neste artigo, vamos demonstrar o funcionamento do processo, bem como todas as implicações e requisitos necessários para o sucesso do procedimento.
Até agora, sempre que fosse necessário realizar um recover de uma ou mais tabelas, mas não de toda a base, seria necessário utilizar o backup lógico (exp ou datapump) para essa atividade. Esse procedimento possui a limitação de somente ser possível restaurar a tabela no ponto em que foi realizado o backup da mesma. Se o backup foi realizado durante a madrugada, mas um registro foi indevidamente alterado às 10h00 por exemplo, um recover dessa tabela só traria os registros, obviamente, até a madrugada, excluindo tudo que foi feito durante o período até às 10h00.
Outra possibilidade era restaurar o backup físico, mas isso implica em voltar toda a base, incluindo todos os schemas existentes, e não somente a tabela que se deseja restaurar.
Uma terceira possibilidade era o de duplicar o banco a partir do backup físico, realizar um export lógico da tabela, e restaurá-la na base de produção, ou importar numa base de testes para comparação.
O que a nova feature de recover de tabelas via RMAN, no Oracle 12C faz, é automatizar essa última opção em um processo único e totalmente gerenciado pelo RMAN. O processo é exatamente esse; 1)Faz um duplicate a partir do backup físico; 2) Exporta a tabela ou conjunto de tabelas via datapump; 3) Importa a(s) tabela(s) na base, podendo realizar um remap_table; 4) Exclui a instância clonada, datafiles, controlfiles, arquivos de parâmetro e o dump gerado no processo.
Confesso que quando entendi como o processo funcionava, fiquei um pouco decepcionado. Esperava que, ao realizar um processo de restore da tabela, a “mágica” ocorresse diretamente nos blocos do backup, e não um conjunto de ações que, em seu núcleo de execução, não altera em nada a arquitetura do RMAN como a conhecemos hoje. No entanto, ao executar o processo em meus testes, verifiquei que o processo é bem interessante, e realmente agiliza e facilita muito o trabalho, tirando várias preocupações que o DBA teria ao realizar manualmente todas as etapas mencionadas.
Mas deixemos de conversa de “grupinho de DBA na hora do café”, e vamos demonstrar como a coisa toda funciona.

Em uma instância Oracle 12C Enterprise, tenho uma tablespace chamada TESTE_DATA, que foi criada com 4 datafiles. Tenho um owner chamado TESTE, que possui privilégio de armazenamento ilimitado nesta tablespace. Criei uma tabela para esse owner chamada TABELA_TESTE, fazendo um “CREATE TABLE ….. AS SELECT *” da view DBA_OBJECTS, do SYS. Queria que a tabela tivesse um volume de dados suficiente para que as extensões da tabela ocupassem mais do que 1 datafile, mas não todos. O motivo desta particularidade, compartilharei mais para frente. Como podemos ver a seguir, a tabela se extendeu por 3 dos 4 datafiles da tablespace TESTE_DATA. Mais especificamente nos datafiles de ID 5,7 e 8:

SQL> SELECT distinct(FILE_ID), SEGMENT_NAME, TABLESPACE_NAME from dba_extents where owner='TESTE';

   FILE_ID SEGMENT_NAME         TABLESPACE_NAME
---------- -------------------- ------------------------------
         5 TABELA_TESTE         TESTE_DATA
         7 TABELA_TESTE         TESTE_DATA
         8 TABELA_TESTE         TESTE_DATA

Para fins de demonstração e análise do processo, criei uma nova tablespace, que chamei de CONTROLE_DATA, com 1 datafile:

SQL> create tablespace CONTROLE_DATA datafile '+DGDATA' size 100m;

Tablespace created.

O próximo passo é fazer um backup do banco via RMAN, pois no meu caso, por se tratar de uma instância que utilizo para testes, não possuo nenhuma rotina de backup físico, e portanto, nenhum conjunto de backups.

RMAN> backup database;

Starting backup at 20-FEB-14
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
input datafile file number=00001 name=+DGDATA/DBPROD/DATAFILE/system.271.839926541
input datafile file number=00003 name=+DGDATA/DBPROD/DATAFILE/sysaux.270.839926235
input datafile file number=00009 name=+DGDATA/DBPROD/DATAFILE/controle_data.293.840013943
input datafile file number=00004 name=+DGDATA/DBPROD/DATAFILE/undotbs1.273.839926845
input datafile file number=00002 name=+DGDATA/DBPROD/DATAFILE/teste.287.839954981
input datafile file number=00005 name=+DGDATA/DBPROD/DATAFILE/teste_data.288.840013323
input datafile file number=00007 name=+DGDATA/DBPROD/DATAFILE/teste_data.291.840013325
input datafile file number=00008 name=+DGDATA/DBPROD/DATAFILE/teste_data.292.840013327
input datafile file number=00010 name=+DGDATA/DBPROD/DATAFILE/teste_data.285.840020271
input datafile file number=00006 name=+DGDATA/DBPROD/DATAFILE/users.272.839926837
channel ORA_DISK_1: starting piece 1 at 20-FEB-14
channel ORA_DISK_1: finished piece 1 at 20-FEB-14
piece handle=+DGDATA/DBPROD/BACKUPSET/2014_02_20/nnndf0_tag20140220t105806_0.284.840020291 tag=TAG20140220T105806 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:25
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
including current control file in backup set
including current SPFILE in backup set
channel ORA_DISK_1: starting piece 1 at 20-FEB-14
channel ORA_DISK_1: finished piece 1 at 20-FEB-14
piece handle=+DGDATA/DBPROD/BACKUPSET/2014_02_20/ncsnf0_tag20140220t105806_0.296.840020379 tag=TAG20140220T105806 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:07
Finished backup at 20-FEB-14

De volta à instância, habilitei o time para melhor entendimento do processo. Fiz um “select count” na tabela, seguido da exclusão de mais da metade dos registros da mesma, simulando um erro, acidental ou não, que demande um recover desta tabela para um momento antes da exclusão dos registros:

11:00:08 SQL> select count(1) from TESTE.TABELA_TESTE;

  COUNT(1)
----------
     90829

11:00:43 SQL> DELETE from TESTE.TABELA_TESTE where OWNER='SYS';

41785 rows deleted.

11:00:52 SQL> commit;

Commit complete.

11:00:56 SQL> select count(1) from TESTE.TABELA_TESTE;

  COUNT(1)
----------
     49044

Conectado ao RMAN, emito o comando RECOVER TABLE, informando no parâmetro UNTIL TIME o momento exato em que desejo que a tabela seja recuperada. Informo na string a área que o RMAN utilizará para criação da instância temporária, e também a área de geração do arquivo de exportação do datapump:

[oracle@orazety12c ~]$ rman target /

Recovery Manager: Release 12.1.0.1.0 - Production on Thu Feb 20 11:02:13 2014

Copyright (c) 1982, 2013, Oracle and/or its affiliates.  All rights reserved.

connected to target database: DBPROD (DBID=939006448)

RMAN> RECOVER TABLE TESTE.tabela_teste UNTIL TIME "to_date('2014-02-20:11:00:00', 'yyyy-mm-dd:hh24:mi:ss')" AUXILIARY DESTINATION '/orateste/oradata' DATAPUMP DESTINATION '/orateste/oradata' DUMP FILE 'TABELAS_RECOVER.dat';

Starting recover at 20-FEB-14
using target database control file instead of recovery catalog
current log archived
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of recover command at 02/20/2014 11:02:25
RMAN-05063: Cannot recover specified tables
RMAN-05112: table "TESTE"."TABELA_TESTE" already exists

RMAN>

Opa, erro de tabela existente. Sim, o RMAN não exclui a tabela para substituí-la pelo recover. Se o DBA deseja recuperar a tabela diretamente na produção, deve excluí-la antes do processo:

SQL> DROP table TESTE.TABELA_TESTE;
Table dropped.

Agora sim, podemos executar o procedimento no RMAN com sucesso:

RMAN> RECOVER TABLE TESTE.tabela_teste UNTIL TIME "to_date('2014-02-20:11:00:00', 'yyyy-mm-dd:hh24:mi:ss')" AUXILIARY DESTINATION '/orateste/oradata' DATAPUMP DESTINATION '/orateste/oradata' DUMP FILE 'TABELAS_RECOVER.dat';

Starting recover at 20-FEB-14
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=66 device type=DISK
RMAN-05026: WARNING: presuming following set of tablespaces applies to specified Point-in-Time

List of tablespaces expected to have UNDO segments
Tablespace SYSTEM
Tablespace UNDOTBS1

Creating automatic instance, with SID='yvDv'

initialization parameters used for automatic instance:
db_name=DBPROD
db_unique_name=yvDv_pitr_DBPROD
compatible=12.1.0.0.0
db_block_size=8192
db_files=200
sga_target=1G
processes=80
diagnostic_dest=/u01/app/oracle
db_create_file_dest=/orateste/oradata
log_archive_dest_1='location=/orateste/oradata'
#No auxiliary parameter file used


starting up automatic instance DBPROD

Oracle instance started

Total System Global Area    1068937216 bytes

Fixed Size                     2296576 bytes
Variable Size                281019648 bytes
Database Buffers             780140544 bytes
Redo Buffers                   5480448 bytes
Automatic instance created

contents of Memory Script:
{
# set requested point in time
set until  time "to_date('2014-02-20:11:00:00', 'yyyy-mm-dd:hh24:mi:ss')";
# restore the controlfile
restore clone controlfile;
# mount the controlfile
sql clone 'alter database mount clone database';
# archive current online log
sql 'alter system archive log current';
}
executing Memory Script

executing command: SET until clause

Starting restore at 20-FEB-14
allocated channel: ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: SID=7 device type=DISK

channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: restoring control file
channel ORA_AUX_DISK_1: reading from backup piece +DGDATA/DBPROD/BACKUPSET/2014_02_20/ncsnf0_tag20140220t105806_0.296.840020379
channel ORA_AUX_DISK_1: piece handle=+DGDATA/DBPROD/BACKUPSET/2014_02_20/ncsnf0_tag20140220t105806_0.296.840020379 tag=TAG20140220T105806
channel ORA_AUX_DISK_1: restored backup piece 1
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:08
output file name=/orateste/oradata/DBPROD/controlfile/o1_mf_9jd390ph_.ctl
Finished restore at 20-FEB-14

sql statement: alter database mount clone database

sql statement: alter system archive log current

contents of Memory Script:
{
# set requested point in time
set until  time "to_date('2014-02-20:11:00:00', 'yyyy-mm-dd:hh24:mi:ss')";
# set destinations for recovery set and auxiliary set datafiles
set newname for clone datafile  1 to new;
set newname for clone datafile  4 to new;
set newname for clone datafile  3 to new;
set newname for clone tempfile  1 to new;
# switch all tempfiles
switch clone tempfile all;
# restore the tablespaces in the recovery set and the auxiliary set
restore clone datafile  1, 4, 3;
switch clone datafile all;
}
executing Memory Script

executing command: SET until clause

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

renamed tempfile 1 to /orateste/oradata/DBPROD/datafile/o1_mf_temp_%u_.tmp in control file

Starting restore at 20-FEB-14
using channel ORA_AUX_DISK_1

channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00003 to /orateste/oradata/DBPROD/datafile/o1_mf_sysaux_%u_.dbf
channel ORA_AUX_DISK_1: reading from backup piece +DGDATA/DBPROD/BACKUPSET/2014_02_20/nnndf0_tag20140220t091315_0.294.840014001
channel ORA_AUX_DISK_1: piece handle=+DGDATA/DBPROD/BACKUPSET/2014_02_20/nnndf0_tag20140220t091315_0.294.840014001 tag=TAG20140220T091315
channel ORA_AUX_DISK_1: restored backup piece 1
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:46
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00001 to /orateste/oradata/DBPROD/datafile/o1_mf_system_%u_.dbf
channel ORA_AUX_DISK_1: restoring datafile 00004 to /orateste/oradata/DBPROD/datafile/o1_mf_undotbs1_%u_.dbf
channel ORA_AUX_DISK_1: reading from backup piece +DGDATA/DBPROD/BACKUPSET/2014_02_20/nnndf0_tag20140220t105806_0.284.840020291
channel ORA_AUX_DISK_1: piece handle=+DGDATA/DBPROD/BACKUPSET/2014_02_20/nnndf0_tag20140220t105806_0.284.840020291 tag=TAG20140220T105806
channel ORA_AUX_DISK_1: restored backup piece 1
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:46
Finished restore at 20-FEB-14

datafile 1 switched to datafile copy
input datafile copy RECID=4 STAMP=840021208 file name=/orateste/oradata/DBPROD/datafile/o1_mf_system_9jd3bvjj_.dbf
datafile 4 switched to datafile copy
input datafile copy RECID=5 STAMP=840021208 file name=/orateste/oradata/DBPROD/datafile/o1_mf_undotbs1_9jd3bvjo_.dbf
datafile 3 switched to datafile copy
input datafile copy RECID=6 STAMP=840021208 file name=/orateste/oradata/DBPROD/datafile/o1_mf_sysaux_9jd39f2q_.dbf

contents of Memory Script:
{
# set requested point in time
set until  time "to_date('2014-02-20:11:00:00', 'yyyy-mm-dd:hh24:mi:ss')";
# online the datafiles restored or switched
sql clone "alter database datafile  1 online";
sql clone "alter database datafile  4 online";
sql clone "alter database datafile  3 online";
# recover and open database read only
recover clone database tablespace  "SYSTEM", "UNDOTBS1", "SYSAUX";
sql clone 'alter database open read only';
}
executing Memory Script

executing command: SET until clause

sql statement: alter database datafile  1 online

sql statement: alter database datafile  4 online

sql statement: alter database datafile  3 online

Starting recover at 20-FEB-14
using channel ORA_AUX_DISK_1

starting media recovery

archived log for thread 1 with sequence 12 is already on disk as file /orateste/oradata/arch_1_12_839926904.arc
archived log for thread 1 with sequence 13 is already on disk as file /orateste/oradata/arch_1_13_839926904.arc
archived log for thread 1 with sequence 14 is already on disk as file /orateste/oradata/arch_1_14_839926904.arc
archived log file name=/orateste/oradata/arch_1_12_839926904.arc thread=1 sequence=12
archived log file name=/orateste/oradata/arch_1_13_839926904.arc thread=1 sequence=13
archived log file name=/orateste/oradata/arch_1_14_839926904.arc thread=1 sequence=14
media recovery complete, elapsed time: 00:00:04
Finished recover at 20-FEB-14

sql statement: alter database open read only

contents of Memory Script:
{
   sql clone "create spfile from memory";
   shutdown clone immediate;
   startup clone nomount;
   sql clone "alter system set  control_files =
  ''/orateste/oradata/DBPROD/controlfile/o1_mf_9jd390ph_.ctl'' comment=
 ''RMAN set'' scope=spfile";
   shutdown clone immediate;
   startup clone nomount;
# mount database
sql clone 'alter database mount clone database';
}
executing Memory Script

sql statement: create spfile from memory

database closed
database dismounted
Oracle instance shut down

connected to auxiliary database (not started)
Oracle instance started

Total System Global Area    1068937216 bytes

Fixed Size                     2296576 bytes
Variable Size                285213952 bytes
Database Buffers             775946240 bytes
Redo Buffers                   5480448 bytes

sql statement: alter system set  control_files =   ''/orateste/oradata/DBPROD/controlfile/o1_mf_9jd390ph_.ctl'' comment= ''RMAN set'' scope=spfile

Oracle instance shut down

connected to auxiliary database (not started)
Oracle instance started

Total System Global Area    1068937216 bytes

Fixed Size                     2296576 bytes
Variable Size                285213952 bytes
Database Buffers             775946240 bytes
Redo Buffers                   5480448 bytes

sql statement: alter database mount clone database

contents of Memory Script:
{
# set requested point in time
set until  time "to_date('2014-02-20:11:00:00', 'yyyy-mm-dd:hh24:mi:ss')";
# set destinations for recovery set and auxiliary set datafiles
set newname for datafile  5 to new;
set newname for datafile  7 to new;
set newname for datafile  8 to new;
set newname for datafile  10 to new;
# restore the tablespaces in the recovery set and the auxiliary set
restore clone datafile  5, 7, 8, 10;
switch clone datafile all;
}
executing Memory Script

executing command: SET until clause

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

Starting restore at 20-FEB-14
allocated channel: ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: SID=27 device type=DISK

channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00005 to /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_%u_.dbf
channel ORA_AUX_DISK_1: restoring datafile 00007 to /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_%u_.dbf
channel ORA_AUX_DISK_1: restoring datafile 00008 to /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_%u_.dbf
channel ORA_AUX_DISK_1: restoring datafile 00010 to /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_%u_.dbf
channel ORA_AUX_DISK_1: reading from backup piece +DGDATA/DBPROD/BACKUPSET/2014_02_20/nnndf0_tag20140220t105806_0.284.840020291
channel ORA_AUX_DISK_1: piece handle=+DGDATA/DBPROD/BACKUPSET/2014_02_20/nnndf0_tag20140220t105806_0.284.840020291 tag=TAG20140220T105806
channel ORA_AUX_DISK_1: restored backup piece 1
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:03
Finished restore at 20-FEB-14

datafile 5 switched to datafile copy
input datafile copy RECID=11 STAMP=840021295 file name=/orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw5x_.dbf
datafile 7 switched to datafile copy
input datafile copy RECID=12 STAMP=840021295 file name=/orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw67_.dbf
datafile 8 switched to datafile copy
input datafile copy RECID=13 STAMP=840021295 file name=/orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw6l_.dbf
datafile 10 switched to datafile copy
input datafile copy RECID=14 STAMP=840021295 file name=/orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw8y_.dbf

contents of Memory Script:
{
# set requested point in time
set until  time "to_date('2014-02-20:11:00:00', 'yyyy-mm-dd:hh24:mi:ss')";
# online the datafiles restored or switched
sql clone "alter database datafile  5 online";
sql clone "alter database datafile  7 online";
sql clone "alter database datafile  8 online";
sql clone "alter database datafile  10 online";
# recover and open resetlogs
recover clone database tablespace  "TESTE_DATA", "SYSTEM", "UNDOTBS1", "SYSAUX" delete archivelog;
alter clone database open resetlogs;
}
executing Memory Script

executing command: SET until clause

sql statement: alter database datafile  5 online

sql statement: alter database datafile  7 online

sql statement: alter database datafile  8 online

sql statement: alter database datafile  10 online

Starting recover at 20-FEB-14
using channel ORA_AUX_DISK_1

starting media recovery

archived log for thread 1 with sequence 14 is already on disk as file /orateste/oradata/arch_1_14_839926904.arc
archived log file name=/orateste/oradata/arch_1_14_839926904.arc thread=1 sequence=14
media recovery complete, elapsed time: 00:00:01
Finished recover at 20-FEB-14

database opened

contents of Memory Script:
{
# create directory for datapump import
sql "create or replace directory TSPITR_DIROBJ_DPDIR as ''
/orateste/oradata''";
# create directory for datapump export
sql clone "create or replace directory TSPITR_DIROBJ_DPDIR as ''
/orateste/oradata''";
}
executing Memory Script

sql statement: create or replace directory TSPITR_DIROBJ_DPDIR as ''/orateste/oradata''

sql statement: create or replace directory TSPITR_DIROBJ_DPDIR as ''/orateste/oradata''

Performing export of tables...
   EXPDP> Starting "SYS"."TSPITR_EXP_yvDv_wCFg":
   EXPDP> Estimate in progress using BLOCKS method...
   EXPDP> Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
   EXPDP> Total estimation using BLOCKS method: 13 MB
   EXPDP> Processing object type TABLE_EXPORT/TABLE/TABLE
   EXPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
   EXPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
   EXPDP> . . exported "TESTE"."TABELA_TESTE"                      10.36 MB   90829 rows
   EXPDP> Master table "SYS"."TSPITR_EXP_yvDv_wCFg" successfully loaded/unloaded
   EXPDP> ******************************************************************************
   EXPDP> Dump file set for SYS.TSPITR_EXP_yvDv_wCFg is:
   EXPDP>   /orateste/oradata/TABELAS_RECOVER.dat
   EXPDP> Job "SYS"."TSPITR_EXP_yvDv_wCFg" successfully completed at Thu Feb 20 11:18:19 2014 elapsed 0 00:02:01
Export completed


contents of Memory Script:
{
# shutdown clone before import
shutdown clone abort
}
executing Memory Script

Oracle instance shut down

Performing import of tables...
   IMPDP> Master table "SYS"."TSPITR_IMP_yvDv_jrcp" successfully loaded/unloaded
   IMPDP> Starting "SYS"."TSPITR_IMP_yvDv_jrcp":
   IMPDP> Processing object type TABLE_EXPORT/TABLE/TABLE
   IMPDP> Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
   IMPDP> . . imported "TESTE"."TABELA_TESTE"                      10.36 MB   90829 rows
   IMPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
   IMPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
   IMPDP> Job "SYS"."TSPITR_IMP_yvDv_jrcp" successfully completed at Thu Feb 20 11:20:33 2014 elapsed 0 00:01:47
Import completed


Removing automatic instance
Automatic instance removed
auxiliary instance file /orateste/oradata/DBPROD/datafile/o1_mf_temp_9jd3dqym_.tmp deleted
auxiliary instance file /orateste/oradata/YVDV_PITR_DBPROD/onlinelog/o1_mf_3_9jd3hdz0_.log deleted
auxiliary instance file /orateste/oradata/YVDV_PITR_DBPROD/onlinelog/o1_mf_2_9jd3hb34_.log deleted
auxiliary instance file /orateste/oradata/YVDV_PITR_DBPROD/onlinelog/o1_mf_1_9jd3h6l9_.log deleted
auxiliary instance file /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw8y_.dbf deleted
auxiliary instance file /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw6l_.dbf deleted
auxiliary instance file /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw67_.dbf deleted
auxiliary instance file /orateste/oradata/YVDV_PITR_DBPROD/datafile/o1_mf_teste_da_9jd3gw5x_.dbf deleted
auxiliary instance file /orateste/oradata/DBPROD/datafile/o1_mf_sysaux_9jd39f2q_.dbf deleted
auxiliary instance file /orateste/oradata/DBPROD/datafile/o1_mf_undotbs1_9jd3bvjo_.dbf deleted
auxiliary instance file /orateste/oradata/DBPROD/datafile/o1_mf_system_9jd3bvjj_.dbf deleted
auxiliary instance file /orateste/oradata/DBPROD/controlfile/o1_mf_9jd390ph_.ctl deleted
auxiliary instance file TABELAS_RECOVER.dat deleted
Finished recover at 20-FEB-14

Veja na leitura deste extenso log, todas as etapas que mencionei no inicio deste artigo, totalmente evidenciadas. Perceba que o RMAN cria uma instância DUMMY utilizando apenas os datafiles da SYSTEM, SYSAUX, UNDO e os datafiles da tablespace TESTE_DATA, além de criar uma tablespace temporária. Aqui eu chamo atenção para o fato dos 4 datafiles da tablespace TESTE_DATA, que foram recuperados na base, mesmo a tabela só ocupando 3 deles. Outro detalhe é a tablespace CONTROLE_DATA e USERS. Nenhum datafile dessas tablespaces foi recuperado, poupando espaço utilizado no processo. Destas 2 constatações, tiramos 2 lições importantes na utilização deste recurso; A primeira é o fato de que, como o duplicate é realizado apenas da tablespace envolvida mais as de sistema, o espaço demandado é otimizado, evitando que,por exemplo, em uma instância com 10 tablespaces de 1TB no total, precisemos alocar 1TB de espaço apenas para recuperar 1 tabela. A segunda questão é que, apesar de só duplicar as tablespaces de sistema mais a tablespace da tabela, o processo recupera toda a tablespace. Isso quer dizer que, se a tabela possui 5mb, mas a tablespace possui 100GB, teremos que disponibilizar esse tamanho, mais o espaço utilizado para as tablespaces de sistema, a tablespace temporária e área para os archivelogs utilizados. Essa questão é muito importante, e deve ser levada em conta no processo de recover da tabela.
Ao final do processo, no qual o RMAN duplicou a base, acionou o DATAPUMP para exportar a tabela e depois para importar na instância, os arquivos utilizados são excluídos.
Antes do processo ser executado, monitorei os processos de pmon no servidor, e a área do disco /orateste, que utilizei para ser realizada a duplicação e exportação:

oracle    1318  1315  0 11:23 pts/0    00:00:00 grep ora_pm
oracle    3390     1  0 08:44 ?        00:00:04 ora_pmon_dbprod
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
                       25G   17G  7.5G  69% /
/dev/sda1              99M   24M   71M  25% /boot
tmpfs                 1.3G  626M  619M  51% /dev/shm
/dev/sdb1              22G  265M   21G   2% /orateste

Durante o processo, o cenário era o seguinte:

oracle     376   372  0 11:15 pts/0    00:00:00 grep ora_pm
oracle    3390     1  0 08:44 ?        00:00:04 ora_pmon_dbprod
oracle   32307     1  0 11:14 ?        00:00:00 ora_pmon_yvDv
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
                       25G   17G  7.5G  69% /
/dev/sda1              99M   24M   71M  25% /boot
tmpfs                 1.3G  626M  619M  51% /dev/shm
/dev/sdb1              22G  2.0G   19G  10% /orateste

Veja a instância yvDv criada com o duplicate, e 2gb utilizados no processo.
De volta à instância DBPROD, verificamos se a tabela foi recuperada para a quantidade de registros existentes antes doa exclusão acidental:

11:24:23 SQL> SELECT count(1) from TESTE.TABELA_TESTE;

  COUNT(1)
----------
     90829

Um detalhe importante que o DBA deve ficar atento; Após o processo, a instância dummy é eliminada, os datafiles, controlfile e dump são excluídos, mas a estrutura de pastas e archives utilizados no duplicate não! Veja abaixo que, todos os testes anteriores, mais este que realizei para este artigo, geraram estruturas nesta área que não foram eliminadas pelo processo. Perceba também que cada estrutura recebe um nome atribuído pelo RMAN á instância criada, e este sempre é diferente:

[oracle@orazety12c oradata]$ pwd
/orateste/oradata
[oracle@orazety12c oradata]$ ls -ltr
total 94980
-rw-r----- 1 oracle asmadmin  5948416 Feb 19 16:09 arch_1_6_839926904.arc
-rw-r----- 1 oracle asmadmin   877056 Feb 19 16:34 arch_1_7_839926904.arc
-rw-r----- 1 oracle asmadmin    17920 Feb 19 16:36 arch_1_8_839926904.arc
drwxr-x--- 5 oracle asmadmin     4096 Feb 19 16:36 DBPROD
drwxr-x--- 4 oracle asmadmin     4096 Feb 19 16:39 GAMH_PITR_DBPROD
-rw-r----- 1 oracle asmadmin  5312000 Feb 19 16:53 arch_1_9_839926904.arc
-rw-r----- 1 oracle asmadmin   442368 Feb 19 16:54 arch_1_10_839926904.arc
drwxr-x--- 4 oracle asmadmin     4096 Feb 19 16:57 RIUS_PITR_DBPROD
-rw-r----- 1 oracle asmadmin  6773248 Feb 20 08:44 arch_1_11_839926904.arc
-rw-r----- 1 oracle asmadmin 35577344 Feb 20 09:23 arch_1_12_839926904.arc
-rw-r----- 1 oracle asmadmin    86016 Feb 20 09:26 arch_1_13_839926904.arc
drwxr-x--- 4 oracle asmadmin     4096 Feb 20 09:29 SEKV_PITR_DBPROD
-rw-r----- 1 oracle asmadmin 41875968 Feb 20 11:02 arch_1_14_839926904.arc
-rw-r----- 1 oracle asmadmin   173056 Feb 20 11:11 arch_1_15_839926904.arc
drwxr-x--- 4 oracle asmadmin     4096 Feb 20 11:15 YVDV_PITR_DBPROD

E assim chegamos ao fim deste artigo, com essa simples demonstração de uma funcionalidade bem interessante da versão 12C. Vale registrar que apenas a versão Enterprise fornece suporte à esta operação. Temos algumas outras opções para este processo, como apenas gerar o dump (sem exlusão do mesmo ao final do procedimento) e recuperar todo um schema.
Veja que, quando existe o espaço suficiente para a duplicação da base e geração do dump, em apenas 1 único comando (RECOVER TABLE …), o DBA tem todos os 4 processos mencionados no início do artigo, realizados e gerenciados pelo RMAN.
Para mais detalhes destas operações, recomendo a leitura da documentação oficial da Oracle sobre este tema.

Abraços e até mais

%name Recover de tabela a partir de backup RMAN em Oracle 12C

Autor: Bruno Zehetmeyr bruno.zehe

Bacharel em Ciência da Computação pela UNORP-SP, Pós-graduado em Administração de Sistemas de Informação pela UFLA-MG e pós-graduado em Administração de Banco de Dados Oracle pela VERIS-SP. Analista DBA na Teiko Soluções em TI e professor universitário no Centro Universitário de Brusque – UBIFEBE. Certificado OCP 10g e OCP 11g, OCE Linux. Residente em Blumenau-SC.