Oracle EBS 12.2 on 19c CDB/PDB (Database Performance Tuning)

 Oracle EBS 12.2 on 19c CDB/PDB

Database Performance Tuning - Step-by-Step DBA Runbook

Prepared by: Bidhan Mandal | Oracle Core DBA & Apps DBA

Rules before you start: Change ONE thing at a time. Take AWR snapshot before and after every change. Backup SPFILE (CREATE PFILE FROM SPFILE) before parameter changes. Test in UAT first. AWR/ASH/ADDM/SQL Tuning Advisor need Diagnostics + Tuning Pack licence.

Step Index

Step

Area

Goal

1

OS / Server

Confirm CPU, memory, swap, I/O are not the bottleneck

2

CDB / PDB health

Instance, PDB open mode, alert log, errors

3

Live sessions & waits

Find what sessions wait on right now; blockers

4

AWR / ASH / ADDM

Find bottleneck over time window

5

Top SQL tuning

Fix bad plans, use SQL Tuning Advisor, SPM baseline

6

Optimizer statistics

EBS-correct stats gathering (FND_STATS)

7

Memory (SGA / PGA)

Right-size, HugePages

8

Init parameters

Validate against EBS 12.2 certified values

9

Redo, TEMP, UNDO, datafile I/O

Remove storage-side waits

10

EBS application housekeeping

Concurrent manager / workflow purge, big tables

11

ADOP cleanup & invalid objects

12.2 online patching leftovers, invalids

12

Segments & indexes

Recycle bin, unusable indexes, fragmentation

13

CDB resource manager & scheduler jobs

Stop PDB throttling and maintenance window load

14

Middle tier check

OACore JVM, DB connection pool

15

Validate & document

Before/after comparison, change log

 

Quick Triage Table

Top wait / symptom

Likely cause

Go to step

db file sequential read

Bad plan, missing index, slow storage

5, 6, 9

db file scattered read / direct path read

Full scans, stale stats, small buffer cache

5, 6, 7

log file sync / log file parallel write

Slow redo disk, too frequent commits

9

enq: TX - row lock contention

Blocking session / custom code

3, 10

library cache: mutex X / latch: shared pool

Hard parse, small shared pool, invalids

7, 8, 11

CPU 100% with few waits

Bad SQL, parallelism, PDB CPU limit

1, 5, 13

resmgr:cpu quantum

Resource Manager throttling PDB

13

Slow only in concurrent programs

Stale stats, FND tables bloated

6, 10

 

Step 1: OS / Server Check

Rule out the server first. If CPU run queue is high, I/O wait is high, or system is swapping, database tuning will not help until that is fixed.

 bash - oracle@dbserver

# login as oracle on DB server

uptime

top -b -n 1 | head -25

vmstat 5 5

sar -u 5 3

free -m

iostat -xm 5 3

df -h

# HugePages status

grep -i huge /proc/meminfo

 

 

•      vmstat: r column > CPU count = CPU bottleneck; si/so > 0 = swapping (bad); wa > 10-15% = I/O wait.

•      iostat: await > 10-20 ms or %util near 100 on data/redo disks = storage bottleneck.

•      df -h: any mount (datafile, archive, /tmp) at 90%+ must be cleared.

Step 2: CDB / PDB Health

 SQL*Plus - CDB$ROOT then PDB

sqlplus / as sysdba

 

SET LINES 220 PAGES 200 TRIMSPOOL ON

COL name FORMAT A20

SELECT name, cdb, open_mode, log_mode FROM v$database;

SELECT instance_name, status, database_status, startup_time FROM v$instance;

SELECT con_id, name, open_mode, restricted FROM v$pdbs;

 

-- switch into the EBS PDB (replace with your PDB name)

ALTER SESSION SET CONTAINER = EBSPDB;

SHOW CON_NAME

 

-- locate and read alert log

SELECT value FROM v$diag_info WHERE name = 'Diag Trace';

