Statspack in Oracle 19c: A Real-World Performance Tuning Case Study

In a production Oracle environment running on 19c, a critical performance degradation impacted business operations during peak hours. Despite being on a modern database version, the absence of Diagnostic Pack licensing meant AWR was not available. This case study demonstrates how Statspack was implemented and leveraged to identify the root cause and deliver a ~40% performance improvement using fundamental performance analysis techniques.


Environment Overview

ComponentDetails
Database VersionOracle 19c
EditionStandard Edition (No AWR)
ApplicationOracle E-Business Suite
Workload TypeOLTP (High concurrent users)
Peak Users~1200 concurrent sessions

Problem Statement

During peak business hours, users experienced intermittent slowness with transactions taking ~3x longer than baseline. There was no historical performance repository due to AWR unavailability. Key constraints included:

  • Diagnostic Pack not licensed — AWR disabled
  • No ADDM recommendations available
  • Production-critical issue requiring immediate resolution

Strategy and Approach

Even in Oracle Database 19c, we relied on core DBA principles: enable Statspack, capture snapshots at 30-minute intervals, and perform comparative analysis between peak vs non-peak workload and before vs during the degradation window.


Implementation Steps

Step 1: Install Statspack

Connect as SYSDBA and run the Statspack creation script. This creates the PERFSTAT schema and all required objects:

sqlplus / as sysdba
@?/rdbms/admin/spcreate.sql

You will be prompted for the PERFSTAT password, default tablespace, and temporary tablespace. Choose an appropriate tablespace with sufficient space (minimum 100MB recommended for active environments).

Step 2: Automate Snapshot Collection

Schedule automatic snapshots every 30 minutes using the built-in Statspack job:

-- Connect as PERFSTAT user
sqlplus perfstat/<password>

-- Take a manual snapshot
EXECUTE statspack.snap;

-- Schedule automatic snapshots (every 30 mins) using spauto.sql
@?/rdbms/admin/spauto.sql

-- Or manually schedule with DBMS_JOB
VARIABLE jobno NUMBER;
BEGIN
  DBMS_JOB.SUBMIT(:jobno,
    'statspack.snap;',
    SYSDATE,
    'SYSDATE + 30/1440');
  COMMIT;
END;
/

Step 3: Verify Snapshots

-- Check available snapshots
SELECT snap_id, snap_time, snap_level
FROM stats$snapshot
ORDER BY snap_time DESC;

-- Count total snapshots
SELECT COUNT(*) FROM stats$snapshot;

Step 4: Generate Statspack Report

Generate a report between two snapshot IDs covering the peak period:

sqlplus perfstat/<password>
@?/rdbms/admin/spreport.sql
-- Enter begin and end snapshot IDs when prompted

Step 5: Query Top Wait Events Directly

SELECT event,
       total_waits,
       time_waited_micro/1000000 time_waited_secs,
       average_wait
FROM stats$system_event se,
     stats$snapshot s
WHERE s.snap_id BETWEEN &begin_snap AND &end_snap
AND se.snap_id = s.snap_id
AND event NOT LIKE 'SQL*Net%'
ORDER BY time_waited_micro DESC
FETCH FIRST 10 ROWS ONLY;

Analysis Findings

Key finding: log file sync dominated database time — a clear indication of commit-related contention. No high-cost SQL was identified, ruling out query inefficiency as the primary cause. The load profile showed elevated user commits per second and an increased redo generation rate.


Root Cause

Frequent commits inside application loops (row-by-row processing) caused high log file sync waits, redo log contention, increased I/O latency, and reduced transaction throughput.


Resolution

The application team introduced commit batching to significantly reduce commit frequency — modifying loop logic to commit every N rows rather than every single row. Minor redo log tuning was also performed:

-- Check redo log switch frequency (should not exceed 4-5 per hour ideally)
SELECT TO_CHAR(first_time,'YYYY-MM-DD HH24') hour,
       COUNT(*) switches
FROM v$log_history
WHERE first_time > SYSDATE - 1
GROUP BY TO_CHAR(first_time,'YYYY-MM-DD HH24')
ORDER BY 1;

-- Check redo log sizes
SELECT group#, members, bytes/1024/1024 size_mb, status
FROM v$log
ORDER BY group#;

Results

MetricBeforeAfter
Transaction TimeHigh (3x baseline)Down ~40%
log file sync waitsVery HighMinimal
User ExperiencePoorStable

Purging Old Statspack Data

Statspack data grows quickly in busy environments. Purge old snapshots regularly:

-- Purge a range of snapshots
EXECUTE statspack.purge(
  i_begin_snap  => &oldest_snap_id,
  i_end_snap    => &newest_snap_id,
  i_snap_range  => TRUE
);

-- Or use the provided script
@?/rdbms/admin/sppurge.sql

Key Takeaways

  • Modern DB does not mean automatic performance — even in Oracle 19c, performance issues require structured analysis
  • Wait events reveal the truth — hit ratios appeared normal, but wait events exposed the actual bottleneck
  • Application design matters — commit strategy directly impacts performance; database tuning alone cannot fix design inefficiencies
  • Statspack still matters — highly effective in modern 19c environments, especially without AWR licensing
  • Always capture snapshots during peak workload with intervals of 30 minutes or less

Why Not AWR?

ReasonExplanation
LicensingDiagnostic Pack not enabled
Cost ConstraintsAvoid additional licensing overhead
PracticalityStatspack provided sufficient insight for this issue

This case reflects real-world experience in Oracle E-Business Suite environments, high-concurrency OLTP systems, and performance tuning under licensing constraints. Have questions? Reach out at sdanwarahmed@gmail.com.


Discover more from Syed Anwar Ahmed – Oracle DBA Blog

Subscribe to get the latest posts sent to your email.

Comments

Leave a comment

Discover more from Syed Anwar Ahmed – Oracle DBA Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading