Dataguard : Controlling PDB Replication in Data Guard Environments

 
 
With the wider adoption of Oracle container databases and the impending de-support of non-container database coming with Oracle Database 20c, it is well worth considering how this will impact your standby databases and Disaster Recovery (DR) environments.
The good news is that overall, the principle of standby databases has not changed significantly but there are a few key points that need to be understood to manage standby databases in the multi-tenant architecture effectively. There also a few new features that will help with standby database management.

First, let's consider that all Pluggable databases in a container share the same instance components

Background Processes
Shared and Process memory (SGA/PGA)
Online and archive redo logs

So, bearing those in mind, how do we protect a pluggable database (PDB) with a Standby database? For the most part, as we did before with a few key clarifications:

The database role is defined at the container level; Individual pluggable databases (PDBs) do not have their own roles.
The database role is changed by issuing DDL at the container level; all PDB's change at the same time.
ALTER DATABASE RECOVER MANAGED STANDBY functions only in the root container; it is not allowed in a PDB.
The ALTER PLUGGABLE DATABASE [OPEN|CLOSE] SQL statement is supported on the standby, provided you have already opened the root container.
The ALTER PLUGGABLE DATABASE RECOVER statement is not supported on the standby. (Standby recovery is always at the CDB level.)
 A Standby container database does not have to have standby PDB's for all PDB's in the primary container.
 
 
 
 
 
 
 
 
 
 
Create Standby Container

Let's walk through a quick example. Once we have our primary container setup with the PDB's we can start thinking about setting up a standby container database.

When using Multi-tenant, the standby is setup at the container level and then all PDB's are protected if you choose them to be. So first we need to set up the CDB with a standby, mostly as we did with non CDB:

--> Enable Force Logging Mode
--> Configure the Primary Database to Receive Redo Data
--> Set Primary Database Initialization Parameters (log_archive_dest_2 etc)
--> Enable Archiving
--> Create a Backup Copy of the Primary Database Data Files
--> Create a Control File for the Standby Database
--> Create a Parameter File for the Standby Database
--> Copy Files from the Primary System to the Standby System
--> Set Up the Environment to Support the Standby Database
--> Copy password file to standby Oracle Home
--> Configure and start new Container listener
--> Update tnsnames on both nodes & verify connectivity:
        --> From Primary node: sqlplus sys@CDB2 as sysdba
        --> From Standby node: sqlplus sys@CDB1 as sysdba


DB creation method
For Non CDB
dbca -silent -createDuplicateDB -gdbName cdb2 -primaryDBConnectionString 192.168.45.101:1521/cdb1 -sid cdb1 -createAsStandby -dbUniqueName cdb2

For CDB databases


--> Start the Physical Standby Database (nomount)

    > Restore the backup
    > rman target /
    > restore standby controlfile from '/Location.ctl';
    > alter database mount;
    > restore database;


--> sqlplus / as sysdba

    Start Standby log apply    
        ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
    Verify the Physical Standby Database Is Performing Properly
        SELECT ROLE, THREAD#, SEQUENCE#, ACTION FROM V$DATAGUARD_PROCESS;
    Verify PDB Status
    So now we have a standby container DB let's check on the PDB's

    set lines 100
    column name format a20
    SELECT name, open_mode FROM v$pdbs;
    
    NAME                 OPEN_MODE
    -------------------- ----------
    PDB$SEED             MOUNTED
    PDB1                 MOUNTED
    PDB2                 MOUNTED         
    PDB3                 MOUNTED 
    PDB4                 MOUNTED
  
  SQL> SELECT DB_UNIQUE_NAME, OPEN_MODE, DATABASE_ROLE from v$database;

    DB_UNIQUE_NAME                 OPEN_MODE            DATABASE_ROLE
    ------------------------------ -------------------- ----------------
    CDB2                         MOUNTED              PHYSICAL STANDBY


    SELECT name, open_mode FROM v$pdbs;

    NAME                  OPEN_MODE
    --------------------  ----------
    PDB$SEED              READ ONLY
    PDB1                  MOUNTED
    PDB2                  MOUNTED
    PDB3                  MOUNTED
   
    
Note that the PDB Name matches the primary PDB Name (PDB1)

. Adding additional PDB's

Given the fact the role is set at the container level, we cannot add a distinct PDB to the standby container without it already having an associated Primary PDB in the Primary CDB. When we add a Primary PDB, we can select at that point whether it is protected by a standby or not. Note that we can also add/remove the standby out with this operation as indeed we did above.

Adding a new PDB to the primary container with a standby (seed)
This can be done in one step by either creating a new database or cloning from an existing PDB

SQL > CREATE PLUGGABLE DATABASE TESTPDB2 ADMIN USER PDMADMIN IDENTIFIED BY ******* STANDBYS=('CDB2');

This is great as it creates a new PDB (Based on the seed database) and also creates a standby database in the standby container

Adding a new PDB to the primary container with a standby (Clone)
SQL> CREATE PLUGGABLE DATABASE TESTPDB3 FROM PDB1 STANDBYS=('CDB2');

Again, we get a new Primary and standby in one statement very cool!

Switchover between Primary and Standby
We do not do this very often but when building a new database, it is a good idea to switch over the roles to ensure that the archivelogs can be sent and applied between the DR and primary sites. As mentioned earlier, this is done at the container database level, and all PDB's are switched.

Verify that the primary database can be switched to the standby role.
PRIMARY CDB
ALTER DATABASE COMMIT TO SWITCHOVER TO PHYSICAL STANDBY WITH SESSION SHUTDOWN;
STARTUP MOUNT;
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;


--> NEW PRIMARY (CDB2)

ALTER DATABASE COMMIT TO SWITCHOVER TO PRIMARY WITH SESSION SHUTDOWN;
ALTER DATABASE OPEN;
ALTER PLUGGABLE DATABASE ALL OPEN;

Switch Back using the same process as above.

Convert to snapshot standby
Snapshot standby is a really great feature of Dataguard and can be used for reporting databases, DR tests, production destructive testing, etc so let’s make sure it still works in 19c! Again, this is done at the Container level with all PDB's affected.

ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
ALTER DATABASE CONVERT TO SNAPSHOT STANDBY;
ALTER DATABASE OPEN;

So, all in all, yes it works largely the same way except that it is all done at container level so all PDB's are converted to snapshot standby at the same time. 
 
 
--> For Specifice pluggable database  :

STANDBYS Clause (12.1.0.2)
The STANDBYS clause of the CREATE PLUGGABLE DATABASE statement was introduced in 12.1.0.2 to allow us to indicate which standby databases should protect the new PDB being created. The syntax is as follows.

CREATE PLUGGABLE DATABASE ... STANDBYS={('cdb_name', 'cdb_name', ...) | NONE | ALL [EXCEPT ('cdb_name', 'cdb_name', ...)]}
Here are some examples of the usage.

STANDBYS clause omitted : The pluggable database is protected by all standby databases.
STANDBYS=NONE : The pluggable database is not protected on any of the standby databases.
STANDBYS=ALL : The pluggable database is protected on all of the standby databases.
STANDBYS=ALL EXCEPT ('cdb1_stby_1','cdb1_stby_2') : The pluggable database is protected by all standby databases, except those with a DB_UNIQUE_NAME of 'cdb1_stby_1' and 'cdb1_stby_2'.
STANDBYS=('cdb1_stby_1') : The pluggable database is only protected by the standby database with a DB_UNIQUE_NAME of 'cdb1_stby_1'.
Create a new pluggable database on the primary database, which won't be protected by any standby database


--> Create a new pluggable database on the primary database, which won't be protected by any standby database


-- Primary
CREATE PLUGGABLE DATABASE pdb2  ADMIN USER pdb_admin IDENTIFIED BY Password1  STANDBYS=NONE;
ALTER PLUGGABLE DATABASE pdb2 OPEN;

-- Primary
COLUMN name FORMAT a30
SELECT name, open_mode, recovery_status FROM   v$pdbs ORDER BY 1;

NAME                           OPEN_MODE  RECOVERY
------------------------------ ---------- --------
PDB$SEED                       READ ONLY  ENABLED
PDB1                           READ WRITE ENABLED
PDB2                           READ WRITE ENABLED

-- Standby
COLUMN name FORMAT A30
SELECT name, open_mode, recovery_status FROM   v$pdbs ORDER BY 1;

NAME                           OPEN_MODE  RECOVERY
------------------------------ ---------- --------
PDB$SEED                       MOUNTED    ENABLED
PDB1                           MOUNTED    ENABLED
PDB2                           MOUNTED    DISABLED


We only have a single standby database, so we could achieve the same result by excluding it using the DB_UNIQUE_NAME along with ALL EXCEPT as shown below.

-- Primary

-- Remove the PDB.
ALTER PLUGGABLE DATABASE pdb2 CLOSE;
DROP PLUGGABLE DATABASE pdb2 INCLUDING DATAFILES;

-- Recreate it using ALL ACCEPT.
CREATE PLUGGABLE DATABASE pdb2  ADMIN USER pdb_admin IDENTIFIED BY Password1  STANDBYS=ALL EXCEPT ('cdb2');
ALTER PLUGGABLE DATABASE pdb2 OPEN;


-- Primary
-- Remove the PDB.
ALTER PLUGGABLE DATABASE pdb2 CLOSE;
DROP PLUGGABLE DATABASE pdb2 INCLUDING DATAFILES;