-- then: tail -300 <diag_trace>/alert_<SID>.log | grep -i -E "ORA-|error|cannot|stuck"

 

 

Note: Instance-level parameters (SGA, PGA, processes) are set in CDB$ROOT. Most EBS application queries, AWR per-PDB views and stats gathering are done inside the PDB.

Step 3: Live Sessions, Waits and Blockers

3.1 Active session wait summary

 SQL - inside PDB

SELECT event, wait_class, COUNT(*) sessions

FROM   v$session

WHERE  status = 'ACTIVE' AND type = 'USER' AND wait_class <> 'Idle'

GROUP  BY event, wait_class

ORDER  BY sessions DESC;

 

SELECT wait_class, event, total_waits,

       ROUND(time_waited_micro/1e6) sec_waited

FROM   v$system_event

WHERE  wait_class <> 'Idle'

ORDER  BY time_waited_micro DESC

FETCH FIRST 15 ROWS ONLY;

 

 

3.2 Blocking sessions

 SQL

SELECT blocking_session, sid, serial#, event, seconds_in_wait, sql_id, module

FROM   v$session

WHERE  blocking_session IS NOT NULL

ORDER  BY seconds_in_wait DESC;

 

-- kill only after confirming with application owner

-- ALTER SYSTEM KILL SESSION 'sid,serial#,@inst_id' IMMEDIATE;

 

 

3.3 Long running sessions (> 5 min)

 SQL

SELECT sid, serial#, username, sql_id, event, last_call_et sec,

       module, action, machine

FROM   v$session

WHERE  status = 'ACTIVE' AND type = 'USER' AND last_call_et > 300

ORDER  BY last_call_et DESC;

 

 

Step 4: AWR, ASH and ADDM

Generate reports for the slow time window and compare with a good-performance window. In 19c CDB, enable PDB-level AWR snapshots so reports can be taken inside the PDB.

 SQL*Plus

-- CDB$ROOT: allow PDB snapshots

ALTER SYSTEM SET awr_pdb_autoflush_enabled = TRUE SCOPE=BOTH;

 

-- snapshot every 30 min, keep 14 days

EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(retention=>20160, interval=>30);

EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;

 

-- reports (run inside PDB, choose begin/end snap IDs)

@?/rdbms/admin/awrrpt.sql

@?/rdbms/admin/ashrpt.sql

@?/rdbms/admin/addmrpt.sql

-- compare good vs bad period

@?/rdbms/admin/awrddrpt.sql

 

 

Read the AWR report in this order:

•      Load Profile: DB Time vs elapsed time (DB Time >> elapsed x CPU count = overloaded); hard parses/sec; logical/physical reads.

•      Top 10 Foreground Events by Total Wait Time: your main bottleneck.

•      Time Model Statistics: SQL execute vs parse vs PL/SQL time.

•      SQL ordered by Elapsed Time / CPU / Gets / Reads: candidates for Step 5.

•      Instance Efficiency: Soft Parse % should be > 95%; Buffer Hit % alone is not a proof of health.

•      Advisory sections: SGA/PGA target advice, buffer pool advisory.

Step 5: Top SQL Tuning

5.1 Find top SQL now

 SQL

SELECT sql_id, plan_hash_value phv, executions execs,

       ROUND(elapsed_time/1e6) ela_sec,

       ROUND(elapsed_time/NULLIF(executions,0)/1e6,2) ela_per_exec,

       buffer_gets, disk_reads, module,

       SUBSTR(sql_text,1,60) sql_text

FROM   v$sql

ORDER  BY elapsed_time DESC

FETCH FIRST 15 ROWS ONLY;

 

 

5.2 Check the execution plan

 SQL

-- from cursor cache (with actual row counts if gather_plan_statistics hint used)

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST +PEEKED_BINDS'));

 

-- from AWR history

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&sql_id'));

 

-- did plan change? (multiple PHV for same sql_id)

SELECT sql_id, plan_hash_value, COUNT(*) snaps, MIN(begin_interval_time) first_seen

FROM   dba_hist_sqlstat s JOIN dba_hist_snapshot n USING (snap_id, dbid, instance_number)

WHERE  sql_id = '&sql_id'

GROUP  BY sql_id, plan_hash_value;

 

 

5.3 SQL Tuning Advisor

 SQL

DECLARE

  l_task VARCHAR2(128);

BEGIN

  l_task := DBMS_SQLTUNE.CREATE_TUNING_TASK(

              sql_id     => '&sql_id',

              scope      => DBMS_SQLTUNE.SCOPE_COMPREHENSIVE,

              time_limit => 600,

              task_name  => 'TUNE_&sql_id');

  DBMS_SQLTUNE.EXECUTE_TUNING_TASK(task_name => l_task);

END;

/

SET LONG 1000000 LONGCHUNKSIZE 1000000 PAGES 0 LINES 200

SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK('TUNE_&sql_id') FROM dual;

 

 

5.4 Pin the good plan (SQL Plan Baseline)

 SQL

DECLARE

  n PLS_INTEGER;

BEGIN

  n := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(

         sql_id          => '&sql_id',

         plan_hash_value => &good_phv);

  DBMS_OUTPUT.PUT_LINE('Plans loaded: ' || n);

END;

/

SELECT sql_handle, plan_name, enabled, accepted FROM dba_sql_plan_baselines ORDER BY created DESC;

 

 

EBS note: Do not edit Oracle seeded SQL. Fix with stats, indexes recommended by Oracle Support, baselines, or an Oracle patch. Custom (XX) code can be rewritten.

Step 6: Optimizer Statistics (EBS Way)

For EBS application schemas always gather with the concurrent program "Gather Schema Statistics" (FND_STATS), not raw DBMS_STATS, so histograms and EBS-specific handling are correct.

•      System Administrator > Requests > Submit: "Gather Schema Statistics", Schema = ALL, Estimate Percent = 10 (or Auto), Backup Flag = NOBACKUP, Invalidate Dependent Cursors = Yes.

•      Run during low load. Schedule weekly.

 SQL

-- check stale stats on application schemas

SELECT owner, COUNT(*) stale_tables

FROM   dba_tab_statistics

WHERE  stale_stats = 'YES' AND owner NOT IN ('SYS','SYSTEM')

GROUP  BY owner ORDER BY 2 DESC;

 

-- tables never analyzed or very old

SELECT owner, table_name, num_rows, last_analyzed

FROM   dba_tables

WHERE  owner IN ('APPLSYS','APPS','GL','AP','AR','PO','INV','HR')

AND   (last_analyzed IS NULL OR last_analyzed < SYSDATE - 30)

AND    temporary = 'N'

ORDER  BY last_analyzed NULLS FIRST

FETCH FIRST 30 ROWS ONLY;

 

-- dictionary, fixed objects, system stats (as SYSDBA, in PDB)

EXEC DBMS_STATS.GATHER_DICTIONARY_STATS;

EXEC DBMS_STATS.GATHER_FIXED_OBJECTS_STATS;

EXEC DBMS_STATS.GATHER_SYSTEM_STATS('NOWORKLOAD');

 

 

Step 7: Memory - SGA, PGA, HugePages

 SQL - CDB$ROOT

SHOW PARAMETER sga

SHOW PARAMETER pga

SHOW PARAMETER memory_target

SHOW PARAMETER use_large_pages

 

COL component FORMAT A35

SELECT component, ROUND(current_size/1048576) mb

FROM   v$sga_dynamic_components WHERE current_size > 0;

 

-- advisors: look for where estd_db_time_factor stops improving

SELECT sga_size, sga_size_factor, estd_db_time, estd_db_time_factor FROM v$sga_target_advice;

SELECT ROUND(pga_target_for_estimate/1048576) mb, pga_target_factor,

       estd_pga_cache_hit_percentage, estd_overalloc_count

FROM   v$pga_target_advice;

 

SELECT name, ROUND(value/1048576) mb FROM v$pgastat

WHERE  name IN ('total PGA allocated','maximum PGA allocated','over allocation count');

 

-- shared pool / library cache health

SELECT namespace, gets, gethitratio, pins, reloads, invalidations FROM v$librarycache;

 

 

Apply changes (CDB$ROOT, restart required for some)

 SQL

CREATE PFILE='/tmp/init_before_tuning.ora' FROM SPFILE;

 

-- example values only: size from advisors and server RAM

ALTER SYSTEM SET sga_target=24G SCOPE=SPFILE;

ALTER SYSTEM SET sga_max_size=24G SCOPE=SPFILE;

ALTER SYSTEM SET pga_aggregate_target=8G SCOPE=SPFILE;

ALTER SYSTEM SET use_large_pages='ONLY' SCOPE=SPFILE;

 

 

•      Keep SGA + PGA at roughly 60-70% of server RAM when the server is DB-only.

•      HugePages: set vm.nr_hugepages in /etc/sysctl.conf to cover SGA, and disable Transparent HugePages. Never use AMM (memory_target) together with HugePages.

Step 8: Validate Init Parameters

Compare your values with the current Oracle EBS 12.2 database initialization parameters for 19c (MOS Doc ID 396009.1). Verify the document for your exact EBS and RU level before changing anything.

 SQL

COL name FORMAT A35

COL value FORMAT A40

SELECT name, value, isdefault

FROM   v$parameter

WHERE  name IN ('processes','sessions','open_cursors','session_cached_cursors',

                'sga_target','pga_aggregate_target','db_files','undo_retention',

                'optimizer_adaptive_plans','optimizer_adaptive_statistics',

                'optimizer_features_enable','cursor_sharing','filesystemio_options',

                'disk_asynch_io','db_file_multiblock_read_count','parallel_max_servers',

                'use_large_pages','job_queue_processes')

ORDER  BY name;

 

-- sessions vs limit

SELECT resource_name, current_utilization, max_utilization, limit_value

FROM   v$resource_limit WHERE resource_name IN ('processes','sessions');

 

 

•      If max_utilization of processes is near limit_value, raise processes (restart needed).

•      High "parse" time or shared pool waits: check session_cached_cursors and open_cursors against the Oracle note.

•      filesystemio_options = SETALL and disk_asynch_io = TRUE on filesystem storage (not needed for ASM).

Step 9: Redo, TEMP, UNDO and Datafile I/O

 SQL

-- redo log size and switch rate (target about 1 switch per 15-20 min at peak)

SELECT group#, thread#, bytes/1048576 mb, members, status FROM v$log;

SELECT TO_CHAR(first_time,'YYYY-MM-DD HH24') hr, COUNT(*) switches

FROM   v$log_history WHERE first_time > SYSDATE - 3

GROUP  BY TO_CHAR(first_time,'YYYY-MM-DD HH24') ORDER BY 1;

 

-- log file sync average

SELECT event, total_waits, ROUND(time_waited_micro/NULLIF(total_waits,0)/1000,2) avg_ms

FROM   v$system_event WHERE event IN ('log file sync','log file parallel write');

 

-- TEMP

SELECT tablespace_name, ROUND(tablespace_size/1048576) total_mb,

       ROUND(allocated_space/1048576) alloc_mb, ROUND(free_space/1048576) free_mb

FROM   dba_temp_free_space;

 

-- UNDO

SELECT tablespace_name, status, ROUND(SUM(bytes)/1048576) mb

FROM   dba_undo_extents GROUP BY tablespace_name, status;

 

-- slowest datafiles

SELECT df.name, fs.phyrds, fs.phywrts,

       ROUND(fs.readtim*10/NULLIF(fs.phyrds,0),2) avg_read_ms

FROM   v$filestat fs JOIN v$datafile df ON df.file# = fs.file#

