Oracle9i Database Performance Guide and Reference
Release 1 (9.0.1)

Part Number A87503-02
Go To Documentation Library
Home
Go To Product List
Book List
Go To Table Of Contents
Contents
Go To Index
Index

Master Index

Feedback

Go to previous page Go to next page

14
Memory Configuration and Use

This chapter explains how to allocate memory to Oracle memory caches, and how to use those caches. Proper sizing and effective use of the Oracle memory caches greatly improves database performance.

This chapter contains the following sections:

Understanding Memory Allocation Issues

Oracle stores information in memory caches and on disk. Memory access is much faster than disk access. Physical I/O takes a significant amount of time when compared with memory access, typically in the order of ten milliseconds. Physical I/O also increases the CPU resources required, because of the path length in device drivers and operating system event schedulers. For this reason, it is more efficient for data requests for frequently accessed objects to be satisfied solely by memory, rather than also requiring disk access.

The performance goal is to reduce the physical I/O overhead as much as possible by making it more likely that the required data is in memory, or by making the process of retrieving the required data more efficient.

Oracle Memory Caches

The main Oracle memory caches that affect performance are the following:

The size of these memory caches is configurable using initialization configuration parameters. The values for these parameters are also dynamically configurable using the ALTER SYSTEM statement (except for the log buffer, which is static after startup).

Dynamically Changing Cache Sizes

It is possible to reconfigure the sizes of the shared pool and the buffer cache dynamically, in addition to dynamically reconfiguring process-private memory. Memory for the shared pool and buffer cache is allocated in units of granules. A granule can be 4 MB or 16 MB, depending on the total size of your SGA at the time of instance startup. If the size of your SGA is less than 128 MB, then the granules are 4 MB in size; otherwise, they are 16 MB. It is possible to decrease the size of one cache and reallocate that memory to another cache, if needed. The total SGA size can be expanded to a value equal to the SGA_MAX_SIZE parameter.


Note:

SGA_MAX_SIZE cannot be dynamically resized. 


The maximum amount of memory usable by the instance is determined at instance startup by the initialization parameter SGA_MAX_SIZE. You can specify SGA_MAX_SIZE to be larger than the sum of all of the memory components (such as buffer cache and shared pool); otherwise, SGA_MAX_SIZE defaults to the actual size used by those components. Setting SGA_MAX_SIZE larger than the sum memory used by all of the components lets you dynamically increase a cache size without needing to decrease the size of another cache.

See Also:

Oracle9i Database Concepts or Oracle9i SQL Reference for more information on the dynamic SGA 

Application Considerations

With memory configuration, it is important to size the cache appropriately for the application's needs. Conversely, tuning the application's use of the caches can greatly reduce the resource requirements. Efficient use of the Oracle memory caches also reduces the load on other related resources, such as the latches that protect those caches, the CPU, and the I/O system.

For best performance, consider the following:

Making changes or additions to an existing application might require resizing Oracle memory structures to meet the needs of your modified application.

Operating System Memory Use

For most operating systems, it is important to consider the following:

Iteration During Configuration

Configuring memory allocation involves distributing available memory to Oracle memory structures, depending on the needs of your application. The distribution of memory to Oracle structures can affect the amount of physical I/O necessary for Oracle to operate. Having a good first initial memory configuration also provides an indication of whether the I/O system is effectively configured.

It might be necessary to repeat the steps of memory allocation after the initial pass through the process. Subsequent passes let you make adjustments in earlier steps based on changes in later steps. For example, decreasing the size of the buffer cache allows you to increase the size of another memory structure, such as the shared pool.

Configuring and Using the Buffer Cache

For many types of operations, Oracle uses the buffer cache to cache blocks read from disk. Oracle bypasses the buffer cache for particular operations, such as sorting and parallel reads. For operations that use the buffer cache, this section explains the following:

Using the Buffer Cache Effectively

To use the buffer cache effectively, the application's SQL statements should be tuned to avoid unnecessary resource consumption. To ensure this, verify that frequently executed SQL statements and SQL statements that perform many buffer gets have been tuned.

See Also:

Chapter 6, "Optimizing SQL Statements" for information on how to do this 

Sizing the Buffer Cache

When configuring a brand new instance, it is impossible to know the correct size for the buffer cache. Typically, a DBA makes a first estimate for the cache size, then runs a representative workload on the instance and examines the relevant statistics to see whether the cache is under- or over-configured.

Buffer Cache Statistics

There are a number of different statistics that can be used to examine buffer cache activity. These include the following:

Using V$DB_CACHE_ADVICE

This view is populated when the DB_CACHE_ADVICE parameter is set to ON. This view shows the estimated miss rates for twenty potential buffer cache sizes, ranging from 10% of the current size to 200% of the current size. Each of the twenty potential cache sizes has its own row in this view, with the predicted physical I/O activity that would take place for that cache size. The DB_CACHE_ADVICE parameter is dynamic, so the advisory can be enabled and disabled dynamically to allow you to collect advisory data for a specific workload.

There are two minor overheads associated with this advisory:

The parameter DB_CACHE_ADVICE should be set to ON, and a representative workload should be running on the instance. Allow the workload to stabilize before querying V$DB_CACHE_ADVICE view.

The following SQL statement returns the predicted I/O requirement for the default buffer pool for various cache sizes:

column size_for_estimate         format 999,999,999,999 heading 'Cache Size (m)'
column buffers_for_estimate      format 999,999,999 heading 'Buffers'
column estd_physical_read_factor format 999.90 heading 'Estd Phys|Read Factor'
column estd_physical_reads       format 999,999,999 heading 'Estd Phys| Reads'

SELECT size_for_estimate, buffers_for_estimate
     , estd_physical_read_factor, estd_physical_reads
  FROM V$DB_CACHE_ADVICE
 WHERE name          = 'DEFAULT'
   AND block_size    = (SELECT value FROM V$PARAMETER 
                         WHERE name = 'db_block_size')
   AND advice_status = 'ON';

The following output shows that if the cache was 212MB, rather than the current size of 304MB, the estimated additional number of physical reads would be 17 million (17,850,847). Increasing the cache size beyond it's current size would not provide a significant benefit.

                                Estd Phys    Estd Phys
 Cache Size (MB)      Buffers Read Factor        Reads
