Oracle Latch Waits - Diagnose Shared Pool & Buffer Latches
Oracle Latch Waits
Section titled “Oracle Latch Waits”Overview
Section titled “Overview”Wait Event Class: Concurrency
Key Events: latch free, latch: shared pool, latch: cache buffers chains, latch: library cache, latch: row cache objects
Latches are lightweight, low-level serialization mechanisms that protect Oracle’s in-memory data structures. Unlike enqueues (which queue waiting sessions fairly), latches use a spin-then-sleep approach:
- A session needing a latch attempts to acquire it in a tight spin loop
- After spinning for
_spin_countiterations (default ~2000), the session sleeps and tries again - After enough failed attempts, the session records a
latch freeor named latch wait
Latches are acquired and released very rapidly — typical hold times are microseconds. They are designed for structures that change frequently and need protection across very short critical sections. Because they are not fair (no FIFO queue), a highly contended latch causes many sessions to spin wasting CPU while waiting.
Important Latch Types
Section titled “Important Latch Types”| Latch Name | Protects | Common Cause of Contention |
|---|---|---|
shared pool | Shared pool memory allocation | Hard parsing, shared pool fragmentation |
library cache | Library cache (parsed cursors) | Hard parsing storms, cursor invalidation |
library cache pin | Pinned library cache objects | DDL during active DML, package compilation |
cache buffers chains | Buffer cache hash chains | Hot blocks accessed by many sessions |
cache buffers lru chain | LRU list for buffer cache | Cache under pressure, excessive physical reads |
row cache objects | Data dictionary (row cache) | Heavy data dictionary access |
redo allocation | Redo buffer space allocation | Extreme redo generation rates |
latch free vs Named Latch Events
Section titled “latch free vs Named Latch Events”In Oracle 10g and earlier, all latch waits recorded as latch free. From 10g onward, Oracle introduced named latch events (latch: shared pool, latch: cache buffers chains, etc.) for the most common latches. Both forms appear in AWR. Named latch events provide more specific diagnostic information.
When This Wait Is a Problem
Section titled “When This Wait Is a Problem”Thresholds
Section titled “Thresholds”| Metric | Assessment |
|---|---|
| Latch hit ratio > 99.9% | Good — low contention |
| Latch hit ratio 99–99.9% | Monitor — some contention |
| Latch hit ratio < 99% | Problem — significant spinning |
latch free in AWR Top 5 wait events | Investigate immediately |
Specific latch with sleeps > 1% of gets | Hot latch — targeted action needed |
Normal vs. Problematic
Section titled “Normal vs. Problematic”Latch waits are expected at very low levels (< 1 in 1000 gets). They become a problem when:
- A single latch becomes a bottleneck serializing many sessions
- CPU utilization is high but throughput is low (sessions spinning on latches)
- AWR shows latch waits as a significant fraction of non-idle DB time
- The same latch child appears consistently as the hot latch
Diagnostic Queries
Section titled “Diagnostic Queries”1. Top Latches by Misses and Sleeps
Section titled “1. Top Latches by Misses and Sleeps”-- Most contended latches at the instance levelSELECT name, gets, misses, sleeps, immediate_gets, immediate_misses, ROUND(misses * 100.0 / NULLIF(gets, 0), 4) AS miss_pct, ROUND(sleeps * 100.0 / NULLIF(gets, 0), 4) AS sleep_pct, spin_gets, wait_time / 100 AS wait_secsFROM v$latchWHERE misses > 0ORDER BY sleeps DESCFETCH FIRST 20 ROWS ONLY;2. Hot Latch Children — Find the Specific Contention Point
Section titled “2. Hot Latch Children — Find the Specific Contention Point”-- For latches with children (like cache buffers chains), identify the hot childSELECT lc.name, lc.child#, lc.addr, lc.gets, lc.misses, lc.sleeps, ROUND(lc.sleeps * 100.0 / NULLIF(lc.gets, 0), 4) AS sleep_pctFROM v$latch_children lcWHERE lc.name IN ('cache buffers chains', 'library cache', 'shared pool') AND lc.sleeps > 0ORDER BY lc.sleeps DESCFETCH FIRST 20 ROWS ONLY;3. Identify Blocks Protected by Hot Cache Buffers Chains Latch
Section titled “3. Identify Blocks Protected by Hot Cache Buffers Chains Latch”-- Find which buffer is protected by the hot cache buffers chains latch child-- First get the latch address from previous query, then:SELECT bf.file#, bf.dbablk AS block#, bf.class#, bf.state, bf.dirty, bf.temp, bf.ping, bf.lru_flag, do.object_name, do.object_type, do.ownerFROM x$bh bfLEFT JOIN dba_objects do ON bf.obj = do.data_object_idWHERE bf.hladdr = '&hot_latch_address' -- Address from v$latch_children.addrORDER BY bf.tch DESC -- tch = touch count, higher = hotterFETCH FIRST 10 ROWS ONLY;4. Library Cache and Shared Pool Latch Context
Section titled “4. Library Cache and Shared Pool Latch Context”-- Hard parse rate — primary driver of shared pool and library cache latch contentionSELECT name, value, ROUND(value / (SELECT (SYSDATE - startup_time) * 3600 FROM v$instance), 2) AS per_hourFROM v$sysstatWHERE name IN ('parse count (hard)', 'parse count (total)', 'parse count (failures)', 'execute count', 'cursor authentications')ORDER BY name;
-- Sessions with high parse counts right nowSELECT s.sid, s.serial#, s.username, s.program, ss.value AS hard_parses, s.sql_idFROM v$sesstat ssJOIN v$session s ON ss.sid = s.sidJOIN v$statname sn ON ss.statistic# = sn.statistic#WHERE sn.name = 'parse count (hard)' AND ss.value > 100ORDER BY ss.value DESCFETCH FIRST 20 ROWS ONLY;5. Shared Pool Free Memory and Fragmentation
Section titled “5. Shared Pool Free Memory and Fragmentation”-- Shared pool memory breakdownSELECT name, bytes / 1024 / 1024 AS mbFROM v$sgastatWHERE pool = 'shared pool'ORDER BY bytes DESCFETCH FIRST 15 ROWS ONLY;
-- Check for shared pool fragmentation (many small free chunks = fragmentation)SELECT pool, name, COUNT(*) AS chunk_count, ROUND(SUM(bytes) / 1024 / 1024, 2) AS total_mb, ROUND(MIN(bytes) / 1024, 2) AS min_chunk_kb, ROUND(MAX(bytes) / 1024 / 1024, 2) AS max_chunk_mb, ROUND(AVG(bytes) / 1024, 2) AS avg_chunk_kbFROM v$shared_pool_reservedUNION ALLSELECT pool, name, COUNT(*), ROUND(SUM(bytes) / 1024 / 1024, 2), ROUND(MIN(bytes) / 1024, 2), ROUND(MAX(bytes) / 1024 / 1024, 2), ROUND(AVG(bytes) / 1024, 2)FROM v$sgastatWHERE pool = 'shared pool' AND name = 'free memory'GROUP BY pool, name;Root Causes
Section titled “Root Causes”1. Hard Parsing — Shared Pool and Library Cache Latch Contention
Section titled “1. Hard Parsing — Shared Pool and Library Cache Latch Contention”Every time Oracle parses a SQL statement for the first time (or cannot reuse an existing cursor), it must acquire the shared pool latch to allocate memory and the library cache latch to insert the new cursor. Hard parsing is the most common driver of shared pool latch contention.
Hard parsing is caused by:
- Non-shared SQL — literals embedded in queries instead of bind variables (
WHERE id = 1234rather thanWHERE id = :id) - Session cursors not cached — applications that close and re-parse the same SQL every execution
- Large number of unique SQL — workloads with huge SQL diversity (ad-hoc query tools, ORMs generating unique SQL per request)
- Shared pool flushes —
ALTER SYSTEM FLUSH SHARED_POOLinvalidates all cursors, forcing a hard parse storm on next execution
2. Cache Buffers Chains Latch — Hot Blocks
Section titled “2. Cache Buffers Chains Latch — Hot Blocks”The buffer cache is organized as a hash table. Each hash bucket is a doubly-linked list (chain) of buffer headers, protected by a cache buffers chains latch child. When many sessions concurrently access the same buffer (a hot block), they all serialize on the same latch child protecting that block’s hash bucket.
Root cause is the hot block itself — the same blocks discussed in buffer busy waits. The difference: buffer busy waits is when a session is waiting for another to finish with the buffer; latch: cache buffers chains is when many sessions spin trying to acquire the hash chain latch to even check if the block is in cache.
Both are symptoms of the same underlying hot block problem.
3. Row Cache Objects Latch — Data Dictionary Contention
Section titled “3. Row Cache Objects Latch — Data Dictionary Contention”Oracle caches the data dictionary (table definitions, grants, etc.) in the row cache, protected by the row cache objects latch. Heavy DDL operations, frequent schema changes, or large numbers of sessions doing initial authentication all contend for this latch.
4. Redo Allocation Latch
Section titled “4. Redo Allocation Latch”Under extreme redo generation rates (very high DML throughput), sessions contend for the redo allocation latch to reserve space in the log buffer. Modern Oracle versions use multiple redo copy latches and private redo threads to reduce this, but extreme workloads can still see it.
5. Library Cache Pin Contention
Section titled “5. Library Cache Pin Contention”When a DDL statement (e.g., CREATE OR REPLACE PROCEDURE, ALTER TABLE, ANALYZE) runs while other sessions are executing objects that depend on the altered object, those sessions must wait on library cache pin. The DDL needs an exclusive pin to modify the library cache object, but executing sessions hold shared pins.
Resolution Steps
Section titled “Resolution Steps”Fix Hard Parsing — Use Bind Variables
Section titled “Fix Hard Parsing — Use Bind Variables”-- BAD: Literal values cause unique SQL — every value is a separate hard parseSELECT * FROM orders WHERE order_id = 12345;SELECT * FROM orders WHERE order_id = 12346;SELECT * FROM orders WHERE order_id = 12347;-- Each of these is a unique SQL string, causing separate hard parses
-- GOOD: Bind variables allow cursor reuse-- In PL/SQL:EXECUTE IMMEDIATE 'SELECT * FROM orders WHERE order_id = :id' USING v_order_id;
-- In application code (Java example pseudocode):-- PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE order_id = ?");-- ps.setInt(1, orderId);
-- Force cursor sharing as a temporary workaround (not recommended long-term):ALTER SYSTEM SET cursor_sharing = FORCE SCOPE=BOTH;-- Better: target specific sessions or use SQL patchesIncrease Session Cached Cursors
Section titled “Increase Session Cached Cursors”-- Caching cursors at the session level reduces repeated soft parses for the same SQL-- (Soft parses still require the library cache latch, but less frequently)ALTER SYSTEM SET session_cached_cursors = 50 SCOPE=BOTH;-- Increase to 100-200 for connection-pooling environments
-- Check cursor cache effectivenessSELECT name, valueFROM v$sysstatWHERE name IN ('session cursor cache hits', 'parse count (total)');
-- Cache hit ratio should be high (>95% for well-tuned apps)Resolve Cache Buffers Chains Latch — Address the Hot Block
Section titled “Resolve Cache Buffers Chains Latch — Address the Hot Block”-- Same resolution as buffer busy waits — eliminate the hot block:-- 1. Partition the table to distribute access-- 2. Use ASSM to eliminate segment header hot blocks-- 3. For index right-edge inserts: reverse key or hash-partition the index-- 4. Cache small hot reference tables in the KEEP pool
-- As an internal measure: increase the number of hash buckets (reduces-- collisions in the hash chain, so fewer blocks map to the same latch)-- This is typically handled automatically; manual intervention via _db_block_hash_buckets-- is rarely needed and should only be done under Oracle Support guidance
-- Verify hot block remediation is working:SELECT name, sleeps FROM v$latch WHERE name = 'cache buffers chains';-- Monitor this over time after making changesTune the Shared Pool Size
Section titled “Tune the Shared Pool Size”-- Check shared pool advisorySELECT shared_pool_size_for_estimate / 1024 / 1024 AS size_mb, shared_pool_size_factor, estd_lc_size, estd_lc_memory_objects, estd_lc_time_saved, estd_lc_load_time, estd_lc_load_time_factorFROM v$shared_pool_adviceORDER BY shared_pool_size_for_estimate;
-- Increase shared pool if it is undersized (reduces latch contention from ORA-04031 flushes)ALTER SYSTEM SET shared_pool_size = 2G SCOPE=BOTH;
-- Or via SGA_TARGET (let Oracle manage allocation):ALTER SYSTEM SET sga_target = 16G SCOPE=BOTH;
-- Protect large objects from aging out:ALTER SYSTEM SET shared_pool_reserved_size = 100M SCOPE=SPFILE;Pin Critical PL/SQL Objects to Prevent Invalidation
Section titled “Pin Critical PL/SQL Objects to Prevent Invalidation”-- Pin large or frequently-used packages in the shared pool-- Prevents them from aging out and causing re-parseEXECUTE DBMS_SHARED_POOL.KEEP('SYS.STANDARD', 'P');EXECUTE DBMS_SHARED_POOL.KEEP('SYS.DBMS_STANDARD', 'P');EXECUTE DBMS_SHARED_POOL.KEEP('YOUR_SCHEMA.YOUR_PACKAGE', 'P');
-- Verify pinned objectsSELECT owner, name, type, kept FROM v$db_object_cacheWHERE kept = 'YES'ORDER BY owner, name;Prevention & Tuning
Section titled “Prevention & Tuning”1. Enforce Bind Variable Usage at the Application Level
Section titled “1. Enforce Bind Variable Usage at the Application Level”This is the single most impactful prevention measure. Review application code and ORM frameworks to ensure parameterized queries are used. Tools like SQL Monitor in Oracle Enterprise Manager can identify the top literal SQL generators.
-- Find SQL with high version counts (symptoms of literal SQL flood)SELECT sql_id, version_count, SUBSTR(sql_text, 1, 100) AS sql_snippetFROM v$sqlareaWHERE version_count > 50ORDER BY version_count DESCFETCH FIRST 20 ROWS ONLY;2. Use Connection Pooling with Cursor Caching
Section titled “2. Use Connection Pooling with Cursor Caching”Connection pools that keep sessions alive (rather than creating new connections per request) benefit from SESSION_CACHED_CURSORS. Combine connection pooling with bind variables for maximum cursor reuse.
3. Monitor Latch Hit Ratio Over Time
Section titled “3. Monitor Latch Hit Ratio Over Time”-- Include latch health in regular AWR reviewsSELECT snap_id, stat_name, value, value - LAG(value) OVER (ORDER BY snap_id) AS deltaFROM dba_hist_latchWHERE stat_name IN ('shared pool', 'cache buffers chains', 'library cache') AND snap_id BETWEEN &begin_snap AND &end_snapORDER BY snap_id, stat_name;4. Reduce DDL During Peak Hours
Section titled “4. Reduce DDL During Peak Hours”Library cache pin contention spikes during DDL operations on active objects. Schedule package recompilations, ANALYZE statements, and ALTER TABLE during off-peak maintenance windows.
5. Use CURSOR_SHARING Carefully
Section titled “5. Use CURSOR_SHARING Carefully”CURSOR_SHARING = FORCE can mask poor application SQL practices and sometimes causes unexpected plan changes. Use it as a temporary fix only, and work toward proper bind variable usage in application code. CURSOR_SHARING = SIMILAR was deprecated and should not be used.
Related Wait Events
Section titled “Related Wait Events”- buffer busy waits — Block-level contention; hot blocks cause both latch and buffer busy waits
- db file sequential read — Physical reads that populate the buffer cache, affecting cache buffers chains latch pressure
- enq: TX - row lock contention — Row-level locking; a different serialization mechanism from latches
- cursor: pin S wait on X — Mutex-based cursor contention, the modern successor to library cache latch waits