17 Juillet 2017
En 12c Enterprise, avec l'option Multitenant (container DB), Rman permet de sauvegarder 3 éléments distincts :
-La base container nommée "CDB",
-les pluggables databases "PDBs"
-les métadatas.
Dans cet article, nous allons étudier la partie Pluggable database, à mon sens la plus courante dans le monde de la production.
Pour cet exemple, l'OS utilisé est un system Linux (Oracle Linux 7).
Réalisons donc un backup complet de notre CDB qui inclut donc toutes les PDBs :
Petit passage en archivelog avant toutes choses :
SQL> alter system set log_archive_dest='/oradata/CDB/arch' scope=both ;
System altered.
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup mount ;
ORACLE instance started.
..
Database mounted.
SQL>alter database archivelog ;
Database altered.
SQL> alter database open;
Database altered.
SQL>alter pluggable database all open read write ;
Pluggable database altered.
SQL> archive log list;
Database log mode Archive Mode
Automatic archival Enabled
Archive destination /oradata/CDB/arch
Oldest online log sequence 208
Next log sequence to archive 210
Current log sequence 210
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
4 MYPDB1 READ WRITE NO
Go pour un petit backup full
RMAN> sql 'alter system switch logfile';
using target database control file instead of recovery catalog
sql statement: alter system switch logfile
RMAN> backup database format '/oratmp/Backup/bkpfull_svg_DB_on%_%t%U.bkp' plus archivelog ;
Starting backup at 18-SEP-15
current log archived
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=423 device type=DISK
channel ORA_DISK_1: starting archived log backup set
...
...
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:07
Finished backup at 18-SEP-15
...
Finished Control File and SPFILE Autobackup at 18-SEP-15
Ok, nous allons maintenant créer une petite panne "utilisateur" sur la PDB MYPDB1 :
SQL> alter session set container=MYPDB1;
Session altered.
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS";
COUNT(*)
----------
5296
SQL> delete from MRS_EXECUTED_BLOCKS;
5296 rows deleted
SQL> commit;
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS";
COUNT(*)
----------
0
Là, nous avons une simple erreur utilisateur... Deux choix s'offrent principalement à nous :
- La restauration complète de la PDB
ou
- La restauration incomplète, ou encore restauration d'une ou plusieurs tables dans le cas de cet article.
Voyons voir tout d'abord la restauration complète de la PDB dans laquelle se trouve la table ci dessus.
Note : Bien sur pour ce type d'opération, la PDB se doit d'être CLOSE.
RMAN> sql 'alter pluggable database MYPDB1 close immediate';
using target database control file instead of recovery catalog
sql statement: alter pluggable database MYPDB1 close immediate
RMAN> run { set until time "to_date('18-09-2015:08:50:00', 'DD-MM-YYYY:hh24:mi:ss')"; restore pluggable database MYPDB1; recover pluggable database MYPDB1 AUXILIARY DESTINATION '/oratmp'; }
Note :
En restauration PITR (Point In Time Recovery), si la flash_recovery n'est pas configurée, l'utilisation du paramètre "AUXILIARY DESTINATION" est obligatoire pour effectuer le Recover.
Cette log est assez intéressante ... On s’aperçoit qu'RMAN va créer une instance temporaire "clone" de l'instance CDB pour effectuer ONLINE, sans aucun arrêt de l'instance principale (imaginons une prod), un recover en PITR de notre PDB.
Cela veut dire aussi en langage "stockage" qu'il faut prévoir du disque (soit unitairement comme l'exemple ci dessus, ou soit fixe pour une Flash_recovery), et cela peut monter vite en Go en fonction de la taille de notre PDB ..
RMAN> sql 'alter pluggable database MYPDB1 open resetlogs';
sql statement: alter pluggable database MYPDB1 open resetlogs
RMAN>exit;
/oratmp/CDB[CDB]:sqlplus / as sysdba
SQL*Plus: Release 12.1.0.2.0 Production on Fri Sep 18 09:20:33 2015
Copyright (c) 1982, 2014, Oracle. All rights reserved.
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
SQL> set lines 200
SQL> select DBID,NAME,OPEN_MODE,OPEN_TIME from v$pdbs;
DBID NAME OPEN_MODE OPEN_TIME
---------- ------------------------------ ---------- ---------------------------------------------------------------------------
548741631 PDB$SEED READ ONLY 17-SEP-15 07.56.27.384 PM +02:00
888812708 MYPDB1 READ WRITE 18-SEP-15 09.20.02.391 AM +02:00
SQL> alter session set container=MYPDB1 ;
Session altered.
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS";
COUNT(*)
----------
5296
=> Ok, nous avons retrouvé nos datas, restauré uniquement notre PDB sans impact sur la dispo de l'instance CDB principale, et donc de facto sur les autres PDBs (dans le cas où nous en aurions eu plusieurs), mais nous avons quand même du générer une indisponibilité de la PDB cible.
Nous aurions pu donc passer par une restauration table uniquement, puisque RMAN (en 12c) permet la restauration d'une table directement !
Mais qu'allons nous faire de Datapump alors me direz vous ? Et bien le conserver, car il est toujours utile d'avoir un dump à coté au cas ou (par expérience, avec les joies de la robotique ..)
Note :
Attention, pour Datapump sur une PDB, il faut là encore utiliser un ALIAS TNS pour le réaliser, car un datapump full de la CDB ne permet en effet pas de réaliser un export des PDBs.
Vu que nous avons restauré la PDB, il faut re-simuler une panne, on refait donc un petit backup de notre PDB, avec une gestion des archivelogs car dans ce second exemple je vais utiliser le until sequence pour ainsi réaliser une restauration PITR.
SQL> alter session set container=cdb$root;
Session altered.
SQL> alter system switch logfile ;
System altered.
SQL> exit
RMAN> run {
backup pluggable database MYPDB1 format '/oratmp/Backup/bkpfull_MYPDB1_on%_%t%U.bkp' ;
backup archivelog all format '/oratmp/Backup/bkpfull_MYPDB1_ARCH_%t%U.bkp' ;
}
Starting backup at 18-SEP-15
using channel ORA_DISK_1
...
...
...
Finished Control File and SPFILE Autobackup at 18-SEP-15
RMAN> exit
-> On génère une petite archivelog histoire de nous arrêter à la précédente
/home/oracle[CDB]:sqlplus / as sysdba
SQL*Plus: Release 12.1.0.2.0 Production on Fri Sep 18 09:25:55 2015
Copyright (c) 1982, 2014, Oracle. All rights reserved.
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
SQL> alter system switch logfile ;
System altered.
SQL> select sequence#,to_char(completion_time,'DD-MM-YYYY HH24:MI:SS') from v$archived_log where to_char(completion_time,'DD-MM-YYYY HH24:MI:SS') > '18-09-2015 10:10:00';
SEQUENCE# TO_CHAR(COMPLETION_
---------- -------------------
370 18-09-2015 10:15:51
371 18-09-2015 10:16:29
372 18-09-2015 10:16:50
373 18-09-2015 10:17:31
374 18-09-2015 10:18:41
SQL> alter session set container=MYPDB1 ;
Session altered.
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS";
COUNT(*)
----------
5296
SQL> delete from "MYPDB1"."MRS_EXECUTED_BLOCKS";
5296 rows deleted.
SQL> commit;
Commit complete.
==> Lançons donc notre restauration de cette table à partir de RMAN, en s’arrêtant à la séquence 330 qui ne contient donc pas l'opération de delete ci dessus ..
Ici, pas besoin de faire un close de la pluggable database, la restauration n'étant pas complète et étant directement sur un objet précis. Par contre, il faut spécifier sur quelle PDB les tables concernées sont stockées :
Note :
J'ai volontairement fait un DELETE et non pas un DROP TABLE, car dans le cas d'un drop table, il n'est pas nécessaire de passer par une table temporaire via la génération d'un dump, comme je le fais ci dessous.
En effet, RMAN ne permet pas de restaurer le contenu de la table sur elle même, il faut passer par une table temporaire sinon cela plante.
Bien sur vous me direz que si toutes les lignes ont été supprimées, on peut dropper la table et la restaurer entièrement... oui pourquoi pas ! Mais imaginons que seules quelques lignes soient impactées, nous pourrions faire du rattrapage à partir d'une table temporaire, et éviter ainsi d'avoir à restaurer l'intégralité de notre table.
RMAN> recover table MYPDB1.MRS_EXECUTED_BLOCKS of pluggable database MYPDB1
until sequence 374
auxiliary destination '/oratmp' ;
Starting recover at 18-SEP-15
using target database control file instead of recovery catalog
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of recover command at 09/18/2015 11:20:05
RMAN-05063: Cannot recover specified tables
RMAN-05112: table "MYPDB1"."MRS_EXECUTED_BLOCKS" already exists
-> Donc on lance :
RMAN> recover table MYPDB1.MRS_EXECUTED_BLOCKS of pluggable database MYPDB1
until sequence 374
auxiliary destination '/oratmp'
remap table MYPDB1.MRS_EXECUTED_BLOCKS:MRS_EXECUTED_BLOCKS_TMP;
Starting recover at 18-SEP-15
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=36 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='zhkk'
...
...
...
Finished recover at 18-SEP-15
database opened
contents of Memory Script:
{
sql clone 'alter pluggable database MYPDB1 open';
}
executing Memory Script
sql statement: alter pluggable database MYPDB1 open
Un petit Focus sur la partie "datapump" de RMAN :
contents of Memory Script:
{
# create directory for datapump import
sql 'MYPDB1' "create or replace directory
TSPITR_DIROBJ_DPDIR as ''
/oratmp''";
# create directory for datapump export
sql clone 'MYPDB1' "create or replace directory
TSPITR_DIROBJ_DPDIR as ''
/oratmp''";
}
executing Memory Script
sql statement: create or replace directory TSPITR_DIROBJ_DPDIR as ''/oratmp''
sql statement: create or replace directory TSPITR_DIROBJ_DPDIR as ''/oratmp''
Performing export of tables...
EXPDP> Starting "SYS"."TSPITR_EXP_zhkk_Avay":
EXPDP> Estimate in progress using BLOCKS method...
EXPDP> Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
EXPDP> Total estimation using BLOCKS method: 12 MB
EXPDP> Processing object type TABLE_EXPORT/TABLE/TABLE
EXPDP> Processing object type TABLE_EXPORT/TABLE/COMMENT
EXPDP> Processing object type TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
EXPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
EXPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
EXPDP> . . exported "MYPDB1"."MRS_EXECUTED_BLOCKS" 9.598 MB 438313 rows
EXPDP> Master table "SYS"."TSPITR_EXP_zhkk_Avay" successfully loaded/unloaded
EXPDP> ******************************************************************************
EXPDP> Dump file set for SYS.TSPITR_EXP_zhkk_Avay is:
EXPDP> /oratmp/tspitr_zhkk_58836.dmp
EXPDP> Job "SYS"."TSPITR_EXP_zhkk_Avay" successfully completed at Fri Sep 18 11:12:21 2015 elapsed 0 00:00:11
Export completed
-> Import avec le remap table :
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_zhkk_wCBw" successfully loaded/unloaded
IMPDP> Starting "SYS"."TSPITR_IMP_zhkk_wCBw":
IMPDP> Processing object type TABLE_EXPORT/TABLE/TABLE
IMPDP> Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
IMPDP> . . imported "MYPDB1"."MRS_EXECUTED_BLOCKS_TMP" 9.598 MB 438313 rows
IMPDP> Processing object type TABLE_EXPORT/TABLE/COMMENT
IMPDP> Processing object type TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
IMPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
IMPDP> Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
IMPDP> Job "SYS"."TSPITR_IMP_zhkk_wCBw" successfully completed at Fri Sep 18 11:12:24 2015 elapsed 0 00:00:01
-> Et maintenant, RMAN supprime son espace de travail :
Removing automatic instance
Automatic instance removed
auxiliary instance file /oratmp/CDB/datafile/o1_mf_temp_bzqo9tv9_.tmp deleted
auxiliary instance file /oratmp/CDB/datafile/o1_mf_temp_bzqo9th7_.tmp deleted
auxiliary instance file /oratmp/ZHKK_PITR_MYPDB1_CDB/onlinelog/o1_mf_3_bzqoc7g4_.log deleted
auxiliary instance file /oratmp/ZHKK_PITR_MYPDB1_CDB/onlinelog/o1_mf_2_bzqoc7dz_.log deleted
auxiliary instance file /oratmp/ZHKK_PITR_MYPDB1_CDB/onlinelog/o1_mf_1_bzqoc7cq_.log deleted
auxiliary instance file /oratmp/ZHKK_PITR_MYPDB1_CDB/datafile/o1_mf_aedifica_bzqoc5kb_.dbf deleted
auxiliary instance file /oratmp/CDB/datafile/o1_mf_sysaux_bzqo9p7d_.dbf deleted
auxiliary instance file /oratmp/CDB/datafile/o1_mf_system_bzqo9p7f_.dbf deleted
auxiliary instance file /oratmp/CDB/datafile/o1_mf_sysaux_bzqo975w_.dbf deleted
auxiliary instance file /oratmp/CDB/datafile/o1_mf_undotbs1_bzqo975t_.dbf deleted
auxiliary instance file /oratmp/CDB/datafile/o1_mf_system_bzqo975w_.dbf deleted
auxiliary instance file /oratmp/CDB/controlfile/o1_mf_bzqo91sv_.ctl deleted
Finished recover at 18-SEP-15
On vérifie :
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS";
COUNT(*)
----------
0
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS_TMP";
COUNT(*)
----------
5296
SQL> insert into "MYPDB1"."MRS_EXECUTED_BLOCKS" select * from "MYPDB1"."MRS_EXECUTED_BLOCKS_TMP" ;
5296 rows created.
SQL> commit ;
Commit complete.
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS"; COUNT(*) ---------- 5296 SQL> drop table "MYPDB1"."MRS_EXECUTED_BLOCKS_TMP"; Table dropped.
Comme je le mentionnais plus haut, dans le cas d'un drop direct d'une table, l'instruction de recover est plus simple :
SQL> alter session set container=MYPDB1;
Session altered.
SQL> drop table "MYPDB1"."MRS_EXECUTED_BLOCKS";
Table dropped.
La restauration complète de la table :
RMAN> recover table MYPDB1.MRS_EXECUTED_BLOCKS of pluggable database MYPDB1 until sequence 374 auxiliary destination '/oratmp' ;
Et voila, nous avons retrouvé notre table complète :
SQL> select count(*) from "MYPDB1"."MRS_EXECUTED_BLOCKS";
COUNT(*)
----------
5296
Il va de soi qu'il y a plein d'autres tests à faire avec notre ami RMAN, mais cela donne déja une bonne idée de ce que la version 12c permet de faire !
Micka