Introduction to Oracle Data Guard
Oracle Data Guard provides a comprehensive set of services that create, maintain, manage, and monitor one or more standby databases to enable production Oracle databases to survive disasters and data corruptions. It maintains these standby databases as copies of the production database, and if the production database becomes unavailable, Data Guard can switch any standby database to the production role — minimizing downtime and preventing data loss.
In this guide, we'll configure a Physical Standby Database using RMAN Active Duplication. The primary database ships redo data to the standby, which continuously applies it to stay synchronized. Here's the high-level architecture:
Environment Overview & Prerequisites
| Component | Primary (dg1) | Standby (dg2) |
|---|---|---|
| Hostname | dg1.bracecoder | dg2.bracecoder |
| IP Address | 192.168.0.2 | 192.168.0.7 |
| OS | Oracle Linux 7.8 | Oracle Linux 7.8 |
| Database Version | 19c EE (19.3.0.0) | 19c EE (19.3.0.0) |
| DB Name | oradb | oradb |
| DB Unique Name | oradb | oradb_stby |
| Oracle Home | /u01/app/oracle/product/19.0.0/db_1 | /u01/app/oracle/product/19.0.0/db_1 |
| Storage | ASM (+DATA, +FRA) | ASM (+DATA, +FRA) |
| CDB? | NO (Non-CDB) | NO (Non-CDB) |
Prerequisites before you begin:
- Oracle Database 19c Enterprise Edition installed on both servers (software only on standby)
- Oracle Restart (Grid Infrastructure) configured with ASM on both servers
- Network connectivity between dg1 and dg2 (ping and SSH working)
- Sufficient disk space in ASM disk groups on both servers
- The primary database must be in ARCHIVELOG mode
Let's first verify the infrastructure is ready on both servers.
Verify Oracle Restart Resources — Primary (dg1)
Run crsctl stat res -t as the grid user to confirm Oracle Restart services are running. We need ASM, the Listener, the disk groups, and the database to be ONLINE.
[grid@dg1 ~]$ crsctl stat res -t
--------------------------------------------------------------------------------
Name Target State Server State details
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.DATA.dg
ONLINE ONLINE dg1 STABLE
ora.FRA.dg
ONLINE ONLINE dg1 STABLE
ora.LISTENER.lsnr
ONLINE ONLINE dg1 STABLE
ora.asm
ONLINE ONLINE dg1 Started,STABLE
ora.ons
OFFLINE OFFLINE dg1 STABLE
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.cssd
1 ONLINE ONLINE dg1 STABLE
ora.diskmon
1 OFFLINE OFFLINE STABLE
ora.evmd
1 ONLINE ONLINE dg1 STABLE
ora.oradb.db
1 ONLINE ONLINE dg1 Open,HOME=/u01/app/o
racle/product/19.0.0
/db_1,STABLE
--------------------------------------------------------------------------------shell
oradb all ONLINE and STABLE.
Verify Oracle Restart Resources — Standby (dg2)
On the standby server, we need ASM, the Listener, and disk groups ready. There's no database here yet — we'll create the standby later.
[grid@dg2 ~]$ crsctl stat res -t
--------------------------------------------------------------------------------
Name Target State Server State details
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.DATA.dg
ONLINE ONLINE dg2 STABLE
ora.FRA.dg
ONLINE ONLINE dg2 STABLE
ora.LISTENER.lsnr
ONLINE ONLINE dg2 STABLE
ora.asm
ONLINE ONLINE dg2 Started,STABLE
ora.ons
OFFLINE OFFLINE dg2 STABLE
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.cssd
1 ONLINE ONLINE dg2 STABLE
ora.diskmon
1 OFFLINE OFFLINE STABLE
ora.evmd
1 ONLINE ONLINE dg2 STABLE
--------------------------------------------------------------------------------shell
Verify /etc/hosts on Both Servers
Both servers must resolve each other's hostnames. This is critical for TNS connectivity and RMAN duplication to work.
[grid@dg1 ~]$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.2 dg1.bracecoder dg1
192.168.0.7 dg2.bracecoder dg2shell
/etc/hosts file must be identical on both servers so that hostname resolution is consistent in both directions.
STEP 1 Verify the Primary Database
Before making any changes, we verify the current state of the primary database — its role, archiving mode, redo log configuration, ASM disk groups, version, datafiles, and log file members. This gives us a clear baseline.
Check Database Role & Open Mode
Confirm the database is open as PRIMARY in READ WRITE mode.
SQL> SELECT name, db_unique_name, database_role, open_mode FROM v$database;
NAME DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE
--------- ------------------------------ ---------------- --------------------
ORADB oradb PRIMARY READ WRITEsql
Verify ARCHIVELOG Mode
Data Guard requires the primary to be in ARCHIVELOG mode so that redo data is archived and can be shipped to the standby.
SQL> archive log list;
Database log mode Archive Mode
Automatic archival Enabled
Archive destination USE_DB_RECOVERY_FILE_DEST
Oldest online log sequence 12
Next log sequence to archive 14
Current log sequence 14sql
Check Online Redo Log Groups
We need to know the number and size of online redo log groups to determine how many standby redo logs to create (rule: standby redo log groups = online redo log groups + 1).
SQL> SELECT group#, thread#, bytes/1024/1024 MB, members, status FROM v$log;
GROUP# THREAD# MB MEMBERS STATUS
---------- ---------- ---------- ---------- ----------------
1 1 200 2 INACTIVE
2 1 200 2 CURRENT
3 1 200 2 INACTIVEsql
Check ASM Disk Groups
Verify that both +DATA and +FRA have sufficient free space for the standby configuration overhead and archive logs.
SQL> SELECT name, total_mb, free_mb FROM v$asm_diskgroup;
NAME TOTAL_MB FREE_MB
------------------------------ ---------- ----------
DATA 20472 15508
FRA 10236 9500sql
Check Database Version
SQL> SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Productionsql
List Datafiles & Log File Members
Document the current datafiles and log file members. These are stored in ASM, which is important for the standby's db_file_name_convert parameter later.
SQL> SELECT name FROM v$datafile;
NAME
--------------------------------------------------------------------------------
+DATA/ORADB/DATAFILE/system.257.1205523331
+DATA/ORADB/DATAFILE/sysaux.258.1205523387
+DATA/ORADB/DATAFILE/undotbs1.259.1205523413
+DATA/ORADB/DATAFILE/soe.267.1205602145
+DATA/ORADB/DATAFILE/users.260.1205523413
SQL> SELECT member FROM v$logfile;
MEMBER
--------------------------------------------------------------------------------
+DATA/ORADB/ONLINELOG/group_3.264.1205523499
+FRA/ORADB/ONLINELOG/group_3.259.1205523501
+DATA/ORADB/ONLINELOG/group_2.263.1205523489
+FRA/ORADB/ONLINELOG/group_2.257.1205523493
+DATA/ORADB/ONLINELOG/group_1.262.1205523489
+FRA/ORADB/ONLINELOG/group_1.258.1205523493sql
STEP 2 Create Standby Redo Logs & Enable Force Logging
Standby redo logs (SRLs) are used by the standby database to receive redo data from the primary in real-time. They must be created on the primary first so that the standby inherits them during duplication. The rule is: number of SRL groups = online redo log groups + 1.
Why +1? The extra group ensures that there's always a standby redo log available for writing while others are being archived — preventing redo transport stalls.
SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
GROUP 4 ('+DATA', '+FRA') SIZE 200M;
Database altered.
SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
GROUP 5 ('+DATA', '+FRA') SIZE 200M;
Database altered.
SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
GROUP 6 ('+DATA', '+FRA') SIZE 200M;
Database altered.
SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
GROUP 7 ('+DATA', '+FRA') SIZE 200M;
Database altered.sql
+DATA and +FRA for redundancy, matching the online redo log layout.
Verify Standby Redo Logs
SQL> SELECT group#, thread#, bytes/1024/1024 MB, status FROM v$standby_log;
GROUP# THREAD# MB STATUS
---------- ---------- ---------- ----------
4 1 200 UNASSIGNED
5 1 200 UNASSIGNED
6 1 200 UNASSIGNED
7 1 200 UNASSIGNEDsql
Enable Force Logging
Force logging ensures that all changes are written to the redo logs, even those from operations marked as NOLOGGING. Without this, some data changes could be missing on the standby, causing inconsistencies.
SQL> ALTER DATABASE FORCE LOGGING;
Database altered.
SQL> SELECT force_logging FROM v$database;
FORCE_LOGGING
---------------------------------------
YESsql
STEP 3 Configure Data Guard Parameters on Primary
Now we set the critical Data Guard parameters on the primary database. Each parameter plays a specific role in the redo transport and failover mechanism.
3.1 — Log Archive Config
This tells the primary about all databases in the Data Guard configuration by their db_unique_name. It's required for redo transport to know its targets.
SQL> ALTER SYSTEM SET log_archive_config='DG_CONFIG=(oradb,oradb_stby)' SCOPE=BOTH;
System altered.sql
3.2 — Archive Destinations
log_archive_dest_1 is the local archive destination — where archived logs are stored on the primary itself (using the Fast Recovery Area). log_archive_dest_2 is the remote destination — it ships redo to the standby database asynchronously via the TNS service name oradb_stby.
SQL> ALTER SYSTEM SET log_archive_dest_1='LOCATION=USE_DB_RECOVERY_FILE_DEST
VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=oradb' SCOPE=BOTH;
System altered.
SQL> ALTER SYSTEM SET log_archive_dest_2='SERVICE=oradb_stby ASYNC
VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=oradb_stby' SCOPE=BOTH;
System altered.sql
ASYNC (asynchronous) means the primary does not wait for the standby to acknowledge receipt before committing transactions. This gives better performance on the primary at the cost of potential minimal data loss during failover. Use SYNC for zero data loss (Maximum Protection / Maximum Availability mode).
3.3 — Enable Archive Destinations
SQL> ALTER SYSTEM SET log_archive_dest_state_1=ENABLE SCOPE=BOTH;
System altered.
SQL> ALTER SYSTEM SET log_archive_dest_state_2=ENABLE SCOPE=BOTH;
System altered.sql
3.4 — Archive Log Format
Defines the naming convention for archived redo logs. %t = thread number, %s = sequence number, %r = resetlogs ID. This requires SCOPE=SPFILE because it's a static parameter.
SQL> ALTER SYSTEM SET log_archive_format='%t_%s_%r.arc' SCOPE=SPFILE;
System altered.sql
3.5 — Standby File Management
Setting this to AUTO tells Oracle to automatically create corresponding datafiles on the standby when new datafiles are added on the primary. Without this, you'd need to manually manage files on the standby.
SQL> ALTER SYSTEM SET standby_file_management=AUTO SCOPE=BOTH;
System altered.sql
3.6 — FAL (Fetch Archive Log) Configuration
fal_server tells the primary where the standby can fetch missing archived logs from. fal_client identifies itself. These are used during gap resolution — if the standby misses some redo, it uses FAL to request the missing logs.
SQL> ALTER SYSTEM SET fal_server='oradb_stby' SCOPE=BOTH;
System altered.
SQL> ALTER SYSTEM SET fal_client='oradb' SCOPE=BOTH;
System altered.sql
3.7 — Enable Flashback Database
Flashback allows you to quickly reinstate a failed primary after a failover, without needing to rebuild it from scratch. This is highly recommended for any Data Guard setup.
SQL> ALTER DATABASE FLASHBACK ON;
Database altered.sql
3.8 — Enable Data Guard Broker
The DG Broker provides a centralized management framework for Data Guard. It simplifies switchover, failover, and monitoring through the dgmgrl command-line tool.
SQL> ALTER SYSTEM SET dg_broker_start=TRUE SCOPE=BOTH;
System altered.sql
Verify All Parameters
SQL> SHOW PARAMETER log_archive_config;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_config string DG_CONFIG=(oradb,oradb_stby)
SQL> SHOW PARAMETER log_archive_dest_2;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_2 string SERVICE=oradb_stby ASYNC VALID
_FOR=(ONLINE_LOGFILES,PRIMARY_
ROLE) DB_UNIQUE_NAME=oradb_stb
y
SQL> SHOW PARAMETER fal_server;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
fal_server string oradb_stby
SQL> SHOW PARAMETER fal_client;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
fal_client string oradb
SQL> SHOW PARAMETER dg_broker_start;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
dg_broker_start boolean TRUEsql
STEP 4 Restart Primary & Verify
Some parameters (like log_archive_format) require a restart to take effect because they were set with SCOPE=SPFILE. Restart the primary database to activate all changes.
SQL> SHUTDOWN IMMEDIATE;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> STARTUP;
ORACLE instance started.
Total System Global Area 1996486272 bytes
Fixed Size 8898176 bytes
Variable Size 452984832 bytes
Database Buffers 1526726656 bytes
Redo Buffers 7876608 bytes
Database mounted.
Database opened.
SQL> SELECT name, db_unique_name, database_role, open_mode FROM v$database;
NAME DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE
--------- ------------------------------ ---------------- --------------------
ORADB oradb PRIMARY READ WRITEsql
STEP 5 Configure Network (TNS) on Both Servers
Data Guard uses Oracle Net Services (TNS) to communicate between primary and standby. We need to configure tnsnames.ora on both servers so each can resolve the other's service name. The listener on the standby also needs a static registration since the standby database won't be fully open to register itself dynamically.
5.1 — Configure tnsnames.ora on Primary (dg1)
As the oracle user on dg1:
[oracle@dg1 ~]$ cd $ORACLE_HOME/network/admin
# Backup existing file
[oracle@dg1 admin]$ cp tnsnames.ora tnsnames.ora.bkp_$(date +%Y%m%d)
[oracle@dg1 admin]$ vi tnsnames.orashell
Add these entries (the same entries go on both servers):
oradb =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = oradb.bracecoder)
)
)
oradb_stby =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.7)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = oradb_stby.bracecoder)
)
)tnsnames.ora
db_domain parameter is set to bracecoder, so the full service name includes the domain suffix. If you use just oradb without the domain, tnsping may succeed but sqlplus connections may fail with ORA-12514.
5.2 — Test TNS from Primary
[oracle@dg1 admin]$ tnsping oradb
[oracle@dg1 admin]$ tnsping oradb_stby
# Test actual database connection
[oracle@dg1 admin]$ sqlplus sys/admin@oradb as sysdba
SQL> SELECT name, db_unique_name FROM v$database;
NAME DB_UNIQUE_NAME
--------- ------------------------------
ORADB oradbshell
5.3 — Configure Listener on Standby (dg2)
The standby database won't be running when we first configure Data Guard, so it can't dynamically register with the listener. We need static registration in the listener.ora. As the grid user on dg2:
[grid@dg2 ~]$ cd $ORACLE_HOME/network/admin
[grid@dg2 admin]$ cp listener.ora listener.ora.bkp
[grid@dg2 admin]$ vi listener.orashell
Add the SID_LIST_LISTENER block at the top of the file:
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = oradb_stby.bracecoder)
(ORACLE_HOME = /u01/app/oracle/product/19.0.0/db_1)
(SID_NAME = oradb)
)
)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.7)(PORT = 1521))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
)
ENABLE_GLOBAL_DYNAMIC_ENDPOINT_LISTENER=ONlistener.ora
SID_NAME is oradb (the actual SID/instance name on this server), while GLOBAL_DBNAME is oradb_stby.bracecoder (the service name used for TNS connections). This static entry lets RMAN connect to the standby even when the instance is in NOMOUNT state.
Reload the listener and verify:
[grid@dg2 admin]$ lsnrctl reload
[grid@dg2 admin]$ lsnrctl status
Service "oradb_stby.bracecoder" has 1 instance(s).
Instance "oradb", status UNKNOWN, has 1 handler(s) for this service...shell
5.4 — Configure tnsnames.ora on Standby (dg2)
Add the same TNS entries as on dg1, for both grid and oracle users:
# As grid user
[grid@dg2 ~]$ cd $ORACLE_HOME/network/admin
[grid@dg2 admin]$ vi tnsnames.ora
# As oracle user
[oracle@dg2 ~]$ cd $ORACLE_HOME/network/admin
[oracle@dg2 admin]$ vi tnsnames.orashell
Add the identical entries (same as dg1's tnsnames.ora shown above).
5.5 — Test TNS from Standby
[oracle@dg2 admin]$ tnsping oradb
[oracle@dg2 admin]$ tnsping oradb_stby
# Test connection to PRIMARY from standby
[oracle@dg2 admin]$ sqlplus sys/admin@oradb as sysdba
SQL> SELECT name FROM v$database;
NAME
---------
ORADBshell
STEP 6 Create Password File & Standby Controlfile on Primary
The standby needs an identical password file (so SYS can authenticate over the network for redo transport) and a standby controlfile (which contains the database structure metadata for the standby).
6.1 — Verify/Create Password File
[oracle@dg1 ~]$ cd $ORACLE_HOME/dbs
[oracle@dg1 dbs]$ ls -l orapworadb
# If not found, create it:
[oracle@dg1 dbs]$ orapwd file=orapworadb password=admin entries=10 format=12shell
6.2 — Create Standby Controlfile
A standby controlfile is a special controlfile that tells Oracle this database is a standby. It's different from a normal backup controlfile.
SQL> ALTER DATABASE CREATE STANDBY CONTROLFILE AS '/tmp/standby_oradb.ctl';
Database altered.
[oracle@dg1 dbs]$ ls -l /tmp/standby_oradb.ctlsql
6.3 — Copy Files to Standby
Use SCP to transfer the password file and standby controlfile to dg2:
# Test SSH first
[oracle@dg1 ~]$ ssh oracle@dg2 hostname
dg2.bracecoder
# Copy password file
[oracle@dg1 ~]$ scp $ORACLE_HOME/dbs/orapworadb oracle@dg2:$ORACLE_HOME/dbs/
# Copy standby controlfile
[oracle@dg1 ~]$ scp /tmp/standby_oradb.ctl oracle@dg2:/tmp/shell
6.4 — Verify on Standby
[oracle@dg2 ~]$ ls -l $ORACLE_HOME/dbs/orapworadb
[oracle@dg2 ~]$ ls -l /tmp/standby_oradb.ctlshell
STEP 7 Prepare Standby Server (DG2) — ASM Directories
Before we can create the standby database, we need to prepare the ASM directory structure on dg2 to receive the datafiles.
[grid@dg2 ~]$ export ORACLE_SID=+ASM
[grid@dg2 ~]$ asmcmd
ASMCMD> mkdir +DATA/ORADB_STBY
ASMCMD> mkdir +FRA/ORADB_STBY
ASMCMD> ls -l +DATA
Type Redund Striped Time Sys Name
Y ASM/
N ORADB_STBY/
ASMCMD> ls -l +FRA
Type Redund Striped Time Sys Name
N ORADB_STBY/
ASMCMD> exitshell
db_unique_name of the standby (oradb_stby). Oracle ASM automatically organizes files under this directory when the standby database is created.
STEP 8 Create Standby Initialization Parameter File
The standby needs its own init parameter file with settings that mirror the primary, but with key differences — notably db_unique_name, control_files path, and the db_file_name_convert/log_file_name_convert parameters that map primary paths to standby paths.
[oracle@dg2 ~]$ cd $ORACLE_HOME/dbs
[oracle@dg2 dbs]$ cat > initoradb.ora << 'EOF'
*.audit_file_dest='/u01/app/oracle/admin/oradb/adump'
*.audit_trail='db'
*.compatible='19.0.0.0'
*.control_files='+DATA/ORADB_STBY/CONTROLFILE/control01.ctl','+FRA/ORADB_STBY/CONTROLFILE/control02.ctl'
*.db_block_size=8192
*.db_create_file_dest='+DATA'
*.db_domain='bracecoder'
*.db_file_name_convert='+DATA/ORADB','+DATA/ORADB_STBY','+FRA/ORADB','+FRA/ORADB_STBY'
*.db_name='oradb'
*.db_recovery_file_dest='+FRA'
*.db_recovery_file_dest_size=10G
*.db_unique_name='oradb_stby'
*.diagnostic_dest='/u01/app/oracle'
*.dispatchers='(PROTOCOL=TCP) (SERVICE=oradbXDB)'
*.fal_client='oradb_stby'
*.fal_server='oradb'
*.log_archive_config='DG_CONFIG=(oradb,oradb_stby)'
*.log_archive_dest_1='LOCATION=USE_DB_RECOVERY_FILE_DEST VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=oradb_stby'
*.log_archive_dest_2='SERVICE=oradb ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=oradb'
*.log_archive_dest_state_1='ENABLE'
*.log_archive_dest_state_2='ENABLE'
*.log_archive_format='%t_%s_%r.arc'
*.log_file_name_convert='+DATA/ORADB','+DATA/ORADB_STBY','+FRA/ORADB','+FRA/ORADB_STBY'
*.open_cursors=300
*.pga_aggregate_target=512M
*.processes=300
*.remote_login_passwordfile='EXCLUSIVE'
*.sga_target=1536M
*.standby_file_management='AUTO'
*.undo_tablespace='UNDOTBS1'
EOFshell
db_unique_name=oradb_stby(notoradb)control_filespoint to+DATA/ORADB_STBYand+FRA/ORADB_STBYdb_file_name_convertandlog_file_name_convertmap primary paths → standby pathsfal_clientandfal_serverare reversed compared to primarylog_archive_dest_2points back to the primary (for role reversal)
Create Audit Directory & Start NOMOUNT
The audit directory must exist before the instance can start, otherwise you'll get ORA-09925.
[oracle@dg2 dbs]$ mkdir -p /u01/app/oracle/admin/oradb/adump
[oracle@dg2 dbs]$ chmod 755 /u01/app/oracle/admin/oradb/adumpshell
[oracle@dg2 dbs]$ sqlplus / as sysdba
SQL> STARTUP NOMOUNT PFILE='$ORACLE_HOME/dbs/initoradb.ora';
ORACLE instance started.
Total System Global Area 1610609200 bytes
Fixed Size 8897072 bytes
Variable Size 385875968 bytes
Database Buffers 1207959552 bytes
Redo Buffers 7876608 bytes
SQL> SELECT instance_name, status FROM v$instance;
INSTANCE_NAME STATUS
---------------- ------------
oradb STARTEDsql
Create SPFILE from PFILE
Convert the text-based PFILE to a binary SPFILE so the instance uses it by default on subsequent startups.
SQL> CREATE SPFILE FROM PFILE='$ORACLE_HOME/dbs/initoradb.ora';
File created.
SQL> SHUTDOWN IMMEDIATE;
ORACLE instance shut down.
SQL> STARTUP NOMOUNT;
ORACLE instance started.sql
STEP 9 Restore Controlfiles to ASM
Copy the standby controlfile from /tmp into the ASM disk groups using asmcmd. This must be done as the grid user since ASM is owned by grid.
[grid@dg2 ~]$ asmcmd cp /tmp/standby_oradb.ctl +DATA/ORADB_STBY/control01.ctl
copying /tmp/standby_oradb.ctl -> +DATA/ORADB_STBY/control01.ctl
[grid@dg2 ~]$ asmcmd cp /tmp/standby_oradb.ctl +FRA/ORADB_STBY/control02.ctl
copying /tmp/standby_oradb.ctl -> +FRA/ORADB_STBY/control02.ctlshell
Fix Controlfile Path in SPFILE
ASM may create the controlfile at a different internal path than what the SPFILE expects. We need to update the control_files parameter to match the actual ASM alias paths (without the /CONTROLFILE/ subdirectory).
SQL> CREATE PFILE='/tmp/initoradb_new.ora' FROM SPFILE;sql
Edit /tmp/initoradb_new.ora and update the control_files line:
# Change FROM:
*.control_files='+DATA/ORADB_STBY/CONTROLFILE/control01.ctl','+FRA/ORADB_STBY/CONTROLFILE/control02.ctl'
# Change TO:
*.control_files='+DATA/ORADB_STBY/control01.ctl','+FRA/ORADB_STBY/control02.ctl'pfile
Recreate SPFILE and mount the standby:
SQL> STARTUP NOMOUNT PFILE='/tmp/initoradb_new.ora';
ORACLE instance started.
SQL> CREATE SPFILE FROM PFILE='/tmp/initoradb_new.ora';
File created.
SQL> SHUTDOWN IMMEDIATE;
ORACLE instance shut down.
SQL> STARTUP MOUNT;
ORACLE instance started.
...
Database mounted.
SQL> SELECT name, db_unique_name, database_role, open_mode FROM v$database;
NAME DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE
--------- ------------------------------ ---------------- --------------------
ORADB oradb_stby PHYSICAL STANDBY MOUNTEDsql
PHYSICAL STANDBY. This confirms the standby controlfile is properly configured.
STEP 10 Duplicate Database Using RMAN
This is the most critical step — we use RMAN's Active Database Duplication to copy the entire primary database to the standby over the network. The standby must be in NOMOUNT state for this to work.
Prepare Standby for Duplication
The standby was in MOUNT from the previous step. RMAN requires NOMOUNT for DUPLICATE ... FOR STANDBY:
SQL> SHUTDOWN IMMEDIATE;
Database dismounted.
ORACLE instance shut down.
SQL> STARTUP NOMOUNT;
ORACLE instance started.sql
Run RMAN Duplication
Connect RMAN to both the TARGET (primary) and AUXILIARY (standby) databases simultaneously, then run the duplication:
[oracle@dg2 ~]$ rman TARGET sys/admin@oradb AUXILIARY sys/admin@oradb_stby
connected to target database: ORADB (DBID=2958788253)
connected to auxiliary database: ORADB (not mounted)shell
RMAN> DUPLICATE TARGET DATABASE
FOR STANDBY
FROM ACTIVE DATABASE
DORECOVER
NOFILENAMECHECK;rman
DUPLICATE TARGET DATABASE— Copy the target (primary) databaseFOR STANDBY— Create it as a standby (not a standalone clone)FROM ACTIVE DATABASE— Copy directly over the network (no backup needed)DORECOVER— Apply any redo generated during the copy to make the standby currentNOFILENAMECHECK— Don't verify filenames match between primary and standby (needed since ASM paths differ)
The duplication process takes several minutes. RMAN will:
- Copy the password file
- Restore a fresh standby controlfile
- Mount the standby database
- Copy all datafiles over the network
- Archive the current log and restore recent archive logs
- Recover the standby to make it current
Starting Duplicate Db at 08-FEB-2026 16:00:57
using target database control file instead of recovery catalog
allocated channel: ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: SID=44 device type=DISK
current log archived
contents of Memory Script:
{
backup as copy reuse
passwordfile auxiliary format '/u01/app/oracle/product/19.0.0/db_1/dbs/orapworadb' ;
}
executing Memory Script
Starting backup at 08-FEB-2026 16:01:01
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=97 device type=DISK
Finished backup at 08-FEB-2026 16:01:05
contents of Memory Script:
{
restore clone from service 'oradb' standby controlfile;
}
executing Memory Script
Starting restore at 08-FEB-2026 16:01:05
using channel ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: restoring control file
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:04
output file name=+DATA/ORADB_STBY/control01.ctl
output file name=+FRA/ORADB_STBY/control02.ctl
Finished restore at 08-FEB-2026 16:01:12
contents of Memory Script:
{
sql clone 'alter database mount standby database';
}
executing Memory Script
sql statement: alter database mount standby database
RMAN-05529: warning: DB_FILE_NAME_CONVERT resulted in invalid ASM names; names changed to disk group only.
contents of Memory Script:
{
set newname for tempfile 1 to
"+DATA";
switch clone tempfile all;
set newname for datafile 1 to
"+DATA";
set newname for datafile 3 to
"+DATA";
set newname for datafile 4 to
"+DATA";
set newname for datafile 5 to
"+DATA";
set newname for datafile 7 to
"+DATA";
restore
from nonsparse from service
'oradb' clone database
;
sql 'alter system archive log current';
}
executing Memory Script
executing command: SET NEWNAME
renamed tempfile 1 to +DATA in control file
executing command: SET NEWNAME
executing command: SET NEWNAME
executing command: SET NEWNAME
executing command: SET NEWNAME
executing command: SET NEWNAME
Starting restore at 08-FEB-2026 16:01:19
using channel ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00001 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:45
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00003 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:26
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00004 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:07
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00005 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:35
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00007 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:02
Finished restore at 08-FEB-2026 16:03:17
sql statement: alter system archive log current
current log archived
contents of Memory Script:
{
restore clone force from service 'oradb'
archivelog from scn 2769737;
switch clone datafile all;
}
executing Memory Script
Starting restore at 08-FEB-2026 16:03:18
using channel ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: starting archived log restore to default destination
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: restoring archived log
archived log thread=1 sequence=17
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:01
channel ORA_AUX_DISK_1: starting archived log restore to default destination
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: restoring archived log
archived log thread=1 sequence=18
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:02
Finished restore at 08-FEB-2026 16:03:21
datafile 1 switched to datafile copy
input datafile copy RECID=7 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/system.262.1224691283
datafile 3 switched to datafile copy
input datafile copy RECID=8 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/sysaux.263.1224691327
datafile 4 switched to datafile copy
input datafile copy RECID=9 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/undotbs1.264.1224691355
datafile 5 switched to datafile copy
input datafile copy RECID=10 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/soe.265.1224691361
datafile 7 switched to datafile copy
input datafile copy RECID=11 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/users.266.1224691397
contents of Memory Script:
{
set until scn 2770069;
recover
standby
clone database
delete archivelog
;
}
executing Memory Script
executing command: SET until clause
Starting recover at 08-FEB-2026 16:03:21
using channel ORA_AUX_DISK_1
starting media recovery
archived log for thread 1 with sequence 17 is already on disk as file +FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_17.261.1224691399
archived log for thread 1 with sequence 18 is already on disk as file +FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_18.262.1224691401
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_17.261.1224691399 thread=1 sequence=17
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_18.262.1224691401 thread=1 sequence=18
media recovery complete, elapsed time: 00:00:01
Finished recover at 08-FEB-2026 16:03:24
contents of Memory Script:
{
delete clone force archivelog all;
}
executing Memory Script
released channel: ORA_DISK_1
released channel: ORA_AUX_DISK_1
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=97 device type=DISK
deleted archived log
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_17.261.1224691399 RECID=1 STAMP=1224691398
deleted archived log
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_18.262.1224691401 RECID=2 STAMP=1224691400
Deleted 2 objects
Finished Duplicate Db at 08-FEB-2026 16:04:02rman output
Verify Standby After Duplication
SQL> SELECT name, db_unique_name, database_role, open_mode, protection_mode
FROM v$database;
NAME DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE PROTECTION_MODE
--------- ----------------- ---------------- ------------ --------------------
ORADB oradb_stby PHYSICAL STANDBY MOUNTED MAXIMUM PERFORMANCE
SQL> SELECT name FROM v$datafile;
NAME
--------------------------------------------------------------------------------
+DATA/ORADB_STBY/DATAFILE/system.262.1224691283
+DATA/ORADB_STBY/DATAFILE/sysaux.263.1224691327
+DATA/ORADB_STBY/DATAFILE/undotbs1.264.1224691355
+DATA/ORADB_STBY/DATAFILE/soe.265.1224691361
+DATA/ORADB_STBY/DATAFILE/users.266.1224691397
SQL> SELECT group#, thread#, bytes/1024/1024 MB, status FROM v$standby_log;
GROUP# THREAD# MB STATUS
---------- ---------- ---------- ----------
4 1 200 UNASSIGNED
5 1 200 UNASSIGNED
6 1 200 UNASSIGNED
7 1 200 UNASSIGNEDsql
+DATA/ORADB_STBY. The database role is confirmed as PHYSICAL STANDBY.
STEP 11 Start Managed Recovery & Verify Synchronization
The final step is to start Managed Recovery Process (MRP) on the standby. MRP continuously applies redo data received from the primary, keeping the standby in sync.
Start MRP on Standby (dg2)
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE
USING CURRENT LOGFILE DISCONNECT FROM SESSION;
Database altered.sql
RECOVER MANAGED STANDBY DATABASE— Start the managed recovery processUSING CURRENT LOGFILE— Apply redo in real-time from standby redo logs (real-time apply)DISCONNECT FROM SESSION— Run MRP as a background process so you get your SQL prompt back
SQL
SQL> ALTER SYSTEM SET log_archive_dest_2='SERVICE=oradb_stby ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=oradb_stby' SCOPE=BOTH;
System altered.
SQL> SELECT dest_id, status, error FROM v$archive_dest_status WHERE dest_id = 2;
DEST_ID STATUS
---------- ---------
ERROR
-----------------------------------------------------------------
2 VALID
SQL>
Verify MRP Status on Standby
SQL> SELECT process, status, thread#, sequence#, block#, blocks
FROM v$managed_standby
WHERE process LIKE 'MRP%' OR process LIKE 'RFS%';
PROCESS STATUS THREAD# SEQUENCE# BLOCK# BLOCKS
--------- ------------ ---------- ---------- ---------- ----------
MRP0 APPLYING_LOG 1 21 197 409600
RFS IDLE 1 21 197 1
RFS IDLE 0 0 0 0sql
APPLYING_LOG status — the standby is actively receiving and applying redo from the primary. RFS (Remote File Server) shows the redo transport connection is established. Data Guard is fully operational!
Verify Archive Destination on Primary (dg1)
SQL> SELECT dest_id, status, error FROM v$archive_dest_status WHERE dest_id = 2;
DEST_ID STATUS ERROR
---------- --------- --------------------------------------------------
2 VALIDsql
VALID with no errors means redo transport from primary to standby is working correctly.
Conclusion
Congratulations! You have successfully configured Oracle Data Guard with a Physical Standby Database. Here's a summary of what we accomplished:
| Component | Status |
|---|---|
| Primary Database (oradb @ dg1) | ✔ Running — READ WRITE |
| Standby Database (oradb_stby @ dg2) | ✔ Running — MOUNTED (Physical Standby) |
| Redo Transport | ✔ Active — ASYNC mode |
| Log Apply (MRP) | ✔ APPLYING_LOG |
| Protection Mode | Maximum Performance |
| Flashback | ✔ Enabled on Primary |
| DG Broker | ✔ Started |
In the next part (Day 2), we'll cover configuring the Data Guard Broker for simplified management, performing switchover and failover operations, enabling Active Data Guard (read-only standby), and setting up monitoring and alerts.
- Use Oracle Secure Backup or RMAN backups on the standby to offload backup workload from the primary
- Consider
SYNCredo transport for zero data loss if your network latency allows it - Always test switchover/failover in a non-production environment first
- Monitor
v$dataguard_statsandv$archive_dest_statusregularly for transport/apply lag - Set up email alerts for Data Guard gaps or transport errors
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to share your thoughts!