---------------- ------------ ----------- ------------
              30        3,802       18.70  192,317,943      10% of Current Size
              60        7,604       12.83  131,949,536
              91       11,406        7.38   75,865,861
             121       15,208        4.97   51,111,658
             152       19,010        3.64   37,460,786
             182       22,812        2.50   25,668,196
             212       26,614        1.74   17,850,847
             243       30,416        1.33   13,720,149
             273       34,218        1.13   11,583,180
             304       38,020        1.00   10,282,475      Current Size
             334       41,822         .93    9,515,878
             364       45,624         .87    8,909,026
             395       49,426         .83    8,495,039
             424       53,228         .79    8,116,496
             456       57,030         .76    7,824,764
             486       60,832         .74    7,563,180
             517       64,634         .71    7,311,729
             547       68,436         .69    7,104,280
             577       72,238         .67    6,895,122
             608       76,040         .66    6,739,731      200% of Current Size

This view assists in cache sizing by providing information that predicts the number of physical reads for each potential cache size. The data also includes a physical read factor, which is a factor by which the current number of physical reads is estimated to change if the buffer cache is resized to a given value.

The relationship between successfully finding a block in the cache and the size of the cache is not always a smooth distribution. When sizing the buffer pool, avoid the use of additional buffers that contribute little or nothing to the cache hit ratio. In the example illustrated in Figure 14-1, only narrow bands of increments to the cache size may be worthy of consideration.

Examining Figure 14-1 leads to these observations:

Figure 14-1 Physical I/O and Buffer Cache Size


Text description of stu81041.gif follows
Text description of the illustration stu81041.gif
Calculating the Buffer Cache Hit Ratio

The buffer cache hit ratio calculates how often a requested block has been found in the buffer cache, without requiring disk access. This ratio is computed using data selected from the dynamic performance view V$SYSSTAT. The buffer cache hit ratio can be used to verify the physical I/O as predicted by V$DB_CACHE_ADVICE.

These statistics are used to calculate the hit ratio:

session logical 
reads

The total number of requests to access a block (whether in memory or on disk).

physical reads

The total number of requests to access a data block that resulted in access to datafiles on disk. (The block could have been read into the cache or read into local memory by a direct read).

physical reads 
direct

This indicates the number of blocks read, bypassing the buffer cache (excluding direct reads for LOBs).

physical reads 
direct (lob)

This indicates the number of blocks read while reading LOBs, bypassing the buffer cache.

The example below has been simplified by using values selected directly from the V$SYSSTAT table, without selecting these values over an interval. It is best to calculate the delta of these statistics over an interval while your application is running, then use them in the formula below.

See Also:

Chapter 20, "Oracle Tools to Gather Database Statistics" for more information on collecting statistics over an interval 

For example:

SELECT NAME, VALUE
  FROM V$SYSSTAT