ORDER  BY fs.phyrds DESC FETCH FIRST 10 ROWS ONLY;

 

 

•      log file sync avg > 10 ms: move redo to faster disk, add larger redo groups (e.g. 2 GB), review commit frequency in custom code.

•      avg_read_ms > 10 ms on SSD/SAN = storage issue; escalate with iostat data.

•      Add redo group example: ALTER DATABASE ADD LOGFILE THREAD 1 GROUP 11 ('/u02/redo/redo11.log') SIZE 2G; then drop old small groups when INACTIVE.

Step 10: EBS Application Housekeeping

Bloated FND and Workflow tables slow concurrent managers, forms and OAF pages. Run these standard purge programs from System Administrator responsibility and schedule them.

Concurrent program

Suggested setting

Purge Concurrent Request and/or Manager Data

Entity = ALL, Mode = Age, Age = 7-30 days

Purge Obsolete Workflow Runtime Data

Item Type blank (all), Age 7-30 days, Core Workflow only = No

Purge Signon Audit Data

Audit date = sysdate - 30

Purge Debug Log and System Alerts

Older than 7-30 days

Gather Schema Statistics

After each large purge (Step 6)

 

 SQL - APPS

-- size of key tables

SELECT COUNT(*) FROM applsys.fnd_concurrent_requests;

SELECT COUNT(*) FROM applsys.wf_items;

SELECT COUNT(*) FROM applsys.wf_item_activity_statuses;

 

-- top 20 segments in APPS-related schemas

SELECT owner, segment_name, segment_type, ROUND(bytes/1048576) mb

FROM   dba_segments

WHERE  owner IN ('APPLSYS','APPS','GL','AP','AR','PO','INV','XLA','XDO')

ORDER  BY bytes DESC FETCH FIRST 20 ROWS ONLY;

 

-- long running concurrent requests

SELECT r.request_id, p.user_concurrent_program_name prog,

       ROUND((SYSDATE - r.actual_start_date)*24*60) run_min

FROM   apps.fnd_concurrent_requests r, apps.fnd_concurrent_programs_tl p

WHERE  r.concurrent_program_id = p.concurrent_program_id

AND    r.program_application_id = p.application_id

AND    p.language = 'US' AND r.phase_code = 'R'

ORDER  BY run_min DESC;

 

-- pending requests backlog

SELECT COUNT(*) pending FROM apps.fnd_concurrent_requests WHERE phase_code = 'P' AND status_code = 'I';

 

 

Step 11: ADOP Cleanup and Invalid Objects

In EBS 12.2, leftover old editions and cross-edition objects after patching cause slow parsing and slow DDL. Run cleanup after every successful cutover.

 bash - applmgr (adjust EBSapps.env path)

# on application tier, as applmgr

source /u01/app/oracle/EBSapps.env run

adop -status

adop phase=cleanup cleanup_mode=full

 

 

 SQL*Plus

-- editions present (only ORA$BASE + current run edition expected)

SELECT edition_name, usable FROM dba_editions;

 

-- invalid objects

SELECT owner, object_type, COUNT(*) cnt

FROM   dba_objects WHERE status = 'INVALID'

GROUP  BY owner, object_type ORDER BY cnt DESC;

 

-- recompile

@?/rdbms/admin/utlrp.sql

 

 

Tip: If APPS objects stay invalid after utlrp, compile with adadmin (Compile/Reload Database Entities > Compile APPS schema) and check the error in DBA_ERRORS.

Step 12: Segments and Indexes

 SQL

-- recycle bin

SELECT COUNT(*) FROM dba_recyclebin;

PURGE DBA_RECYCLEBIN;

 

-- unusable indexes (must be rebuilt)

SELECT owner, index_name, status FROM dba_indexes WHERE status = 'UNUSABLE';

SELECT index_owner, index_name, partition_name FROM dba_ind_partitions WHERE status = 'UNUSABLE';

 

-- rebuild example (online)

-- ALTER INDEX owner.index_name REBUILD ONLINE;

 