-- Recreate it with one of these.
CREATE PLUGGABLE DATABASE pdb2  ADMIN USER pdb_admin IDENTIFIED BY Password1;

CREATE PLUGGABLE DATABASE pdb2  ADMIN USER pdb_admin IDENTIFIED BY Password1  STANDBYS=ALL;

CREATE PLUGGABLE DATABASE pdb2  ADMIN USER pdb_admin IDENTIFIED BY Password1  STANDBYS=('cdb2');

-- Open it.
ALTER PLUGGABLE DATABASE pdb2 OPEN;

--> Remove the new PDB before trying the examples in the following section.


ENABLED_PDBS_ON_STANDBY Parameter (12.2)

The ENABLED_PDBS_ON_STANDBY initialisation parameter was introduced in this form in 12.2 to control which pluggable databases are protected by a specific standby database. The parameter can be set on a primary or standby database, but it is only used by standby databases. Here are some examples of how the parameter might be used with wildcard and exclusions.

"*" : All PDBs are protected.
"PDB1", "PDB2" : Only pluggable databases called "PDB1" and "PDB2" are protected.
"PDB*" : Only pluggable databases with a name beginning with "PDB" are protected.
"*", "-PDB*" : All pluggable databases are protected, except those with a name beginning with "PDB".
"*", "-PDB1" : All pluggable databases are protected, except if the name is "PDB1".
On the standby database we issue the following command to prevent a pluggable database called PDB2 from being replicated to the standby database. All other PDBs will be protected as normal.

--> Standby
ALTER SYSTEM SET enabled_pdbs_on_standby="*", "-PDB2";
On primary database create pluggable databases called PDB2 and PDB3.

--> Primary
CREATE PLUGGABLE DATABASE pdb2   ADMIN USER pdb_admin IDENTIFIED BY Password1;
ALTER PLUGGABLE DATABASE pdb2 OPEN;

CREATE PLUGGABLE DATABASE pdb3   ADMIN USER pdb_admin IDENTIFIED BY Password1;
ALTER PLUGGABLE DATABASE pdb3 OPEN;

status of the pluggable databases on both the primary and standby databases.

--> Primary
COLUMN name FORMAT A30

SELECT name, open_mode, recovery_status
FROM   v$pdbs
ORDER BY 1;

NAME                           OPEN_MODE  RECOVERY
------------------------------ ---------- --------
PDB$SEED                       READ ONLY  ENABLED
PDB1                           READ WRITE ENABLED
PDB2                           READ WRITE ENABLED
PDB3                           READ WRITE ENABLED

SQL>


--> Standby
COLUMN name FORMAT A30

SELECT name, open_mode, recovery_status
FROM   v$pdbs
ORDER BY 1;

NAME                           OPEN_MODE  RECOVERY
------------------------------ ---------- --------
PDB$SEED                       MOUNTED    ENABLED
PDB1                           MOUNTED    ENABLED
PDB2                           MOUNTED    DISABLED
PDB3                           MOUNTED    ENABLED

SQL>
As expected, the pluggable database called PDB2 is not protected by the standby database.

To clean up, remove the new pluggable databases and reset the ENABLED_PDBS_ON_STANDBY parameter on the standby database.

-- Primary
ALTER PLUGGABLE DATABASE pdb2 CLOSE;
DROP PLUGGABLE DATABASE pdb2 INCLUDING DATAFILES;
ALTER PLUGGABLE DATABASE pdb3 CLOSE;
DROP PLUGGABLE DATABASE pdb3 INCLUDING DATAFILES;

-- Standby
ALTER SYSTEM SET enabled_pdbs_on_standby="*";


Key Differences in 19c

The process of flashing back a physical standby to a point in time that was captured on the primary is simplified by automatically replicating restore points from primary to the standby. See Replicating Restore Points from Primary to Standby.
When flashback or point-in-time recovery is performed on the primary database, a standby that is in mounted mode can automatically follow the same recovery procedure performed on the primary.
DML operations can be performed on Active Data Guard standby instances by transparently redirected to and run on the primary database.
Changes in Oracle Database Release 18c
These are the changes in Oracle Data Guard Concepts and Administration for Changes in Oracle Database Release 18c, Version 18.1.

The database buffer cache state is now maintained on an Oracle Active Data Guard standby during a role change
Global temporary tables can now be dynamically created on an Oracle Active Data Guard standby database
A new view V$DATAGUARD_PROCESS (which replaces V$MANAGED_STANDBY) provides information that can be queried to verify that redo is being transmitted from the primary database and applied to the standby database.
Database nologging has been extended with two new modes: Standby Nologging for Load Performance and Standby Nologging for Data Availability.
A standby database can now be refreshed over the network using one RMAN command, RECOVER STANDBY DATABASE.