WHERE NAME IN ('session logical reads','physical reads','physical reads 
direct','physical reads direct (lob)')

The output of this query will look similar to the following:

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
session logical reads                                             464905358
physical reads                                                     10380487
physical reads direct                                                 86850
physical reads direct (lob)                                               0

Calculate the hit ratio for the buffer cache with the following formula:

Hit Ratio = 1 - ((physical reads - physical reads direct - physical reads direct (lob)) / session logical reads)

Based on the sample statistics above, the buffer cache hit ratio is .978 (or 97.8%).

Interpreting and Using the Buffer Cache Statistics

There are a many factors to examine before considering whether or not to increase or decrease the buffer cache size. For example, examine V$DB_CACHE_ADVICE data and the buffer cache hit ratio before deciding to increase or decrease the buffer cache size.

Additionally, a low cache hit ratio does not imply that increasing the size of the cache would be beneficial for performance. A good cache hit ratio could wrongly indicate that the cache is adequately sized for the workload.

Considerations when looking at the buffer cache hit ratio include the following:

Increasing Memory Allocated to the Buffer Cache

As a general rule, investigate increasing the size of the cache if the cache hit ratio is low and your application has been tuned to avoid performing full table scans.

Set the DB_CACHE_ADVICE parameter to ON, and let the cache statistics stabilize. Examine the advisory data in the V$DB_CACHE_ADVICE view to determine the next increment required to significantly decrease the amount of physical I/O performed. If it is possible to allocate the required extra memory to the buffer cache, without causing the host OS to page, then allocate this memory. To increase the amount of memory allocated to the buffer cache, increase the value of the parameter DB_CACHE_SIZE.

If required, resize the buffer pools dynamically, rather than shutting down the instance to perform this change.


Note:

When the cache is resized, DB_CACHE_ADVICE is set to OFF. Also, V$DB_CACHE_ADVICE shows the advisory for the old value of the cache. This remains until DB_CACHE_ADVICE is explicitly set back to READY or ON


The DB_CACHE_SIZE parameter specifies the size of the default cache for the database's standard block size. To create and use tablespaces with block sizes different than the database's standard block sizes (such as to support transportable tablespaces), you must configure a separate cache for each block size used. The DB_nK_CACHE_SIZE parameter can be used to configure the size of the nonstandard block size needed (where n is 2, 4, 8, 16 or 32 and n is not the standard block size).


Note:

The process of choosing a cache size is the same, regardless of whether the cache is the default standard block size cache, the KEEP or RECYCLE cache, or a nonstandard block size cache.  


See Also:

Oracle9i Database Reference and Oracle9i Database Administrator's Guide for more information on using the DB_nK_CACHE_SIZE parameters 

Reducing Memory Allocated to the Buffer Cache

If your hit ratio is high, then your cache is probably large enough to hold your most frequently accessed data. Check V$DB_CACHE_ADVICE data to see whether decreasing your cache size significantly causes the number of physical I/Os to increase. If not, and if you require memory for another memory structure, then you might be able to reduce the cache size and still maintain good performance. To make the buffer cache smaller, reduce the size of the cache by changing the value for the parameter DB_CACHE_SIZE.

Considering Multiple Buffer Pools

A single default buffer pool is generally adequate for most systems. However, users with detailed knowledge of their application's buffer pool might benefit from configuring multiple buffer pools.

With segments that have atypical access patterns, cache blocks from those segments in two different buffer pools: the KEEP pool and the RECYCLE pool. A segment's access pattern may be atypical in that it is constantly accessed (that is, hot), or infrequently accessed (for example, a large segment accessed by a batch job only once per day).

Multiple buffer pools let you address these differences. You can use a KEEP buffer pool to maintain frequently-accessed segments in the buffer cache, and a RECYCLE buffer pool to prevent objects from consuming unnecessary space in the cache. When an object is associated with a cache, all blocks from that object are placed in that cache. Oracle maintains a DEFAULT buffer pool for objects that have not been assigned to a specific buffer pool. The default buffer pool is of size DB_CACHE_SIZE. Each buffer pool uses the same LRU replacement policy (for example, if the KEEP pool is not large enough to store all of the segments allocated to it, then the oldest blocks age out of the cache).

By allocating objects to appropriate buffer pools, you can do the following:

Random Access to Large Segments

A problem can occur with an LRU aging method when a very large segment is accessed with a large or unbounded index range scan. Here, very large means large compared to the size of the cache. Any single segment that accounts for a substantial portion (more than 10%) of nonsequential physical reads is probably one of these segments. Random reads to such a large segment can cause buffers that contain data for other segments to be aged out of the cache. The large segment ends up consuming a large percentage of the cache, but it does not benefit from the cache.

Very frequently-accessed segments are not affected by large segment reads, because their buffers are warmed frequently enough that they do not age out of the cache. The issue exists for "warm" segments that are not accessed frequently enough to survive the buffer aging caused by the large segment reads. There are three options for solving this problem:

  1. If the object accessed is an index, then investigate whether the index is selective. If not, then tune the SQL statement to use a more selective index.

  2. If the SQL statement is tuned, then you can move the large segment into a separate RECYCLE cache, so that it does not affect the other segments. The RECYCLE cache should be smaller than the DEFAULT buffer pool, and it should reuse buffers more quickly than the DEFAULT buffer pool.

  3. Alternatively, move the small warm segments into a separate KEEP cache that is not used at all for large segments. The KEEP cache can be sized to minimize misses in the cache. You can make the response times for specific queries more predictable by putting the segments accessed by the queries in the KEEP cache to ensure that they do not age out.

Oracle Real Application Cluster Instances

You can create multiple buffer pools for each database instance. The same set of buffer pools need not be defined for each instance of the database. Among instances, the buffer pools can be different sizes or not defined at all. Tune each instance according to the requirements placed by the application on that instance.

Buffer Pool data in V$DB_CACHE_ADVICE

V$DB_CACHE_ADVICE can be used to size all pools configured on your instance. Make the initial cache size estimate, run the representative workload, then simply query the V$DB_CACHE_ADVICE view for the pool you are interested in.

For example, to query data from the KEEP pool:

SELECT size_for_estimate, buffers_for_estimate
     , estd_physical_read_factor, estd_physical_reads
  FROM V$DB_CACHE_ADVICE
 WHERE name          = 'KEEP'
   AND block_size    = (SELECT value FROM V$PARAMETER 
                         WHERE name = 'db_block_size')
   AND advice_status = 'ON';

Buffer Pool Hit Ratios

The data in V$SYSSTAT reflects the logical and physical reads for all buffer pools within one set of statistics. To determine the hit ratio for the buffer pools individually, you must query the V$BUFFER_POOL_STATISTICS view. This view maintains per-pool statistics on the number of logical reads and writes.

The buffer pool hit ratio can be determined using the following formula:

where the values of physical reads, block gets, and consistent gets can be obtained from the following query:

SELECT NAME, PHYSICAL_READS, DB_BLOCK_GETS, CONSISTENT_GETS
      , 1 - (PHYSICAL_READS / (DB_BLOCK_GETS + CONSISTENT_GETS)) "Hit Ratio"
  FROM V$BUFFER_POOL_STATISTICS;

Determining Which Segments Have Many Buffers in the Pool

The V$BH view shows the data object ID of all blocks that currently reside in the SGA. You can either look at the buffer cache usage pattern of all segments using Method 1, or examine the usage pattern of a specific segment, as shown in Method 2.

Method 1

The query below counts the number of blocks for all segments that reside in the buffer cache at that point in time. Depending on your buffer cache size, this could require a lot of sort space:

column object_name format a40
column number_of_blocks format 999,999,999,999

SELECT o.object_name, COUNT(1) number_of_blocks
  FROM DBA_OBJECTS o, V$BH bh
 WHERE o.object_id  = bh.objd
   AND o.owner     != 'SYS'
 GROUP BY o.object_name
 ORDER BY count(1);

OBJECT_NAME                              NUMBER_OF_BLOCKS
---------------------------------------- ----------------
OA_PREF_UNIQ_KEY                                        1
SYS_C002651                                             1
..
DS_PERSON                                              78
OM_EXT_HEADER                                         701
OM_SHELL                                            1,765
OM_HEADER                                           5,826
OM_INSTANCE                                        12,644
Method 2

Use the following steps to determine the percentage of the cache used by an individual object at a given point in time:

  1. Find the Oracle internal object number of the segment by entering the following:

    SELECT data_object_id, object_type
      FROM user_objects
     WHERE object_name = upper('<SEGMENT_NAME>'); 
    
    

    Because two objects can have the same name (if they are different types of objects), use the OBJECT_TYPE column to identify the object of interest. If the object is owned by another user, then use the view DBA_OBJECTS or ALL_OBJECTS instead of USER_OBJECTS.

  2. Find the number of buffers in the buffer cache for SEGMENT_NAME:

    SELECT COUNT(*) BUFFERS 
      FROM V$BH
     WHERE objd = <DATA_OBJECT_ID>; 
    
    

    where DATA_OBJECT_ID is from step 1.

  3. Find the number of buffers in the instance:

    SELECT name, block_size, sum(buffers)
      FROM V$BUFFER_POOL
     GROUP BY name, block_size
     HAVING SUM(buffers) > 0;
    
    
  4. Calculate the ratio of buffers to total buffers to obtain the percentage of the cache currently used by SEGMENT_NAME.

    % cache used by segment_name = [buffers(Step2)/total buffers(Step3)]


    Note:

    This technique works only for a single segment. You must run the query for each partition for a partitioned object.  


Keep Pool

If there are certain segments in your application that are referenced frequently, then cache blocks from those segments in a separate cache called the KEEP buffer pool. Memory is allocated to the KEEP buffer pool by setting the parameter DB_KEEP_POOL_SIZE to the required size. The memory for the KEEP pool is not a subset of the default pool. Typical segments that can be kept are small reference tables, which are used frequently. Application developers and DBAs can determine which tables are candidates.

You can check the number of blocks from candidate tables by querying V$BH, as described in "Determining Which Segments Have Many Buffers in the Pool".


Note:

The NOCACHE clause has no effect on a table in the KEEP cache. 


The goal of the KEEP buffer pool is to retain objects in memory, thus avoiding I/O operations. The size of the KEEP buffer pool, therefore, depends on the objects that you want to keep in the buffer cache. You can compute an approximate size for the KEEP buffer pool by adding together the blocks used by all objects assigned to this pool. If you gather statistics on the segments, you can query DBA_TABLES.BLOCKS and DBA_TABLES.EMPTY_BLOCKS to determine the number of blocks used.

Calculate the hit ratio by taking two snapshots of system performance at different times using the previous query. Subtract the newest values from the older values for physical reads, block gets, and consistent gets, and use these values to compute the hit ratio.

A 100% buffer pool hit ratio might not be optimal. Often, you can decrease the size of your KEEP buffer pool and still maintain a sufficiently high hit ratio. Allocate blocks removed from use for the KEEP buffer pool to other buffer pools.


Note:

If an object grows in size, then it might no longer fit in the KEEP buffer pool. You will begin to lose blocks out of the cache. 


Each object kept in memory results in a trade-off. It is beneficial to keep frequently-accessed blocks in the cache, but retaining infrequently-used blocks results in less space for other, more active blocks.

Recycle Pool

It is possible to configure a RECYCLE buffer pool for blocks belonging to those segments that you do not want to remain in memory. The RECYCLE pool is good for segments that are scanned rarely or are not referenced frequently. If an application accesses the blocks of a very large object in a random fashion, then there is little chance of reusing a block stored in the buffer pool before it is aged out. This is true regardless of the size of the buffer pool (given the constraint of the amount of available physical memory). Because of this, the object's blocks need not be cached; those cache buffers can be allocated to other objects.

Memory is allocated to the RECYCLE buffer pool by setting the parameter DB_RECYCLE_POOL_SIZE to the required size. This memory for the RECYCLE buffer pool is not a subset of the default pool.

Do not discard blocks from memory too quickly. If the buffer pool is too small, then blocks can age out of the cache before the transaction or SQL statement has completed execution. For example, an application might select a value from a table, use the value to process some data, and then update the record. If the block is removed from the cache after the select statement, then it must be read from disk again to perform the update. The block should be retained for the duration of the user transaction.

Using Multiple Buffer Pools

To define a default buffer pool for an object, use the BUFFER_POOL keyword of the STORAGE clause. This clause is valid for CREATE and ALTER TABLE, CLUSTER, and INDEX SQL statements. After a BUFFER_POOL has been specified, all subsequent blocks read for the object are placed in that pool.

If a buffer pool is defined for a partitioned table or index, then each partition of the object inherits the buffer pool from the table or index definition, unless you override it with a specific buffer pool.

When the buffer pool of an object is changed using the ALTER statement, all buffers currently containing blocks of the altered segment remain in the buffer pool they were in before the ALTER statement. Newly loaded blocks and any blocks that have aged out and are reloaded go into the new buffer pool.

See Also:

Oracle9i SQL Reference for information on specifying BUFFER_POOL in the STORAGE clause 

Configuring and Using the Shared Pool and Large Pool

Oracle uses the shared pool to cache many different types of data. Cached data includes the textual and executable forms of PL/SQL blocks and SQL statements, dictionary cache data, and other data.

Proper use and sizing of the shared pool can reduce resource consumption in at least four ways:

  1. Parse overhead is avoided if the SQL statement is already in the shared pool. This saves CPU resources on the host and elapsed time for the end user.

  2. Latching resource usage is significantly reduced, which results in greater scalability.

  3. Shared pool memory requirements are reduced, because all applications use the same pool of shared SQL statements and dictionary resources.

  4. I/O resources are saved, because dictionary elements that are in the shared pool do not require disk access.

This section covers the following:

Shared Pool Concepts

The main components of the shared pool are the library cache and the dictionary cache. The library cache stores the executable (parsed or compiled) form of recently referenced SQL and PL/SQL code. The dictionary cache stores data referenced from the data dictionary. Many of the caches in the shared pool automatically increase or decrease in size, as needed, including the library cache and the dictionary cache. Old entries are aged out of these caches to accommodate new entries when the shared pool does not have free space for a new entry.

A cache miss on the data dictionary cache or library cache is more expensive than a miss on the buffer cache. For this reason, the shared pool should be sized to ensure that frequently used data is cached.

There are a number of features that make large memory allocations in the shared pool, such as the shared server, parallel query, or Recovery Manager. Oracle recommends segregating the SGA memory used by these features by configuring a distinct memory area, called the large pool.

See Also:

"Consider using the Large Pool" for more information on configuring the large pool 

Allocation of memory from the shared pool is performed in chunks. This allows large objects (over 5k) to be loaded into the cache without requiring a single contiguous area, hence reducing the possibility of running out of enough contiguous memory due to fragmentation.

Infrequently, Java, PL/SQL, or SQL cursors may make allocations out of the shared pool that are larger than 5k. To allow these allocations to happen most efficiently, Oracle segregates a small amount of the shared pool. This memory is used if the shared pool does not have enough space. The segregated area of the shared pool is called the reserved pool.

See Also:

"Consider Configuring the Reserved Pool" for more information on the reserved area of the shared pool 

Dictionary Cache Concepts

Information stored in the data dictionary cache includes usernames, segment information, profile data, tablespace information, and sequence numbers. The dictionary cache also caches descriptive information, or metadata, about schema objects. Oracle uses this metadata when parsing SQL cursors or during the compilation of PL/SQL programs.

Library Cache Concepts

The library cache holds executable forms of SQL cursors, PL/SQL programs, and Java classes. This section focuses on tuning as it relates to cursors, PL/SQL programs, and Java classes. These are collectively referred to as application code.

When application code is run, Oracle attempts to reuse existing code if it has been executed previously and can be shared. If the parsed representation of the statement does exist in the library cache and it can be shared, then Oracle reuses the existing executable. This is known as a soft parse, or a library cache hit.

If Oracle is unable to use existing code, then a new executable version of the application code must be built. This is known as a hard parse, or a library cache miss.

See Also:

"SQL Sharing Criteria" for details on when a SQL and PL/SQL statements can be shared 

Library cache misses can occur on either the parse step or the execute step when processing a SQL statement.

When an application makes a parse call for a SQL statement, if the parsed representation of the statement does not already exist in the library cache, then Oracle parses the statement and stores the parsed form in the shared pool. This is a hard parse. You might be able to reduce library cache misses on parse calls by ensuring that all shareable SQL statements are in the shared pool whenever possible.

If an application makes an execute call for a SQL statement, and if the executable portion of the previously built SQL statement has been aged out (that is, deallocated) from the library cache to make room for another statement, then Oracle implicitly reparses the statement, creating a new shared SQL area for it, and executes it. This also results in a hard parse. Usually, you can reduce library cache misses on execution calls by allocating more memory to the library cache.

In order to perform a hard parse, Oracle uses more resources than during a soft parse. Resources used for a soft parse include CPU and library cache latch gets. Resources required for a hard parse include additional CPU, library cache latch gets, and shared pool latch gets.

SQL Sharing Criteria

Oracle automatically determines whether a SQL statement or PL/SQL block being issued is identical to another statement currently in the shared pool.

Oracle's performs the following steps for the comparison:

  1. The text of the statement issued is compared to existing statements in the shared pool.

    The text of the statement is hashed. If there is no matching hash value, then the SQL statement does not currently exist in the shared pool, and a hard parse is performed.

    If there is a matching hash value for an existing SQL statement in the shared pool, then Oracle compares the text of the matched statement to the text of the statement hashed to see if they are identical. The text of the SQL statements or PL/SQL blocks must be identical, character for character, including spaces, case, and comments. For example, the following statements cannot use the same shared SQL area:

    SELECT * FROM employees;
    SELECT * FROM Employees;
    SELECT *  FROM employees;
    
    

    Usually, SQL statements that differ only in literals cannot use the same shared SQL area. For example, the following SQL statements do not resolve to the same SQL area:

    SELECT count(1) FROM employees where manager_id = 121;
    SELECT count(1) FROM employees where manager_id = 247;
    
    

    The only exception to this is when the parameter CURSOR_SHARING has been set to SIMILAR or FORCE. Similar statements also share SQL areas when the CURSOR_SHARING parameter is set to SIMILAR or FORCE. The costs and benefits involved in using CURSOR_SHARING are explained later in this section.

    See Also::

    Oracle9i Database Reference for more information on the CURSOR_SHARING parameter 

  2. References to schema objects in the SQL statements or PL/SQL blocks must resolve to the same object in the same schema.

    The objects referenced in the issued statement are compared to the referenced objects of all existing statements identified above to ensure that they are identical.

    For example, if two users each issue the following SQL statement:

    SELECT * FROM employees;
    
    

    and they each have their own employees table, then the above statement is not considered identical, because the statement references different tables for each user.

  3. Bind variables in the SQL statements must match in name, datatype, and length.

    For example, the following statements cannot use the same shared SQL area, because the bind variable names differ:

    SELECT * FROM employees WHERE department_id = :department_id;
    SELECT * FROM employees WHERE department_id = :dept_id;
    
    

    Many Oracle products (such as Oracle Forms and the precompilers) convert the SQL before passing statements to the database. Characters are uniformly changed to uppercase, white space is compressed, and bind variables are renamed so that a consistent set of SQL statements is produced.

  4. The session's environment must be identical. Items compared include the following:

    • Optimization approach and goal. SQL statements must be optimized using the same optimization approach and, in the case of the cost-based approach, the same optimization goal.

    • Session-configurable parameters such as SORT_AREA_SIZE.

Using the Shared Pool Effectively

An important purpose of the shared pool is to cache the executable versions of SQL and PL/SQL statements. This allows multiple executions of the same SQL or PL/SQL code to be performed without the resources required to hard parse, which results in significant reductions in CPU, memory, and latch usage.

The shared pool is also able to support unshared SQL in data warehousing applications, which execute low-concurrency high-resource SQL statements. In this situation, using unshared SQL with literal values is recommended. Using literal values rather than bind variables allows the optimizer to make good column selectivity estimates, thus providing an optimal data access plan.

See Also:

Oracle9i Data Warehousing Guide 

In an OLTP system, there are a number of ways to ensure efficient use of the shared pool and related resources. Discuss the following possibilities with application developers and agree on strategies to ensure that the shared pool is used effectively:

Efficient use of the shared pool in high-concurrency OLTP systems significantly reduces the probability of parse-related application scalability issues.

Share Cursors

Reuse of shared SQL for multiple users running the same application, avoids hard parsing. Soft parses provide a significant reduction in resource usage, such as shared pool and library cache latches. To share cursors, do the following:

Maintain Connections

Large OLTP applications with middle-tiers should maintain connections, rather than connecting and disconnecting for each database request. Maintaining connections saves CPU resources and database resources, such as latches.

Single-user Log on and Qualified Table Reference

Large OLTP systems where users log in to the database as their own userID can benefit from qualifying the segment owner explicitly, rather than by using public synonyms. This significantly reduces the number of entries in the dictionary cache. For example:

SELECT employee_id FROM hr.employees WHERE department_id = :dept_id;

An alternative to qualifying table names is to connect to the database via a single userID, rather than individual userIDs. User-level validation can take place locally on the middle-tier. Reducing the number of distinct userIDs also reduces the load on the dictionary cache.

Use of PL/SQL

Using stored PL/SQL packages can overcome much of the scalability issues for systems with thousands of users, each with individual user sign-on and public synonyms. This is because a package is executed as the owner, rather than the caller, which reduces dictionary cache load considerably.

Avoid Performing DDL

Avoid performing DDL operations on high-usage segments during peak hours. Performing DDL on such segments often results in the dependent SQL being invalidated, and hence reparsed on a later execution.

Cache Sequence Numbers

Allocating sufficient cache for frequently updated sequence numbers significantly reduces the frequency of dictionary cache locks, which improves scalability. The CACHE keyword on the CREATE SEQUENCE or ALTER SEQUENCE statement lets you configure the number of cached entries for each sequence.

See Also:

Oracle9i SQL Reference for details on the CREATE SEQUENCE and ALTER SEQUENCE statements 

Cursor Access and Management

Depending on the Oracle application tool you are using, it is possible to control how frequently your application performs parse calls.

The frequency with which your application either closes cursors or reuses existing cursors for new SQL statements affects the amount of memory used by a session, and often the amount of parsing performed by that session.

An application that closes cursors or reuses cursors (for a different SQL statement, rather than the same SQL statement), does not need as much session memory as an application that keeps cursors open. Conversely, that same application may need to perform more parse calls, hence using extra CPU and Oracle resources.

Cursors associated with SQL statements that are not executed frequently can be closed or reused for other statements, because the likelihood of reexecuting (and reparsing) that statement is low.

Extra parse calls are required when a cursor containing a SQL statement that will be reexecuted is closed or reused for another statement. Had the cursor remained open, it could have been reused without the overhead of issuing a parse call.

The ways in which you control cursor management depends on your application development tool. The following section introduces the methods used for some Oracle tools.

See Also:

 
Reducing Parse Calls with OCI

Do not close and reopen cursors that you will be reexecuting. Instead, leave the cursors open, and change the literal values in the bind variables before execution.

Avoid reusing statement handles for new SQL statements when the existing SQL statement will be reexecuted in the future.

Reducing Parse Calls with the Oracle Precompilers

When using the Oracle precompilers, you can control when cursors are closed by setting precompiler clauses. In Oracle mode, the clauses are as follows:

Oracle recommends that you not use ANSI mode, in which the values of HOLD_CURSOR and RELEASE_CURSOR are switched.

The precompiler clauses can be specified on the precompiler command line or within the precompiler program. With these clauses, you can employ different strategies for managing cursors during execution of the program.

See Also:

Your language's precompiler manual more information on these clauses 

Reducing Parse Calls with SQLJ

Prepare the statement, then reexecute the statement with the new values for the bind variables. The cursor stays open for the duration of the session.

Reducing Parse Calls with JDBC

Avoid closing cursors if they will be reexecuted, because the new literal values can be bound to the cursor for reexecution. Alternatively, JDBC provides a SQL Statement Cache within the JDBC client using the setStmtCacheSize() method. Using this method, JDBC creates a SQL statement cache that is local to the JDBC program.

See Also:

Oracle9i JDBC Developer's Guide and Reference manual for more information on using the JDBC SQL statement cache 

Reducing Parse Calls with Oracle Forms

With Oracle Forms, it is possible to control some aspects of cursor management. You can exercise this control either at the trigger level, at the form level, or at run time.

See Also:

Oracle Forms Reference manual for more information on the reuse of private SQL areas by Oracle Forms 

Sizing the Shared Pool

When configuring a brand new instance, it is impossible to know the correct size to make the shared pool cache. Typically, a DBA makes a first estimate for the cache size, then runs a representative workload on the instance, and examines the relevant statistics to see whether the cache is under- or over-configured.

For most OLTP applications, shared pool size is an important factor to the application performance. Shared pool size is less important for applications that issue a very limited number of discrete SQL statements, such as DSS systems.

If the shared pool is too small, then extra resources are used to manage the limited amount of available space. This consumes CPU and latching resources, and causes contention.

Optimally, the shared pool should be just large enough to cache frequently accessed objects. Having a significant amount of free memory in the shared pool is a waste of memory.

Shared Pool - Library Cache Statistics

The goal when sizing the library cache is to ensure that SQL statements that will be executed multiple times are cached, without allocating too much memory.

The statistic that shows the amount of reloading (that is, reparsing) of a previously cached SQL statement that was aged out of the cache is the RELOADS column in the V$LIBRARYCACHE view. In an application that reuses SQL effectively, on a system with an optimal shared pool size, the RELOADS statistic would have a value of zero.

The INVALIDATIONS column in this view shows the number of times library cache data was invalidated and had to be reparsed. INVALIDATIONS should be near zero. This means SQL statements that could have been shared were invalidated by some operation (for example, a DDL). This statistic should be near zero on OLTP systems during peak loads.

Another key statistic is the amount of free memory in the shared pool at peak times. The amount of free memory can be queried from V$SGASTAT, looking at the free memory for the shared pool. Optimally, free memory should be as low as possible, without causing any RELOADS on the system.

Lastly, a broad indicator of library cache health is the library cache hit ratio. This value should be considered along with the above statistics and other data, such as the rate of hard parsing and whether there is any shared pool or library cache latch contention.

These statistics are discussed below in more detail.

V$LIBRARYCACHE

You can monitor statistics reflecting library cache activity by examining the dynamic performance view V$LIBRARYCACHE. These statistics reflect all library cache activity since the most recent instance startup.

Each row in this view contains statistics for one type of item kept in the library cache. The item described by each row is identified by the value of the NAMESPACE column. Rows of the view with the following NAMESPACE values reflect library cache activity for SQL statements and PL/SQL blocks:

Rows with other NAMESPACE values reflect library cache activity for object definitions that Oracle uses for dependency maintenance.

See Also:

Chapter 24, "Dynamic Performance Views for Tuning" for information on columns of the V$LIBRARYCACHE view 

To examine each namespace individually, use the following query:

SELECT namespace
     , pins
     , pinhits
     , reloads
     , invalidations
  FROM V$LIBRARYCACHE
 ORDER BY namespace;

The output of this query could look like the following:

NAMESPACE             PINS    PINHITS    RELOADS INVALIDATIONS
--------------- ---------- ---------- ---------- -------------
BODY                  8870       8819          0             0
CLUSTER                393        380          0             0
INDEX                   29          0          0             0
OBJECT                   0          0          0             0
PIPE                 55265      55263          0             0
SQL AREA          21536413   21520516      11204             2
TABLE/PROCEDURE   10775684   10774401          0             0
TRIGGER               1852       1844          0             0

To calculate the library cache hit ratio, use the following formula:

Using the library cache hit ratio formula above, the cache hit ratio is the following:

SUM(PINHITS)/SUM(PINS)
----------------------
            .999466248


Note:

The queries return data from instance startup, rather than statistics gathered during an interval, which is preferable.  


See Also:

Chapter 20, "Oracle Tools to Gather Database Statistics" for information on how gather information over an interval 

Examining the data returned leads to these observations:

The amount of free memory in the shared pool is reported in V$SGASTAT. Report the current value from this view using the following query:

SELECT * FROM V$SGASTAT 
 WHERE NAME = 'free memory'
   AND POOL = 'shared pool';

The output will be similar to the following:

POOL        NAME                            BYTES
----------- -------------------------- ----------
shared pool free memory                   4928280

If there is always free memory available in the shared pool, then increasing the size of the pool offers little or no benefit. However, just because the shared pool is full does not necessarily mean there is a problem. It may be indicative of a well-configured system.

Shared Pool - Dictionary Cache Statistics

The algorithm Oracle uses to manage data in the shared pool ages out library cache data in preference to aging out dictionary cache data. Therefore, configuring the library cache to an acceptable size usually ensures that the data dictionary cache is also adequately sized.

Misses on the data dictionary cache are to be expected in some cases. On instance startup, the data dictionary cache contains no data. Therefore, any SQL statement issued is likely to result in cache misses. As more data is read into the cache, the likelihood of cache misses decreases. Eventually, the database reaches a steady state, in which the most frequently used dictionary data is in the cache. At this point, very few cache misses occur.

Each row in the V$ROWCACHE view contains statistics for a single type of the data dictionary item. These statistics reflect all data dictionary activity since the most recent instance startup. These columns in the V$ROWCACHE view reflect the use and effectiveness of the data dictionary cache:

PARAMETER

Identifies a particular data dictionary item. For each row, the value in this column is the item prefixed by dc_. For example, in the row that contains statistics for file descriptions, this column has the value dc_files.

GETS

Shows the total number of requests for information on the corresponding item. For example, in the row that contains statistics for file descriptions, this column has the total number of requests for file descriptions data.

GETMISSES

Shows the number of data requests which were not satisfied by the cache (requiring an I/O).

MODIFICATIONS

Shows the number of times data in the dictionary cache was updated.

Use the following query to monitor the statistics in the V$ROWCACHE. view over a period of time while your application is running. The derived column PCT_SUCC_GETS can be considered the item-specific hit ratio:

column parameter format a21
column pct_succ_gets format 999.9
column updates format 999,999,999

SELECT parameter
     , sum(gets)
     , sum(getmisses)
     , 100*sum(gets - getmisses) / sum(gets)  pct_succ_gets
     , sum(modifications)                     updates
  FROM V$ROWCACHE
 WHERE gets > 0
 GROUP BY parameter;

The output of this select will be similar to the following:

PARAMETER              SUM(GETS) SUM(GETMISSES) PCT_SUCC_GETS      UPDATES
--------------------- ---------- -------------- ------------- ------------
dc_database_links             81              1          98.8            0
dc_free_extents            44876          20301          54.8       40,453
dc_global_oids                42              9          78.6            0
dc_histogram_defs           9419            651          93.1            0
dc_object_ids              29854            239          99.2           52
dc_objects                 33600            590          98.2           53
dc_profiles                19001              1         100.0            0
dc_rollback_segments       47244             16         100.0           19
dc_segments               100467          19042          81.0       40,272
dc_sequence_grants           119             16          86.6            0
dc_sequences               26973             16          99.9       26,811
dc_synonyms                 6617            168          97.5            0
dc_tablespace_quotas         120              7          94.2           51
dc_tablespaces            581248             10         100.0            0
dc_used_extents            51418          20249          60.6       42,811
dc_user_grants             76082             18         100.0            0
dc_usernames              216860             12         100.0            0
dc_users                  376895             22         100.0            0

Examining the data returned by the sample query leads to these observations:

It is also possible to calculate an overall dictionary cache hit ratio as shown below, although summing up the data over all the caches will lose the finer granularity of data:

SELECT (SUM(GETS - GETMISSES - FIXED)) / SUM(GETS) "ROW CACHE" FROM V$ROWCACHE;

Interpreting Shared Pool Statistics

Increasing Memory Allocation

Increasing the amount of memory for the shared pool increases the amount of memory usable by both the library cache and the dictionary cache.

Allocating Additional Memory for the Library Cache

To ensure that shared SQL areas remain in the cache after their SQL statements are parsed, increase the amount of memory available to the library cache until the V$LIBRARYCACHE.RELOADS value is near zero. To increase the amount of memory available to the library cache, increase the value of the initialization parameter SHARED_POOL_SIZE. The maximum value for this parameter depends on your operating system. This measure reduces implicit reparsing of SQL statements and PL/SQL blocks on execution.

To take advantage of additional memory available for shared SQL areas, you might also need to increase the number of cursors permitted for a session. You can do this by increasing the value of the initialization parameter OPEN_CURSORS.

Allocating Additional Memory to the Data Dictionary Cache

Examine cache activity by monitoring the GETS and GETMISSES columns. For frequently accessed dictionary caches, the ratio of total GETMISSES to total GETS should be less than 10% or 15%, depending on the application.

Consider increasing the amount of memory available to the cache if all of the following are true:

Increase the amount of memory available to the data dictionary cache by increasing the value of the initialization parameter SHARED_POOL_SIZE.

Reducing Memory Allocation

If your RELOADS are near zero, and if you have a small amount of free memory in the shared pool, then the shared pool is probably large enough to hold the most frequently accessed data.

If you always have significant amounts of memory free in the shared pool, and if you would like to allocate this memory elsewhere, then you might be able to reduce the shared pool size and still maintain good performance.

To make the shared pool smaller, reduce the size of the cache by changing the value for the parameter SHARED_POOL_SIZE.

Consider using the Large Pool

Unlike the shared pool, the large pool does not have an LRU list. Oracle does not attempt to age memory out of the large pool.

You should consider configuring a large pool if your instance uses any of the following:

Tuning the Large Pool and Shared Pool for the Shared Server Architecture

As Oracle allocates shared pool memory for shared server session memory, the amount of shared pool memory available for the library cache and dictionary cache decreases. If you allocate this session memory from a different pool, then Oracle can use the shared pool primarily for caching shared SQL and not incur the performance overhead from shrinking the shared SQL cache.

Oracle recommends using the large pool to allocate shared server-related UGA (User Global Area), not the shared pool. This is because Oracle uses the shared pool to allocate SGA (Shared Global Area) memory for other purposes, such as shared SQL and PL/SQL procedures. Using the large pool instead of the shared pool decreases fragmentation of the shared pool.

To store shared server-related UGA in the large pool, specify a value for the initialization parameter LARGE_POOL_SIZE. To see which pool (shared pool or large pool) the memory for an object resides, check the column POOL in V$SGASTAT. The large pool is not configured by default; its minimum value is 300K. If you do not configure the large pool, then Oracle uses the shared pool for shared server user session memory.

Configure the size of the large pool based on the number of simultaneously active sessions. Each application requires a different amount of memory for session information, and your configuration of the large pool or SGA should reflect the memory requirement. For example, assuming that the shared server requires 200K - 300K to store session information for each active session. If you anticipate 100 active sessions simultaneously, then configure the large pool to be 30M, or increase the shared pool accordingly if the large pool is not configured.


Note:

If a shared server architecture is used, then Oracle allocates some fixed amount of memory (about 10K) per configured session from the shared pool, even if you have configured the large pool. The CIRCUITS initialization parameter specifies the maximum number of concurrent shared server connections that the database allows. 


See Also:

 
Determining an Effective Setting for Shared Server UGA Storage

The exact amount of UGA Oracle uses depends on each application. To determine an effective setting for the large or shared pools, observe UGA use for a typical user and multiply this amount by the estimated number of user sessions.

Even though use of shared memory increases with shared servers, the total amount of memory use decreases. This is because there are fewer processes, and therefore Oracle uses less PGA memory with shared servers when compared to dedicated server environments.


Note:

For best performance with sorts using shared servers, set SORT_AREA_SIZE and SORT_AREA_RETAINED_SIZE to the same value. This keeps the sort result in the large pool instead of having it written to disk.  


Checking System Statistics in the V$SESSTAT View

Oracle collects statistics on total memory used by a session and stores them in the dynamic performance view V$SESSTAT:

session UGA memory

The value of this statistic is the amount of memory in bytes allocated to the session.

session UGA memory max

The value of this statistic is the maximum amount of memory in bytes ever allocated to the session.

To find the value, query V$STATNAME.

You can use the following query to decide how much larger to make the shared pool if you are using a shared server. Issue the following queries while your application is running:

SELECT SUM(VALUE) || ' BYTES' "TOTAL MEMORY FOR ALL SESSIONS"

FROM V$SESSTAT, V$STATNAME
WHERE NAME = 'session uga memory'
AND V$SESSTAT.STATISTIC# = V$STATNAME.STATISTIC#;
SELECT SUM(VALUE) || ' BYTES' "TOTAL MAX MEM FOR ALL SESSIONS"
FROM V$SESSTAT, V$STATNAME
WHERE NAME = 'session uga memory max'
AND V$SESSTAT.STATISTIC# = V$STATNAME.STATISTIC#;

These queries also select from the dynamic performance view V$STATNAME to obtain internal identifiers for session memory and max session memory. The results of these queries could look like the following:

TOTAL MEMORY FOR ALL SESSIONS
-----------------------------
157125 BYTES

TOTAL MAX MEM FOR ALL SESSIONS
------------------------------
417381 BYTES

The result of the first query indicates that the memory currently allocated to all sessions is 157,125 bytes. This value is the total memory whose location depends on how the sessions are connected to Oracle. If the sessions are connected to dedicated server processes, then this memory is part of the memories of the user processes. If the sessions are connected to shared server processes, then this memory is part of the shared pool.

The result of the second query indicates that the sum of the maximum sizes of the memories for all sessions is 417,381 bytes. The second result is greater than the first, because some sessions have deallocated memory since allocating their maximum amounts.

You can use the result of either of these queries to determine how much larger to make the shared pool if you use a shared server architecture. The first value is likely to be a better estimate than the second unless nearly all sessions are likely to reach their maximum allocations at the same time.

Limiting Memory Use Per User Session by Setting PRIVATE_SGA

You can set the PRIVATE_SGA resource limit to restrict the memory used by each client session from the SGA. PRIVATE_SGA defines the number of bytes of memory used from the SGA by a session. However, this parameter is used rarely, because most DBAs do not limit SGA consumption an a user-by-user basis.

See Also:

Oracle9i SQL Reference, ALTER RESOURCE COST statement, for more information about setting the PRIVATE_SGA resource limit 

Reducing Memory Use With Three-Tier Connections

If you have a high number of connected users, then you can reduce memory usage by implementing "three-tier connections". This by-product of using a TP monitor is feasible only with pure transactional models, because locks and uncommitted DMLs cannot be held between calls. A shared server environment is much less restrictive of the application design than a TP monitor. It dramatically reduces operating system process count and context switches by enabling users to share a pool of servers. A shared server environment also substantially reduces overall memory usage even though more SGA is used in shared server mode.

Consider Using CURSOR_SPACE_FOR_TIME

If you have no library cache misses, then you might be able to accelerate execution calls by setting the value of the initialization parameter CURSOR_SPACE_FOR_TIME to true. This parameter specifies whether a cursor can be deallocated from the library cache to make room for a new SQL statement. CURSOR_SPACE_FOR_TIME has the following values meanings:

Setting the value of the parameter to true saves Oracle a small amount of time and can slightly improve the performance of execution calls. This value also prevents the deallocation of cursors until associated application cursors are closed.

Do not set the value of CURSOR_SPACE_FOR_TIME to true if you have found library cache misses on execution calls. Such library cache misses indicate that the shared pool is not large enough to hold the shared SQL areas of all concurrently open cursors. If the value is true, and if the shared pool has no space for a new SQL statement, then the statement cannot be parsed, and Oracle returns an error saying that there is no more shared memory. If the value is false, and if there is no space for a new statement, then Oracle deallocates an existing cursor. Although deallocating a cursor could result in a library cache miss later (only if the cursor is reexecuted), it is preferable to an error halting your application because a SQL statement cannot be parsed.

Do not set the value of CURSOR_SPACE_FOR_TIME to true if the amount of memory available to each user for private SQL areas is scarce. This value also prevents the deallocation of private SQL areas associated with open cursors. If the private SQL areas for all concurrently open cursors fills your available memory so that there is no space for a new SQL statement, then the statement cannot be parsed. Oracle returns an error indicating that there is not enough memory.

Consider Caching Session Cursors

If an application repeatedly issues parse calls on the same set of SQL statements, then the reopening of the session cursors can affect system performance. Session cursors can be stored in a session cursor cache. This feature can be particularly useful for applications designed using Oracle Forms, because switching from one form to another closes all session cursors associated with the first form.

Oracle checks the library cache to determine whether more than th