# HA niveau 2 - Standby database logical from physical with RMAN from active database

Basé principalement sur [http://www.oracledistilled.com/oracle-database/high-availability/data-guard/creating-a-physical-standby-using-rman-duplicate-from-active-database/](https://www.oracledistilled.com/oracle-database/high-availability/data-guard/creating-a-physical-standby-using-rman-duplicate-from-active-database/)  
Physical standby : [https://oracle-base.com/articles/11g/data-guard-setup-11gr2](https://oracle-base.com/articles/11g/data-guard-setup-11gr2)

On ne peut pas créer directement une LOGICAL STANDBY avec RMAN. Il faut d’abord créer une PHYSICAL STANDBY, puis la convertir en LOGICAL.

## PHYSICAL STANDBY

Primary database informations :  
Host: ocm1.odlabs.net  
DB\_NAME = pritst  
DB\_UNIQUE\_NAME=pritst

Standby database information :  
Host: ocm2.odlabs.net  
DB\_NAME = pritst  
DB\_UNIQUE\_NAME = stbytst

LE DB\_UNIQUE\_NAME DE LA STANDBY EST DONC **DIFFERENT**. Ce paramètre n’est pas modifié sur tous les tutos relatif aux standby databases. Cependant Oracle précise :

"A database in an Oracle Data Guard environment is uniquely identified by means of the DB\_UNIQUE\_NAME parameter in the initialization parameter file. The DB\_UNIQUE\_NAME must be unique across all the databases with the same DBID for RMAN to work correctly in an Oracle Data Guard environment."

**NOTE** : la Standard Edition dispose bien de Dataguard mais pas du Managed Standby (envoi et application automatique des archives par Oracle). Ce n'est disponible que dans l'Enterprise (avec option payante !). La procédure ci-dessous s'applique donc à une version Enterprise.

Pour une Standard Edition, il faudra copier manuellement les archives vers la Standby, puis les appliquer également manuellement (par un script à intervalles réguliers, par exemple).

## SUR LA PRIMARY

1\. La base doit être en ARCHIVELOG FORCE\_LOGGING

```SQL
SQL> select log_mode,force_logging from v$database;
SQL> alter database force logging;
```

2\. Doubler les REDO LOGS par des STANDBY REDO LOGS de taille identiques, mais créer un groupe de plus que les REDO standards

```SQL
SQL> select group#, thread#, bytes/1024/1024 from v$log;
SQL> alter database add standby logfile '/u01/app/oracle/oradata/pritst/stby_redo01.log' size 50M;
SQL> alter database add standby logfile '/u01/app/oracle/oradata/pritst/stby_redo02.log' size 50M;
SQL> alter database add standby logfile '/u01/app/oracle/oradata/pritst/stby_redo03.log' size 50M;
SQL> alter database add standby logfile '/u01/app/oracle/oradata/pritst/stby_redo04.log' size 50M;
```

3\. Vérifier les DB\_\*\_NAME...

```SQL
SQL> show parameter db_name
db_name string pritst

SQL> show parameter db_unique_name
db_unique_name string pritst
```

...Et indiquer le DB\_UNIQUE\_NAME dans LOG\_ARCHIVE\_CONFIG

```SQL
SQL> show parameter LOG_ARCHIVE_CONFIG
log_archive_config string

SQL> alter system set log_archive_config='DG_CONFIG=(pritst,stbytst)';
```

4\. Modifier LOG\_ARCHIVE\_DEST\_1 et LOG\_ARCHIVE\_DEST\_2

```SQL
SQL> show parameter log_archive_dest_1
log_archive_dest_1 string LOCATION=/u01/app/oracle/oradata/pritst/arch

SQL> alter system set LOG_ARCHIVE_DEST_1='LOCATION=/u01/app/oracle/oradata/pritst/arch/ VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=pritst';
SQL> alter system set LOG_ARCHIVE_DEST_STATE_1=ENABLE;

SQL> alter system set LOG_ARCHIVE_DEST_STATE_2=DEFER; # enable it ONLY after creating logical standby
SQL> show parameter LOG_ARCHIVE_DEST_2
log_archive_dest_2 string
SQL> alter system LOG_ARCHIVE_DEST_2='SERVICE=DESTTST LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=stbytst';

-- Attention : nom TNS de la base standby dans "SERVICE"

SQL> alter system set LOG_ARCHIVE_MAX_PROCESSES=16; # it should be between 4 and 30
```

<u>Rappel </u>:

Sur Standard Edition, l'archive\_dest\_2 ne fonctionne pas sur la primary, mais en copiant les archives à la main et en appliquant :

SQL&gt; recover standby database;

il rejoue alors les archives présentes. Cette procédure manuelle fonctionne donc pour la standby, même si c'est pour une Enterprise.

Par contre attention sur une Standard si LOG\_ARCHIVE\_DEST\_2 a été paramétré, la primary remplit ses traces car elle ne sait pas envoyer les archives automatiquement ! On peut paramétrer LOG\_ARCHIVE\_DEST\_STATE\_2=DEFER (comme indiqué ci-dessus) en attendant.

5\. FAL\_SERVER est la base primary et FAL\_CLIENT est la standby.

```SQL
SQL> alter system set fal_server=pritst;
SQL> alter system set fal_client=stbytst;
```

6\. Il faut logguer les ajouts/suppression de fichiers aussi

```SQL
SQL> alter system set standby_file_management=auto;
```

**\* NOTE** : tout ça (et même plus) peut éventuellement être ajouté dans un bloc RUN de RMAN :

```SQL
run {
	sql channel prmy1 "alter system set log_archive_config=''DG_CONFIG=(pritst,stbytst)''";
    sql channel prmy1 "alter system set log_archive_dest_2=''SERVICE=stbytst LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=stbytst''";
    sql channel prmy1 "alter system set log_archive_max_processes=5";
    sql channel prmy1 "alter system set fal_client=stbytst";
    sql channel prmy1 "alter system set fal_server=pritst";
    sql channel prmy1 "alter system set standby_file_management=AUTO";
    sql channel prmy1 "alter system set log_archive_dest_state_1=enable";
    sql channel prmy1 "alter system archive log current";
    sql channel stby "alter database recover managed standby database using current logfile disconnect from session";
    }
```

7\. Et s'assurer que le REMOTE LOGING est activé  
(rappel : password file créé par : orapwd file=orapwtestgg1 password=test entries=5)

```SQL
SQL > show parameter password
SQL > alter system set remote_login_passwordfile=exclusive;
```

8\. LISTENER : les bases doivent être gérée STATIQUEMENT. Ajouter **aux 2** listerner.ora :

```
SID_LIST_LISTENER =
	(SID_LIST =
    	(SID_DESC =
        	(SID_NAME = pritst)
            (ORACLE_HOME = /u01/app/oracle/product/11.2.0/dbhome_1)
        )
    )
```

9\. Ajouter les 2 bases aux tnsnames des 2 serveurs

```
SOURCETST =
	(DESCRIPTION =
    	(ADDRESS = (PROTOCOL = TCP)(HOST = ocm1.odlabs.net)(PORT = 1521))
        (CONNECT_DATA =
        	(SERVER = DEDICATED)
        	(SERVICE_NAME = pritst)
    	)
    )

DESTTST =
	(DESCRIPTION =
    	(ADDRESS = (PROTOCOL = TCP)(HOST = ocm2.odlabs.net)(PORT = 1521))
        (CONNECT_DATA =
        	(SERVER = DEDICATED)
            (SERVICE_NAME = pritst)(UR=A)
# attention : le listener prend le DB_UNIQUE_NAME si la base n'est pas définie statiquement.
# « UR=A » nécessaire car la secondary démarrée en NOMOUNT n'acceptera pas les connexions ("BLOCKED" au lsnrctl status). Ce paramètre permet de passer outre.
		)
    )
```

## SUR LA SECONDARY

1.préparer le terrain pour RMAN

**\* Note** : penser au sysctl.conf si pas déjà fait par l’installation d’Oracle

\- Soit manuellement :

Créer l'arborescence :

```shell
[oracle@ocm2 ~]$ mkdir oradata
[oracle@ocm2 ~]$ mkdir oradata/pritst
[oracle@ocm2 ~]$ mkdir oradata/pritst/arch
[oracle@ocm2 ~]$ mkdir /u01/app/oracle/admin
[oracle@ocm2 ~]$ mkdir /u01/app/oracle/admin/pritst
[oracle@ocm2 ~]$ mkdir /u01/app/oracle/admin/pritst/adump
[oracle@ocm2 ~]$ mkdir /u01/app/oracle/admin/pritst/bdump
[oracle@ocm2 ~]$ mkdir /u01/app/oracle/admin/pritst/dpdump
[oracle@ocm2 ~]$ mkdir /u01/app/oracle/admin/pritst/pfile
```

et ajouter dans /etc/oratab :

```
pritst:/u01/app/oracle/product/11.2.0/dbhome_1:N
```

\- OU simplement en créant une base identique à la primary, vide.

2\. Créer un LISTENER (si base créée manuellement), et enregistrer statiquement la standby

```
SID_LIST_LISTENER =
	(SID_LIST =
		(SID_DESC =
			(SID_NAME = pritst)
			(ORACLE_HOME = /u01/app/oracle/product/11.2.0/dbhome_1)
		)
	)

$ lsnrctl start
```

3\. S'assurer que TNS connaît bien les 2 bases par tnsping

4\. Créer un init.ora basique

```
$ cat initpritst.ora
DB_NAME=pritst
DB_UNIQUE_NAME=stbytst
```

si base a été créée par dbca, crée le pfile à partir du spfile :

```SQL
SQL> create pfile from spfile ;
```

et y modifier DB\_UNIQUE\_NAME, **supprimer** le spfile et redémarrer la base en NOMOUNT.

5\. recopier le password file du primary (important : il ne faut pas le recréer mais reprendre celui du primary)

```shell
$ scp oracle@ocm1:$ORACLE_HOME/dbs/orapwpritst $ORACLE_HOME/dbs
```

6\. Lancer la base en NOMOUNT  
La base démarre avec le PFILE si on s’est assuré que le spfile a bien été supprimé.  
Sortir de la session sqlplus ensuite pour ne pas bloquer RMAN

```
$ sqlplus / as sysdba
Connected to an idle instance.
SQL> startup nomount
SQL> exit
```

7\. fichier de commande de réplication RMAN (toujours sur la standby) dupstby.cmd

```
run {
 allocate channel pri1 type disk;
 allocate channel pri2 type disk;
 allocate channel prmy4 type disk;
 allocate auxiliary channel stby type disk;
  
 duplicate target database 
  for standby 
  from active database
  dorecover
  spfile
    set db_unique_name='stbytst'
    set control_files='/u01/app/oracle/oradata/pritst/control01.ctl','/u01/app/oracle/oradata/pritst/control02.ct'
    set fal_client='pritst'
    set fal_server='stbytst'
    set standby_file_management='AUTO'
    set log_archive_config='dg_config=(pritst,stbytst)'
    set log_archive_dest_1='LOCATION=/u01/app/oracle/oradata/pritst/arch/ VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=stbytst'
    set log_archive_dest_2='service=sourcetst ASYNC valid_for=(ONLINE_LOGFILE,PRIMARY_ROLE) db_unique_name=pritst'
  nofilenamecheck;
  }
```

\* **Noter** l'inversion de pritst et stbytst dans log\_archive\_dest\_1 et log\_archive\_dest\_2

\* Si l'arborescence n'est pas la même que sur le primary, ajouter :

```
set db_file_name_convert='<chemin des fichiers PRYMARY>','<chemin des fichiers SECONDARY>'
set log_file_name_convert='<chemin des fichiers PRYMARY>','<chemin des fichiers SECONDARY>'
```

8\. se connecter aux 2 bases pour lancer le script

\* S'assurer que chacun des serveurs se connecte à la base de l'autre via TNS avec SYS

\* Si la secondary n'a pas été mise en archivelog par dbca, le répertoire archive n'existe peut-être pas ! Créer le même que dans le paramètre LOG\_ARCHIVE\_DEST\_1 qui arrivera de la primary, et vérifier les droits !

\* <span style="color: #ff0000;">**LA BASE PRIMAIRE DOIT AVOIR ETE DEMARREE AVEC UN SPFILE**</span>, sinon RMAN refuse de lancer la duplication !

```
$ rman
Recovery Manager: Release 11.2.0.1.0 - Production on Fri Jul 13 16:47:46 2012
Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.

RMAN> connect target sys/password@SOURCETST
connected to target database: PRITST (DBID=3720300117)

RMAN> connect auxiliary sys/password@DESTTST
connected to auxiliary database: PRITST (not mounted)
```

<u>OU </u>:

```
$ rman target sys/password@SOURCETST auxiliary sys/password@DESTTST
```

\* **NOTE** : en cas de plantage de RMAN, après correction de l’erreur, penser à supprimer le spfile qui a pu être créé juste avant le plantage

```
RMAN> @dupstby.cmd
```

9\. A la fin de la duplication, lancer le processus de recovery

```
$ sqlplus / as sysdba

SQL> alter database recover managed standby database disconnect from session;
```

en cas d'erreur ORA-01153 :

```SQL
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;
SQL> exit
```

10\. Vérifier l’application des REDO LOGS (via les ARCHIVE LOGS)

```SQL
SQL> select sequence#, first_time, next_time, applied from v$archived_log order by sequence#;
```

en forçant sur la **PRIMARY** un switch :

```SQL
SQL> alter system switch logfile;
```

<u>Optionnel</u>

Choisir le niveau de sécurité sur la PRIMARY (MAXIMUM PERFORMANCE par défaut)

```SQL
SQL> SELECT protection_mode FROM v$database;
MAXIMUM PERFORMANCE
```

Choisir un des modes :

<span style="text-decoration: underline;">Maximum Performance // Default  
</span>Transactions on the primary commit as soon as redo information has been written to the online redo log. Transfer of redo information to the standby server is asynchronous.

```SQL
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=stbytst NOAFFIRM ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=stbytst';
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE PERFORMANCE;
```

<span style="text-decoration: underline;">Maximum Availability</span>  
Transactions on the primary do not commit until redo information has been written to the online redo log and the standby redo logs of at least one standby location. If no standby location is available, it acts in the same manner as maximum performance mode until a standby becomes available again.

```SQL
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=stbytst AFFIRM SYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=stbytst';
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE AVAILABILITY;
```

<span style="text-decoration: underline;">Maximum Protection</span>  
Transactions on the primary do not commit until redo information has been written to the online redo log and the standby redo logs of at least one standby location.If not suitable standby location is available, the primary database shuts down.

```SQL
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=DESTTST AFFIRM SYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=stbytst';
SQL> SHUTDOWN IMMEDIATE;
SQL> STARTUP MOUNT;
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE PROTECTION;
SQL> ALTER DATABASE OPEN;
```

## READ-ONLY STANDBY

On peut passer une base PHYSICAL STANDBY momentanément en READ ONLY afin de l’interroger.

```
SQL>alter database recover managed standby database cancel;
SQL>alter database open read only;
SQL>select open_mode from v$database;
SQL>alter database recover managed standby database using current logfile disconnect;
SQL>select open_mode from v$database;
	-- doit indiquer "READ ONLY WITH APPLY"
```

## REDEMARRAGE DE LA STANDBY :

```SQL
-- Start the database:
SQL> STARTUP NOMOUNT;

-- Mount the standby database:
SQL> ALTER DATABASE MOUNT STANDBY DATABASE;

-- Start the managed recovery operation:
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
```

## CONVERSION PHYSICAL EN LOGICAL STANDBY

Basé sur :

[https://chenguangblog.wordpress.com/2011/02/09/creating-logical-standby-database/](https://chenguangblog.wordpress.com/2011/02/09/creating-logical-standby-database/)  
[https://docs.oracle.com/cd/E11882\_01/server.112/e41134/create\_ls.htm#SBYDB4737](https://docs.oracle.com/cd/E11882_01/server.112/e41134/create_ls.htm#SBYDB4737)  
http://rajiboracle.blogspot.fr/2017/01/how-to-convert-physical-standby-to.html

Après création d’une physical standby avec RMAN, vérifier si des types sont incompatibles avec standby dans la base source :

```SQL
SQL> select OWNER, TABLE_NAME, COLUMN_NAME, ATTRIBUTES, DATA_TYPE from dba_logstdby_unsupported;
```

Eventuellement, convertir ou modifier les types incompatibles.

1\. **Sur la PRIMARY**, créer un répertoire archive supplémentaire (utilisé si la primary est switchée en standby)

```shell
mkdir /u01/oradata/orcl/stbyarchives
```

2\. Modifier LOG\_ARCHIVE\_DEST\_1 uniquement pour les REDO primary (ONLINE\_LOGFILES), et créer un LOG\_ARCHIVE\_DEST\_3 utilisé si la base switche en standby

```SQL
SQL> alter system set log_archive_dest_1='LOCATION=/u01/app/oracle/oradata/pritst/arch/ VALID_FOR=(ONLINE_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=pritst';
SQL> alter system set LOG_ARCHIVE_DEST_3='LOCATION=/u01/app/oracle/oradata/orcl/stbyarchives VALID_FOR=(STANDBY_LOGFILES,STANDBY_ROLE)';
```

3\. Stopper et redémarrer au moins une fois la standby après création

```SQL
SQL> shutdown immediate;
SQL> startup nomount;
SQL> alter database mount standby database;
SQL> alter database recover managed standby database disconnect from session;
```

4\. S'assurer que les logs sont au même niveau sur les 2 bases

```SQL
SQL> select sequence#, first_time, next_time, applied from v$archived_log order by sequence#;
```

5\. **Sur la standby**, stopper le processus managed recovery

```SQL
SQL> alter database recover managed standby database cancel;
```

6\. **Sur la primary**, lancer la construction des tables LogMiner pour le dictionnaire

```SQL
SQL> begin
dbms_logstdby.build;
end;
/
```

7\. **Sur la standby**, lancer la conversion en LOGICAL

```SQL
SQL> alter database recover to logical standby STBYORCL; # DB_UNIQUE_NAME de la standby
```

\* Si problème, stopper le processus dans une autre session par :

```SQL
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
```

8\. Générer quelques log switchs sur la **primary** pour s’assurer que les archives sont créées et envoyées vers la standby

```SQL
SQL> alter system switch logfile;
```

9\. Sur la standby, la commande « alter database... » se termine après un certain temps.

En cas d’erreur :

<p class="callout warning">ERROR at line 1:  
ORA-20000: File /u01/app/oracle/oradata/pritst/temp01.dbf has wrong dbid or  
dbname, remove or restore the offending file.</p>

(qui n’est pas bloquante), recréer tout de suite le tablespace TEMP.

La base est désormais une LOGICAL STANDBY, on peut le voir dans l’alert.log :

<p class="callout info">RFS\[12\]: Identified database type as 'logical standby'</p>

10\. Relancer la standby

```SQL
SQL> shutdown immediate
SQL> startup mount
SQL> alter database open resetlogs;
```

11\. Sur la standby, paramétrer un LOG\_ARCHIVE\_DEST\_3 utilisé si la base switche en primary

```SQL
SQL> alter system set LOG_ARCHIVE_DEST_3='LOCATION=/u01/app/oracle/oradata/orcl/stbyarchives VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE)';
```

12\. Il reste à lancer le processus SQL Apply

```SQL
SQL> alter database start logical standby apply immediate;
```

Désormais les changements détectés dans les ARCHIVE sont rejoués en SQL, la base est par ailleurs ouverte pour d’autres requêtes, même de modifications.