Announcement! Thrilled and proud to share that I’ve been selected as an Oracle ACE Apprentice!
A practical Oracle Database blog covering tuning, backup, recovery, HA/DR, troubleshooting, and real-world DBA learnings from 10+ years of hands-on experience.
GoldenGate is not just replication.
when someone says:
๐จ “Oracle database is slow”
Before doing anything:
๐ Don’t jump into AWR immediately
๐ Don’t restart database
๐ Don’t assume it’s “CPU issue”
First rule:
Find the bottleneck, not the symptom
SELECT event, total_waits, time_waited
FROM v$system_event
ORDER BY time_waited DESC
FETCH FIRST 5 ROWS ONLY;
| If you see | Meaning |
|---|---|
| db file sequential read | Index I/O |
| db file scattered read | Full table scan |
| log file sync | Commit issue |
| CPU time high | CPU pressure |
| enq: TX row lock | Blocking |
✅ Now you know WHERE to focus
SELECT sid, serial#, username, sql_id, event, wait_class, seconds_in_wait
FROM v$session
WHERE status = 'ACTIVE'
ORDER BY seconds_in_wait DESC;
๐ Identify:
Long running sessions
Blocking sessions
Top wait events
SELECT blocking_session, sid, serial#, wait_class, event
FROM v$session
WHERE blocking_session IS NOT NULL;
๐ That’s your issue (most critical)
SELECT sql_id, executions, elapsed_time/1000000 total_sec
FROM v$sql
ORDER BY elapsed_time DESC
FETCH FIRST 10 ROWS ONLY;
๐ Look for:
High elapsed time
High executions
Same SQL repeated
SELECT stat_name, value
FROM v$sysstat
WHERE stat_name LIKE '%CPU%';
๐ Or OS level:
top
Bad SQL
Missing indexes
High concurrency
SELECT file#, phyrds, phywrts
FROM v$filestat;
๐ OR check wait events:
db file sequential read → index reads
db file scattered read → full scans
SELECT event, time_waited
FROM v$system_event
WHERE event = 'log file sync';
๐ If high:
Too many commits
Slow disk
Look for:
SQL*Net message from client
SQL*Net more data
๐ Usually application-side delay
Only AFTER initial diagnosis:
@?/rdbms/admin/awrrpt.sql
Focus on:
Top SQL
Wait events
Load profile
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('<SQL_ID>', NULL, 'ALLSTATS LAST'));
๐ Check:
Full table scans
Bad joins
Wrong cardinality
| Issue | Action |
|---|---|
| Blocking | Kill session / fix transaction |
| Bad SQL | Tune query / add index |
| CPU high | Optimize SQL |
| I/O high | Add indexes / check storage |
| Redo issue | Reduce commits |
| Stats stale | Gather stats |
When DB is slow:
Check wait events ✅
Check active sessions ✅
Check blocking ✅
Identify top SQL ✅
Drill into execution plan ✅
If interviewer asks:
๐ “Database is slow, what will you do?”
Say:
“First I check system wait events to identify where time is spent, then I analyze active sessions and blocking, followed by identifying top SQL and reviewing execution plans before taking corrective action.”
80% issues = bad SQL or missing index
10% = locking
10% = infrastructure (CPU/I/O)
“Performance tuning is not about fixing everything…
It’s about fixing the right thing first.”
Time: 11:15 AM
Environment: Production
Users complaint:
“Application is very slow. Pages are hanging.”
You have no prior info.
Identify root cause in 5–10 minutes
Avoid guesswork
Take correct action
You run:
SELECT event, total_waits, time_waited
FROM v$system_event
ORDER BY time_waited DESC
FETCH FIRST 5 ROWS ONLY;
EVENT TIME_WAITED
---------------------------- -----------
enq: TX - row lock contention 9854321
db file sequential read 234567
CPU time 123456
๐ What does this indicate?
Top wait = row lock contention
This is blocking issue, not CPU or I/O
You run:
SELECT blocking_session, sid, serial#, event
FROM v$session
WHERE blocking_session IS NOT NULL;
BLOCKING_SESSION SID EVENT
--------------- ---- -----------------------------
145 201 enq: TX - row lock contention
145 305 enq: TX - row lock contention
145 410 enq: TX - row lock contention
๐ What do you conclude?
Session 145 is blocking multiple sessions
This is a critical production issue
SELECT sid, serial#, username, sql_id, status
FROM v$session
WHERE sid = 145;
SID SERIAL# USERNAME SQL_ID STATUS
145 6789 APPUSER abc123xyz INACTIVE
๐ Why is this dangerous?
Session is INACTIVE but holding locks
Likely:
Uncommitted transaction
Application issue
You have 2 options:
๐ Wait for user to commit/rollback
๐ Kill the session
๐ What will you do?
If production is impacted → Kill session
ALTER SYSTEM KILL SESSION '145,6789' IMMEDIATE;
Check again:
SELECT blocking_session FROM v$session
WHERE blocking_session IS NOT NULL;
No rows selected
Blocking removed
Application restored
Issue resolved in minutes
Users say:
“Reports are very slow”
SELECT event, time_waited
FROM v$system_event
ORDER BY time_waited DESC;
db file scattered read 9999999
CPU time 8888888
๐ What does this indicate?
Heavy Full Table Scans
Likely:
Missing indexes
Bad SQL
SELECT sql_id, elapsed_time, executions
FROM v$sql
ORDER BY elapsed_time DESC
FETCH FIRST 5 ROWS ONLY;
๐ What will you do next?
Pick top SQL_ID
Check execution plan:
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('<SQL_ID>', NULL, 'ALLSTATS LAST'));
Full table scan detected
Missing index
CREATE INDEX idx_col ON table_name(column_name);
Check wait events
Identify type of issue
Find sessions / SQL
Take targeted action
๐ “How will you handle blocking in production?”
“I first check wait events to confirm locking, identify blocking session using V$SESSION, verify the SQL and session state, and if it’s impacting users, I kill the blocking session and inform application team.”
Answer:
SQL Tuning is the process of improving the performance of SQL queries by optimizing execution plans, reducing resource consumption (CPU, I/O), and improving response time.
Answer:
We can identify slow SQL using:
V$SESSION (currently running SQL)
V$SQL (high resource SQL)
AWR reports
ASH reports
Example:
SELECT sql_id, elapsed_time, executions
FROM v$sql
ORDER BY elapsed_time DESC;
Answer:
SQL_ID is a unique identifier for a SQL statement in Oracle.
It helps in:
Tracking SQL performance
Fetching execution plans
Monitoring historical execution
Answer:
Using:
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('<SQL_ID>', NULL, 'ALLSTATS LAST'));
This shows:
Execution steps
Actual vs estimated rows
Cost and time
Answer:
| Feature | EXPLAIN PLAN | DISPLAY_CURSOR |
|---|---|---|
| Type | Estimated | Actual |
| Source | Optimizer | Cursor cache |
| Accuracy | Less | High |
Answer:
Using AWR views:
DBA_HIST_SQLSTAT
DBA_HIST_SQLTEXT
This helps analyze:
Plan changes
Execution trends
Performance degradation
Answer:
PLAN_HASH_VALUE uniquely represents an execution plan.
If it changes, it means the execution plan has changed.
Answer:
A SQL Profile is a set of optimizer hints that helps Oracle generate a better execution plan without modifying the SQL.
Answer:
Identify best plan:
START coe_xfr_sql_profile.sql <SQL_ID>
Generate profile:
START coe_xfr_sql_profile.sql <SQL_ID> <PLAN_HASH_VALUE>
Apply:
@coe_xfr_sql_profile_<SQL_ID>_<PLAN_HASH>.sql
Answer:
SELECT sql_id, plan_hash_value
FROM v$sql
WHERE sql_id = '<SQL_ID>';
Also:
SELECT * FROM dba_sql_profiles;
Answer:
EXEC DBMS_SQLTUNE.DROP_SQL_PROFILE('profile_name');
Answer:
SQL Tuning Advisor is an Oracle tool that analyzes SQL and provides recommendations such as:
Index creation
SQL Profile
Statistics gathering
Alternative execution plans
Answer:
BEGIN
DBMS_SQLTUNE.create_tuning_task(
task_name => 'TASK1',
sql_id => '<SQL_ID>',
time_limit => 60
);
DBMS_SQLTUNE.execute_tuning_task('TASK1');
END;
/
Answer:
Create index
Gather statistics
Accept SQL Profile
Rewrite query
Suggest better execution plan
Answer:
Optimizer relies on statistics to estimate:
Cardinality
Cost
Execution plan
Stale statistics can lead to bad plans.
Answer:
When optimizer incorrectly estimates number of rows, leading to:
Wrong join method
Poor execution plan
Answer:
| Scenario | Use |
|---|---|
| Temporary fix | SQL Profile |
| Long-term optimization | Index |
Answer:
Missing indexes
Full table scans
Stale statistics
Bad execution plans
High I/O operations
Answer:
It shows actual execution statistics of the last run, including:
Actual rows processed
Actual time taken
Question:
A query suddenly became slow. What will you do?
Answer:
Capture SQL_ID
Check current execution plan
Compare with historical plans
Identify plan change
Use COE or SQL Profile
Run SQL Tuning Advisor
Validate performance
Always say:
“First I check whether the issue is due to plan change or data growth, then I decide whether to fix using SQL Profile, Index, or statistics.”
SQL_ID → Unique identifier
PLAN_HASH_VALUE → Execution plan identifier
SQL Profile → Optimizer hint set
AWR → Historical performance data
ASH → Real-time session activity
What is Active Data Guard?
Difference between Data Guard and Active Data Guard?
What is MRP process?
What is FAL_SERVER and FAL_CLIENT?
What is the use of Standby Redo Logs?
What is Switchover vs Failover?
Why FORCE LOGGING is required?
How do you check Data Guard lag?
What is DB_UNIQUE_NAME?
What is NOFILENAMECHECK in RMAN duplicate?
Active Data Guard enables a standby database to be open in read-only mode while continuously applying redo logs from the primary database. This provides:
High availability
Disaster recovery
Real-time reporting
This document provides a complete step-by-step guide to configure Active Data Guard in an Oracle 26AI Multitenant environment.
Source and Target servers with Oracle Linux 8/9
Primary server with Oracle 26AI Database installed
Standby server with Oracle 26AI Software installed
Network connectivity between servers (tnsping must work)
Database Name: AIDB
PDB Name: AIPDB
Primary Host: 192.168.226.129
Standby Host: 192.168.226.130
File location:
$ORACLE_HOME/network/admin/tnsnames.ora
LISTENER_AIDB =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.129)(PORT = 1521))
PRIMARY =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.129)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = PRIMARY)
)
)
AIPDB =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.129)(PORT = 1521))
(CONNECT_DATA =
(SERVICE_NAME = AIPDB)
)
)
STANDBY =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.130)(PORT = 1521))
(CONNECT_DATA =
(SERVICE_NAME = STANDBY)
)
)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.129)(PORT = 1521))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
)
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = PRIMARY)
(ORACLE_HOME = /u01/app/oracle/product/home)
(SID_NAME = AIDB)
)
)
LISTENER_AIDB =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.130)(PORT = 1521))
STANDBY =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.130)(PORT = 1521))
(CONNECT_DATA =
(SERVICE_NAME = STANDBY)
)
)
PRIMARY =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.129)(PORT = 1521))
(CONNECT_DATA =
(SERVICE_NAME = PRIMARY)
)
)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.226.130)(PORT = 1521))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
)
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = STANDBY)
(ORACLE_HOME = /u01/app/oracle/product/home)
(SID_NAME = AIDB)
)
)
STARTUP MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
ALTER DATABASE FORCE LOGGING;
ALTER SYSTEM SET db_unique_name='PRIMARY' SCOPE=SPFILE;
ALTER SYSTEM SET log_archive_config='DG_CONFIG=(PRIMARY,STANDBY)' SCOPE=BOTH;
ALTER SYSTEM SET log_archive_dest_1=
'LOCATION=/u01/app/oracle/product/home/dbs/arch
VALID_FOR=(ALL_LOGFILES,ALL_ROLES)
DB_UNIQUE_NAME=PRIMARY' SCOPE=BOTH;
ALTER SYSTEM SET log_archive_dest_2=
'SERVICE=STANDBY
ASYNC
NOAFFIRM
VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE)
DB_UNIQUE_NAME=STANDBY' SCOPE=BOTH;
ALTER SYSTEM SET FAL_CLIENT=PRIMARY SCOPE=BOTH;
ALTER SYSTEM SET FAL_SERVER=STANDBY SCOPE=BOTH;
ALTER SYSTEM SET standby_file_management='AUTO' SCOPE=BOTH;
ALTER SYSTEM SET db_file_name_convert=
'/u01/.../AIDB','/u01/.../AIDB' SCOPE=BOTH;
ALTER SYSTEM SET log_file_name_convert=
'/u01/.../AIDB','/u01/.../AIDB' SCOPE=BOTH;
ALTER DATABASE ADD STANDBY LOGFILE GROUP 4 ('/path/srl04.log') SIZE 200M;
ALTER DATABASE ADD STANDBY LOGFILE GROUP 5 ('/path/srl05.log') SIZE 200M;
ALTER DATABASE ADD STANDBY LOGFILE GROUP 6 ('/path/srl06.log') SIZE 200M;
ALTER DATABASE ADD STANDBY LOGFILE GROUP 7 ('/path/srl07.log') SIZE 200M;
scp initAIDB.ora orapwAIDB oracle@192.168.226.130:$ORACLE_HOME/dbs
STARTUP NOMOUNT PFILE='initAIDB.ora';
rman target sys/password@PRIMARY auxiliary sys/password@STANDBY
DUPLICATE TARGET DATABASE
FOR STANDBY
FROM ACTIVE DATABASE
NOFILENAMECHECK;
SELECT name, open_mode FROM v$database;
ALTER DATABASE OPEN READ ONLY;
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT;
SELECT process, status FROM v$managed_standby;
SELECT name, value
FROM v$dataguard_stats
WHERE name IN ('transport lag','apply lag');
SELECT * FROM v$archive_gap;
SELECT
arch.sequence# AS received,
appl.sequence# AS applied,
(arch.sequence# - appl.sequence#) AS gap
FROM
(SELECT MAX(sequence#) sequence# FROM v$archived_log) arch,
(SELECT MAX(sequence#) sequence# FROM v$log_history) appl;
ALTER DATABASE COMMIT TO SWITCHOVER TO PHYSICAL STANDBY WITH SESSION SHUTDOWN;
STARTUP MOUNT;
ALTER DATABASE OPEN READ ONLY;
ALTER DATABASE COMMIT TO SWITCHOVER TO PRIMARY;
ALTER DATABASE OPEN;
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT;
SELECT process, status FROM v$managed_standby;
SELECT name, value FROM v$dataguard_stats;
SELECT * FROM v$archive_gap;
Active Data Guard setup involves:
Network configuration
Primary database preparation
Standby database creation using RMAN
Enabling redo apply (MRP)
Validating synchronization and lag
A properly configured Active Data Guard environment ensures high availability, data protection, and real-time reporting capabilities.