-- tables with large wasted space (compare segment size vs num_rows*avg_row_len)

SELECT t.owner, t.table_name, ROUND(s.bytes/1048576) seg_mb,

       ROUND(t.num_rows*t.avg_row_len/1048576) est_data_mb

FROM   dba_tables t JOIN dba_segments s

       ON s.owner = t.owner AND s.segment_name = t.table_name

WHERE  t.owner IN ('APPLSYS','APPS') AND s.bytes > 100*1048576

ORDER  BY s.bytes DESC FETCH FIRST 15 ROWS ONLY;

 

 

Caution: Do not rebuild indexes routinely. Rebuild only for unusable status or proven benefit. Reclaim table space with ALTER TABLE ... SHRINK SPACE / move only in a maintenance window and only for non-Oracle-seeded-critical tables after testing.

Step 13: CDB Resource Manager and Scheduler Jobs

In a multitenant setup a CDB resource plan or maintenance window can throttle the EBS PDB even when the server is idle.

 SQL - CDB$ROOT

-- CDB$ROOT

SHOW PARAMETER resource_manager_plan

SELECT plan, pluggable_database, shares, utilization_limit

FROM   cdb_cdb_rsrc_plan_directives ORDER BY plan;

 

-- PDB CPU usage vs wait (high cpu_wait_time = throttled)

SELECT con_id, begin_time, cpu_consumed_time, cpu_wait_time, avg_running_sessions

FROM   v$rsrcpdbmetric ORDER BY begin_time DESC FETCH FIRST 10 ROWS ONLY;

 

-- wait event check

SELECT event, COUNT(*) FROM v$session WHERE event LIKE 'resmgr%' GROUP BY event;

 

-- autotask / maintenance windows overlapping business hours

SELECT client_name, status FROM dba_autotask_client;

SELECT window_name, enabled, repeat_interval, duration FROM dba_scheduler_windows;

SELECT job_name, state, last_start_date FROM dba_scheduler_running_jobs;

 

 

•      If resmgr:cpu quantum appears, relax the PDB utilization_limit or remove the plan.

•      Move automatic maintenance windows to night/weekend if they run in office hours.

Step 14: Middle Tier Quick Check

 bash - applmgr

# application tier

ps -ef | grep -i oacore | grep -v grep

free -m

# JVM GC pressure for an oacore PID

jstat -gcutil <oacore_pid> 5000 5

# DB sessions per module (from DB)

#   SELECT module, COUNT(*) FROM v$session WHERE type='USER' GROUP BY module ORDER BY 2 DESC;

 

 

•      Frequent full GC or Old gen > 90%: increase oacore heap in WebLogic Admin Console and add managed servers.

•      Many idle JDBC sessions from one module: tune data source pool size (Initial/Max capacity).

•      Forms slow but DB is healthy: check Forms server, network latency and client Java.

Step 15: Validate and Document

 SQL*Plus

-- take end snapshot then generate after-change AWR

EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;

@?/rdbms/admin/awrddrpt.sql

 

 

Metric

Before

After

Comment

Avg active sessions

 

 

 

DB Time / elapsed

 

 

 

Top wait event

 

 

 

Avg log file sync (ms)

 

 

 

Avg single block read (ms)

 

 

 

Key concurrent program runtime

 

 

 

Change made / date / by

 

 

 

 

Rollback: Keep the PFILE backup from Step 7. To revert a parameter: ALTER SYSTEM SET <param>=<old> SCOPE=SPFILE, or CREATE SPFILE FROM PFILE='/tmp/init_before_tuning.ora'; and restart. Drop a baseline: DBMS_SPM.DROP_SQL_PLAN_BASELINE.

Comments

Popular posts from this blog

Configure Oracle Database Vault Realms

Cloning Oracle E-Business Suite 12.2.11: RMAN + Rapid Clone

Utilizing Out-of-the-Box Unified Audit Policies in Oracle 19c