Вы находитесь на странице: 1из 20

Result Cache Features

There are three new result caching features in 11g:

 query result cache;


 PL/SQL function result cache; and
 client OCI result cache.

Query Result Cache

As its name suggests, the query result cache is used to store the results of SQL queries for re-use in
subsequent executions. By caching the results of queries, Oracle can avoid having to repeat the
potentially time-consuming and intensive operations that generated the result set in the first place (for
example, sorting/aggregation, physical I/O, joins etc). The cache results themselves are available
across the instance (i.e. for use by sessions other than the one that first executed the query) and are
maintained by Oracle in a dedicated area of memory. Unlike our home grown solutions using
associative arrays or global temporary tables, the query result cache is completely transparent to our
applications. It is also maintained for consistency automatically, unlike our own caching programs.

Unlike a PL/SQL collection which reside in private PGA RAM, a result cache shareable and is stored in
SGA memory. Unlike materialized views, the result_cache output is stored in the RAM of the SGA, and
in 11g RAC, each node will have it's own private area for storing result_cache output.

There are two ways to visualize a 11g result_cache output:

A sharable PL/SQL collection - Oracle PL/SQL allows for result sets to be saved in RAM for later use by
the session using an array heap called a "collection". The result_cache hint expands this functionality
to allow for inter-session access to pre-summarized row data in RAM. Also, PL/SQL collections (arrays)
were stored in the PGA, whereas the result_cache output is stored in the SGA region.

An ad-hoc, RAM-based Materialized View - Unlike traditional materialized views where tables are pre-
joined and stored on slow disk, the result_cache hint allows the denormalize for of the table rows to be
stored in super-fast RAM with over 100x faster access speeds.
Unlike the process of writing a materialized view to a mechanical platter disk, the result cache is
stored in super-fast RAM, alleviating the need to index the result set, as you might with a materialized
view on a disk. Also, creating a result cache does not require direct DBA intervention, just the setting
of four new init.ora parms.

Since the result cache is all about improving performance, let's examine what we can expect when we
implement this feature.

We will examine the features of the query result cache in more detail throughout this article.

Database Configuration

The initialisation parameters are as follows.

SQL> SELECT name, value, isdefault

2 FROM v$parameter

3 WHERE name LIKE 'result_cache%';

NAME VALUE ISDEFAULT

---------------------------------- ------------------ ---------

result_cache_mode MANUAL TRUE

result_cache_max_size 1081344 TRUE

result_cache_max_result 5 TRUE

result_cache_remote_expiration 0 TRUE

4 rows selected.

 result_cache_mode: the result cache can be enabled in three ways: via hint, alter session or
alter system. Default is MANUAL which means that we need to explicitly request caching via the
RESULT_CACHE hint;

 result_cache_max_size: this is the size of the result cache in bytes. The cache is allocated
directly from the shared pool but is maintained separately (for example, flushing the shared pool
will not flush the result cache);

 result_cache_max_result: this specifies the highest percentage of the cache that is able to be
used by a single result set (default 5%); and

 result_cache_remote_expiration: this specifies the number of minutes for which a result set
based on a remote object can remain valid. The default is 0 which means that result sets
dependant on remote objects will not be cached.

The cache size is dynamic and can be changed either permanently or until the instance is restarted. We
will roughly double the size of the cache for this article and verify that we have a larger result cache as
follows (note this was run as SYSDBA).

 The cache result is invalidated for any DML on the tables the result relies on.
 The cache miss, when the result is invalidated is expensive
 The cache miss, when the result is not in the result cache is expensive
 The ‘expensive’ here is a scalability issue: not detected in unit tests, but big contention when
load increases
This means that, whatever the Oracle Documentation says, the benefit of result cache comes only at cache hit:
when the result of the function is already there, and has not been invalidated. If you call the same function with
always the same parameter, frequently, and with no changes in the related tables, then we are in the good case.
Caching results manually

As we saw earlier, the default caching mode for this instance is MANUAL. This means that query
result sets will not be cached unless we instruct Oracle to do so by using the RESULT_CACHE hint.
In our first example below, we will manually cache the results of a simple aggregate query.

SQL> SELECT value

2 FROM v$parameter

3 WHERE name = 'result_cache_mode';

VALUE

----------------

MANUAL

1 row selected.

We will now run a query and cache its results. We will run this through Autotrace because we are
interested in both the workload statistics and the execution plan (Autotrace will also conveniently
suppress the query output).

SQL> set autotrace traceonly

SQL> set timing on

SQL> SELECT /*+ RESULT_CACHE */

2 p.prod_name
3 , SUM(s.amount_sold) AS total_revenue

4 , SUM(s.quantity_sold) AS total_sales

5 FROM sales s

6 , products p
7 WHERE s.prod_id = p.prod_id

8 GROUP BY

9 p.prod_name;

71 rows selected.
Elapsed: 00:00:05.00

Using the RESULT_CACHE hint, we have instructed Oracle to cache the results of this aggregate
query. We can see that it returned 71 rows and took 5 seconds to execute. We will see the amount
of work that Oracle did to generate these results further below, but first we will see the execution
plan (note that this is a theoretical explain plan and not the real execution plan, but is a good
approximation in this system).

Execution Plan

----------------------------------------------------------

Plan hash value: 504757596

----------------------------------------------------------------------- ... -----------------

| Id | Operation | Name | Rows | ... | Pstart| Pstop |

----------------------------------------------------------------------- ... -----------------


| 0 | SELECT STATEMENT | | 71 | ... | | |
| 1 | RESULT CACHE | 091zc7mvn8ums36mbd2gqac4h0 | | ... | | |

| 2 | HASH GROUP BY | | 71 | ... | | |

|* 3 | HASH JOIN | | 72 | ... | | |

| 4 | VIEW | VW_GBC_5 | 72 | ... | | |

| 5 | HASH GROUP BY | | 72 | ... | | |

| 6 | PARTITION RANGE ALL| | 918K| ... | 1 | 28 |


| 7 | TABLE ACCESS FULL | SALES | 918K| ... | 1 | 28 |

| 8 | TABLE ACCESS FULL | PRODUCTS | 72 | ... | | |

----------------------------------------------------------------------- ... -----------------

Predicate Information (identified by operation id):

---------------------------------------------------

3 - access("ITEM_1"="P"."PROD_ID")

Result Cache Information (identified by operation id):

------------------------------------------------------

1 - column-count=3; dependencies=(SH.SALES, SH.PRODUCTS); parameters=(nls); name="SELECT /*+


RESULT_CACHE */
p.prod_name

, SUM(s.amount_sold) AS total_revenue

, SUM(s.quantity_sold) AS total_"

Note the highlighted sections of the execution plan. It contains some new information, which we can
summarise as follows:

 first, we can see a new operation, "RESULT CACHE" at operation ID=1. This is the last step in
this particular example and it is telling us that Oracle will cache the results of the preceding
operations;

 second, we see a system-generated name beside the RESULT CACHE operation. This is used
internally as a key for looking up and matching SQL statements to their cached results;

 third, we see a new section in the plan report on the result cache metadata for this query.
This section includes information such as the objects that the results are dependent on (i.e.
to maintain cache coherency) and the leading part of the SQL text that generated the results.

Finally, the Auto trace report displays the work that Oracle performed to generate these results.

Statistics

----------------------------------------------------------

14871 recursive calls


0 db block gets

4890 consistent gets

1745 physical reads

0 redo size
3526 bytes sent via SQL*Net to client

416 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client


136 sorts (memory)

0 sorts (disk)

71 rows processed

We can see a range of I/O and CPU activity in these figures, as expected. We will now test the new
query result cache by running the same query a second time and comparing the Autotrace report, as
follows.

SQL> SELECT /*+ RESULT_CACHE */

2 p.prod_name
3 , SUM(s.amount_sold) AS total_revenue

4 , SUM(s.quantity_sold) AS total_sales

5 FROM sales s

6 , products p
7 WHERE s.prod_id = p.prod_id

8 GROUP BY

9 p.prod_name;

71 rows selected.

Elapsed: 00:00:00.01

Execution Plan

----------------------------------------------------------

Plan hash value: 504757596

----------------------------------------------------------------------- ... ---------------


--

| Id | Operation | Name | Rows | ... | Pstart| Pstop


|

----------------------------------------------------------------------- ... ---------------


--

| 0 | SELECT STATEMENT | | 71 | ... | |


|

| 1 | RESULT CACHE | 091zc7mvn8ums36mbd2gqac4h0 | | ... | |


|

| 2 | HASH GROUP BY | | 71 | ... | |


|

|* 3 | HASH JOIN | | 72 | ... | |


|

| 4 | VIEW | VW_GBC_5 | 72 | ... | |


|

| 5 | HASH GROUP BY | | 72 | ... | |


|

| 6 | PARTITION RANGE ALL| | 918K| ... | 1 | 28


|

| 7 | TABLE ACCESS FULL | SALES | 918K| ... | 1 | 28


|

| 8 | TABLE ACCESS FULL | PRODUCTS | 72 | ... | |


|

----------------------------------------------------------------------- ... ---------------


--
Predicate Information (identified by operation id):

---------------------------------------------------

3 - access("ITEM_1"="P"."PROD_ID")

Result Cache Information (identified by operation id):

------------------------------------------------------

1 - column-count=3; dependencies=(SH.SALES, SH.PRODUCTS); parameters=(nls); name="SELECT


/*+ RESULT_CACHE */

p.prod_name

, SUM(s.amount_sold) AS total_revenue

, SUM(s.quantity_sold) AS total_"

Statistics

----------------------------------------------------------
0 recursive calls

0 db block gets

0 consistent gets

0 physical reads
0 redo size

3526 bytes sent via SQL*Net to client

416 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client


0 sorts (memory)

0 sorts (disk)

71 rows processed

Starting with the statistics report, we can see that this time Oracle has done very little work. In fact
it has performed none of the I/O, sorting or recursive SQL that was required to answer our query
the first time. Oracle has recognised that the query can be satisfied from the result cache and simply
returned the pre-computed answer to us instead, in approximately 0.1 seconds.

Interestingly, the execution plan remains the same (this is to be expected because the SQL is not
optimised a second time) but is now slightly misleading. None of the plan operations actually take
place once we have a reusable resultset, but the presence of the RESULT CACHE operation should
alert us to the fact that we might already have a cached set of results. In fact, we can use the
information supplied in this plan to verify the existence of a cached resultset for ourselves, which we
will examine later in this article.

We have now seen a simple example of query result caching. Minimising the amount of work that
Oracle has to do to answer our query will reduce the time it takes. It also follows that the more work
Oracle can avoid, the better the gains from caching.

Controlling Result Set Cache Memory Utilization. Oracle 11g also provides several methods to
limit precisely the amount of memory that may be allocated for SQL query result set caching:

RESULT_CACHE_MAX_SIZE. To reserve an appropriate amount of SGA memory for all local result
caches, the DBA can specify a value for the RESULT_CACHE_MAX_SIZEinitialization parameter.
Oracle 11g automatically rounds the supplied value to the nearest 32K boundary.
If no value is supplied, then Oracle 11g uses the following algorithm to allocate memory for Result
Caches:
 If a value has been specified for the new Oracle 11g MEMORY_TARGET parameter (i.e. the
total memory allocated to both SGA and PGA for the database instance), then Oracle
sets RESULT_CACHE_MAX_SIZE to 0.25% of MEMORY_TARGET.
 If no value for MEMORY_TARGET has been set, but a value for SGA_TARGET has been set,
then Oracle 11g sets RESULT_CACHE_MAX_SIZE to 0.5% of SGA_TARGET.
 Finally, if neither a value for MEMORY_TARGET or SGA_TARGET has been set, then Oracle
sets RESULT_CACHE_MAX_SIZE to 1.0% of the memory allocated to the Shared Pool
based on the setting for SHARED_POOL_SIZE.

Regardless of which calculation method is used, note that Oracle 11g will never
set RESULT_CACHE_MAX_SIZE to more than 75% of SHARED_POOL_SIZE. Moreover, note that
if the DBA wants to deactivate SQL Result Caching features completely, she merely needs to set the
size of this memory allocation area to zero (0) to tell Oracle 11g to reserve absolutely no memory
for results caching.

RESULT_CACHE_MAX_RESULT. This parameter tells Oracle 11g how much of the result cache
should be allowed for any individual query. Its default value of 5% of the entire result cache should
usually be sufficient, but it can also be set between 0% and 100%.

RESULT_CACHE_REMOTE_EXPIRATION. If a query depends on a remote database, then this


parameter determines the number of minutes for which a result set should be retained. The default
value of zero (0) minutes serves as a reminder that any changes to a remote database table can’t
be detected at the local database, and therefore stale result sets might remain for an unduly long
period of time. This parameter can be set globally (ALTER SYSTEM) or on a per-session basis (ALTER
SESSION).

Automate Result Caching

The alternative result_cache_mode to MANUAL is FORCE. This can be session or system specific and
in this mode Oracle will attempt to set or use cached query results when it can, unless we use the
NO_RESULT_CACHE hint. We will see an example of this mode below. We will set the mode to
FORCE at a session level, then repeat our previous SQL example minus the RESULT_CACHE hint.
First we set the result_cache_mode as follows.
Dynamic Result Cache Views

So far we have seen the effects of caching with the two modes of the query result cache. We will
now look a little deeper into what happens with the query cache and what information Oracle
exposes about it. We can search the data dictionary for the result cache dynamic views, as follows.

SQL> SELECT view_name

2 FROM dba_views

3 WHERE view_name LIKE 'V_$RESULT_CACHE%';

VIEW_NAME

------------------------------
V_$RESULT_CACHE_DEPENDENCY

V_$RESULT_CACHE_MEMORY

V_$RESULT_CACHE_OBJECTS

V_$RESULT_CACHE_STATISTICS

4 rows selected.

Oracle provides four dynamic views. We will have a brief look at these below (refer to the online
documentation for more details: a link is provided at the end of this article). We will start with
V$RESULT_CACHE_OBJECTS, which exposes the most information about our cached query results.

SQL> SELECT name

2 , type

3 , cache_id
4 , row_count
5 FROM v$result_cache_objects

6 ORDER BY

7 creation_timestamp;

NAME TYPE CACHE_ID ROW_COUNT

------------------------------ ---------- -------------------------- ----------

SH.PRODUCTS Dependency SH.PRODUCTS 0


SH.SALES Dependency SH.SALES 0

SELECT /*+ RESULT_CACHE */ Result 091zc7mvn8ums36mbd2gqac4h0 71

SELECT p.prod_name Result 12scakxrxks3p73w5nxr69wn3j 71

SELECT DECODE('A','A','1','2' Result 0y8dgk314f9f8bz05qsrrny8u8 1

5 rows selected.

We can see two types of information in this view: dependencies and results. We will discuss
dependencies later, but the results' names clearly align with the queries we have run so far (the
SUM and MAX aggregate sales queries). The last query in the output is executed by SQL*Plus.
Remember from earlier that we executed two SQL statements (equivalent except for the
RESULT_CACHE hint) and note the CACHE_ID values. There is only one entry for the two statements
due to the fact that they shared a result set and hashed to the same CACHE_ID.

We can also look at the result cache statistics for a high-level overview of how it is being used, as
follows.
Result Cache Dependencies

Each query result is dependent on one or more tables (i.e. the source tables for the query). We can
get information on which objects a query is dependent on in a number of places. The
V$RESULT_CACHE_DEPENDENCY view summarises the dependencies for each entry in the result
cache. We saw the dependencies parameter in the Result Cache report from DBMS_XPLAN.DISPLAY
which listed the tables involved in our sample aggregate queries. We also saw entries in the
V$RESULT_CACHE_OBJECTS view data with a type of "Dependency". We can put these together to
summarise the dependencies as follows.

SQL> SELECT ro.id

2 , ro.name
3 , wm_concat(do.object_name) AS object_names
4 FROM v$result_cache_objects ro

5 LEFT OUTER JOIN

6 v$result_cache_dependency rd
7 ON (ro.id = rd.result_id)

8 LEFT OUTER JOIN

9 dba_objects do
10 ON (rd.object_no = do.object_id)
11 WHERE ro.type = 'Result'

12 GROUP BY

13 ro.id

14 , ro.name;

ID NAME OBJECT_NAMES

---------- -------------------------------------------------- ----------------

2 SELECT /*+ RESULT_CACHE */ SALES,PRODUCTS

p.prod_name

, SUM(s.amount_sold) AS total_revenue

, SUM(s.quantity_sold) AS total_

6 SELECT DECODE('A','A','1','2') FROM DUAL

7 SELECT p.prod_name SALES,PRODUCTS

, MAX(s.quantity_sold) AS max_sales

FROM sales s
, products p

WHERE s.prod_id = p.prod_id

GROUP

3 rows selected.

Dependencies are necessary to protect the integrity of the query results in the cache. If the data in
any of the dependant tables is modified, Oracle will invalidate the result cache entry and will not use
it until it is refreshed by a repeat of the original SQL. This behaviour cannot be circumvented, even if
we are prepared to tolerate inconsistent results.

We can demonstrate result cache invalidation very easily. We will perform a "no-change" update to a
single row of PRODUCTS and commit the transaction, as follows.
SQL> UPDATE products

2 SET prod_name = prod_name

3 WHERE ROWNUM = 1;

1 row updated.

SQL> COMMIT;

Commit complete.

We will now repeat one of our cached aggregation queries and measure the workload using Autotrace.

SQL> set autotrace traceonly statistics

SQL> SELECT p.prod_name

2 , MAX(s.quantity_sold) AS max_sales

3 FROM sales s

4 , products p
5 WHERE s.prod_id = p.prod_id

6 GROUP BY

7 p.prod_name;

71 rows selected.

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

1731 consistent gets

0 physical reads

0 redo size

2687 bytes sent via SQL*Net to client

416 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

71 rows processed

Oracle will not attempt to understand the nature of the modification to the dependant objects. Even
with a no-change update, the cached result entry was invalidated and the subsequent repeat of the
source SQL caused the data to be generated again. The V$RESULT_CACHE_OBJECTS view provides
some statistics on this, as follows.

SQL> SELECT id

2 , name
3 , type

4 , invalidations

5 , status
6 FROM v$result_cache_objects

7 ORDER BY

8 id;

ID NAME TYPE INVALIDATIONS STATUS

---- ------------------------------- ---------- ------------- ---------

1 SH.PRODUCTS Dependency 1 Published


0 SH.SALES Dependency 0 Published

6 SELECT DECODE('A','A','1','2') Result 0 Published

2 SELECT /*+ RESULT_CACHE */ Result 0 Invalid

7 SELECT p.prod_name Result 0 Invalid


10 SELECT p.prod_name Result 0 Published

6 rows selected.

We can see that the invalidation occurred at two levels. First, the INVALIDATIONS column details the
number of times that modifications to an underlying table have caused an invalidation. Second, the
STATUS column shows us which results have been invalidated by the same action. When we updated
the PRODUCTS table, we invalidated the results from our previous queries (IDs 2 and 7). We then
repeated one of the original queries, for which Oracle created a new set of results in the cache (ID 10).

Cache Find Count

If we are caching query results, we might be interested to know how often they are used. The
V$RESULT_CACHE_STATISTICS view provides a "Find Count" statistic, but this is cache-wide so we
can't limit it to a particular query. In the following example, we will capture the current Find Count and
then run a SQL statement in a PL/SQL loop 100 times.

SQL> SELECT value

2 FROM v$result_cache_statistics

3 WHERE name = 'Find Count';

VALUE

---------------

1 row selected.

SQL> DECLARE

2 n PLS_INTEGER;
3 BEGIN

4 FOR i IN 1 .. 100 LOOP

5 SELECT /*+ RESULT_CACHE */ COUNT(*) INTO n FROM channels;

6 END LOOP;

7 END;

8 /

PL/SQL procedure successfully completed.


We will now measure the Find Count again, as follows.

SQL> SELECT value

2 FROM v$result_cache_statistics

3 WHERE name = 'Find Count';

VALUE

---------------
105

1 row selected.

This increased by 99, which is to be expected. We executed our SQL statement 100 times. The first
execution cached the results and the 99 remaining executions used them. Needless to say, this was a
single-user test system. We can confirm that we added the SQL results to the cache as follows.

SQL> SELECT name

2 , type

3 , row_count
4 FROM v$result_cache_objects

5 ORDER BY

6 creation_timestamp;

NAME TYPE ROW_COUNT

-------------------------------------------------- ---------- ----------

SELECT /*+ RESULT_CACHE */ Result 71

SH.PRODUCTS Dependency 0

SH.SALES Dependency 0

SELECT p.prod_name Result 71


SELECT DECODE('A','A','1','2') FROM DUAL Result 1

SELECT p.prod_name Result 71

SH.CHANNELS Dependency 0

SELECT /*+ RESULT_CACHE */ COUNT(*) FROM CHANNELS Result 1

8 rows selected.

Parameterized Caching

In our previous example, we executed a single SQL statement 100 times and saw 1 cache entry. A far
more common scenario is to have single-row lookups based on a primary key derived from another
cursor (this is not particularly efficient, but is still extremely common). The query result cache handles
this scenario by recognising the different bind variables and caching each result set independently. The
bind variables act as parameters to the result cache lookup and are listed in the Result Cache report
from DBMS_XPLAN. If a bind variable is repeated, the cached results will be used.

We will demonstrate this behaviour below. We will set the result_cache_mode to FORCE for
convenience. We will choose 4 products and lookup each one 10 times. The lookup will use bind
variables.

SQL> ALTER SESSION SET result_cache_mode = FORCE;

Session altered.
SQL> DECLARE

2
3 TYPE id_ntt IS TABLE OF products.prod_id%TYPE;

4 nt_ids id_ntt := id_ntt(40,41,42,43);

5
6 v_name products.prod_name%TYPE;

7
8 BEGIN

9 FOR i IN 1 .. 10 LOOP

10 FOR ii IN 1 .. nt_ids.COUNT LOOP

11
12 SELECT prod_name INTO v_name

13 FROM products

14 WHERE prod_id = nt_ids(ii);

15
16 END LOOP;

17 END LOOP;

18 END;

19 /

PL/SQL procedure successfully completed.

According to what we now know about the result cache mechanism, we ran 4 different SQL statements
above (the same SQL statement with 4 different inputs). We will query V$RESULT_CACHE_OBJECTS to
verify this, as follows.

SQL> SELECT name

2 , type

3 , row_count
4 FROM v$result_cache_objects

5 ORDER BY

6 creation_timestamp;

NAME TYPE ROW_COUNT

------------------------------------------------------- ---------- ----------

SELECT /*+ RESULT_CACHE */ Result 71

<< ...snip... >>

SH.CHANNELS Dependency 0

SELECT /*+ RESULT_CACHE */ COUNT(*) FROM CHANNELS Result 1

SELECT PROD_NAME FROM PRODUCTS WHERE PROD_ID = :B1 Result 1


SELECT PROD_NAME FROM PRODUCTS WHERE PROD_ID = :B1 Result 1

SELECT PROD_NAME FROM PRODUCTS WHERE PROD_ID = :B1 Result 1

SELECT PROD_NAME FROM PRODUCTS WHERE PROD_ID = :B1 Result 1

12 rows selected.
We can see that the results for the same SQL text was added to the cache 4 times, as expected. The
bind variable inputs are additional parameters to the cache lookup. Each result set was added on the
first execution of each cursor and the cache was "hit" 9 times for each cursor. Similar logic is
commonly used by developers in associative array caching; a colleague of mine calls this "on-
demand caching" (i.e. rather than cache entire lookup tables, only cache a lookup record when it is
actually requested).

Remember that the result_cache_max_result parameter specifies that the largest cached resultset
possible is n% of the total cache memory. While this protects us from filling the cache with the
results of a single SQL statement, it doesn't stop us from filling the cache with parameterised
cursors like those we saw above. In the following example, we will lookup every customer in the
CUSTOMERS table twice. Again, we will be in FORCE result_cache_mode for convenience. Note that
there are 55,500 records in the SH.CUSTOMERS demo table.

SQL> ALTER SESSION SET result_cache_mode = FORCE;

Session altered.

SQL> DECLARE

2 v_first_name customers.cust_first_name%TYPE;

3 BEGIN

4 FOR i IN 1 .. 2 LOOP

5 FOR r IN (SELECT cust_id FROM customers) LOOP

6 SELECT cust_first_name INTO v_first_name

7 FROM customers

8 WHERE cust_id = r.cust_id;


9 END LOOP;

10 END LOOP;

11 END;

12 /

PL/SQL procedure successfully completed.

We will examine the cache entries below. Based on what we know about result cache behaviour, we
can expect a large number of single-row result sets, so we will try to aggregate these. We will query
the minimum and maximum names in V$RESULT_CACHE_OBJECTS, together with a count of the
entries, as follows.

SQL> SELECT MIN(name) AS min_name

2 , MAX(name) AS max_name

3 , COUNT(*) AS cache_entries

4 FROM v$result_cache_objects
5 WHERE type = 'Result';

MIN_NAME MAX_NAME CACHE_ENTRIES

------------------------------ ------------------------------ -------------


SELECT CUST_FIRST_NAME FROM CU SELECT CUST_FIRST_NAME FROM CU 2035

STOMERS WHERE CUST_ID = :B1 STOMERS WHERE CUST_ID = :B1

1 row selected.
We added 2,035 customer lookups (out of a possible 55,500) to the cache. In fact, we completely
flushed our previous results from the cache. We should therefore be aware of the potential for single
lookups, particularly in PL/SQL programs, to "hog" the cache. If we query
V$RESULT_CACHE_STATISTICS, we will see that the "Create Count Success" statistic should be
quite high.

SQL> SELECT *

2 FROM v$result_cache_statistics;

ID NAME VALUE

---------- ------------------------------- ---------------

1 Block Size (Bytes) 1024


2 Block Count Maximum 2048

3 Block Count Current 2048

4 Result Size Maximum (Blocks) 102

5 Create Count Success 111011

6 Create Count Failure 0

7 Find Count 141

8 Invalidation Count 2

9 Delete Count Invalid 4

10 Delete Count Valid 108972

10 rows selected.

We have added over 111,000 resultsets to the cache, mostly as a result of the previous example.
The loop through 55,500 customers would have continually replaced the existing cache entries (we
only had room in the cache for approximately 4% of the total resultsets being processed in the
PL/SQL).

Flashback Query Results

The query result cache supports flashback queries. Most readers will be aware of flashback queries
(an overview is available here). An SCN or timestamp is supplied to a flashback query using the AS
OF extension to the table(s) in the FROM clause. This supplied point-in-time is treated by Oracle as
a parameter to the query result cache.

To demonstrate this, we will run a simple flashback query twice. We will use Autotrace to
demonstrate the result cache behaviour. We will begin by setting the result_cache_mode to FORCE
for convenience.

SQL> ALTER SESSION SET result_cache_mode = FORCE;

Session altered.

We will setup a bind variable for our timestamp and execute a simple flashback query, as follows.

SQL> exec :ts := TO_CHAR(TRUNC(SYSDATE,'HH'),'YYYYMMDDHH24MISS');

PL/SQL procedure successfully completed.

SQL> set autotrace traceonly

SQL> SELECT MIN(prod_id)

2 FROM products AS OF TIMESTAMP TO_TIMESTAMP(:ts,'YYYYMMDDHH24MISS');


1 row selected.

Execution Plan

----------------------------------------------------------

Plan hash value: 1489483397

--------------------------------------------------------------------------- ... --

| Id | Operation | Name | Rows | ... |

--------------------------------------------------------------------------- ... --
| 0 | SELECT STATEMENT | | 1 | ... |
| 1 | RESULT CACHE | 4vff36vw5vmn32gftq4a5qfpxh | | ... |

| 2 | SORT AGGREGATE | | 1 | ... |

| 3 | INDEX FULL SCAN (MIN/MAX)| PRODUCTS_PK | 72 | ... |

--------------------------------------------------------------------------- ... --

Result Cache Information (identified by operation id):

------------------------------------------------------

1 - column-count=1; attributes=(single-row); parameters=(:TS); name="SELECT MIN(prod_id)

FROM products AS OF TIMESTAMP TO_TIMESTAMP(:ts,'YYYYMMDDHH24MISS')"

Statistics

----------------------------------------------------------

3 recursive calls
0 db block gets

73 consistent gets

0 physical reads

0 redo size

422 bytes sent via SQL*Net to client

416 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed

The Result Cache Information report provided by DBMS_XPLAN includes the parameters that Oracle
used for this query; in this case the TS bind variable. The execution plan confirms that Oracle will
cache the results of this flashback query. We will run the same query a second time to see if the
results are re-used, as follows.

SQL> SELECT MIN(prod_id)

2 FROM products AS OF TIMESTAMP TO_TIMESTAMP(:ts,'YYYYMMDDHH24MISS');

1 row selected.
<< ...plan removed... >>

Statistics

---------------------------------------

Creating SQL Query Result Caches: A Brief Demonstration

For a practical demonstration of how to use SQL Query Results Caching features in MANUAL mode, I’ve
provided the code attached here.

Result Cache Features.txt

 I first purged the results cache using DBMS_RESULT_CACHE.PURGE (see next section for more
details), activated MANUAL results caching, and then sized the results cache relatively small
at only 1MB.

 I then issued a SQL query to capture a summary-level presentation of total and average
promotion costs from the contents of the Sales History (SH) schema’s PROMOTIONS table.
The resulting row set that’s captured contains less than 10 rows captured from over 500 rows
in that source table, so it’s a relatively good candidate for SQL query results caching.

 I then issued an EXPLAIN PLAN against the original query, including the +RESULT_CACHE hint so
that I could determine if the result cache just created would be utilized by future queries. I
also created a report that shows in detail how the result cache’s memory has been utilized.
Here’s a sample of this output:

Result Cache Memory Report


[Parameters]
Block Size = 1K bytes
Maximum Cache Size = 1M bytes (1K blocks)
Maximum Result Size = 10K bytes (10 blocks)
[Memory]
Total Memory = 103528 bytes [0.073% of the Shared Pool]
... Fixed Memory = 5132 bytes [0.004% of the Shared Pool]
....... Cache Mgr = 108 bytes
....... Memory Mgr = 124 bytes
....... Bloom Fltr = 2K bytes
....... State Objs = 2852 bytes
... Dynamic Memory = 98396 bytes [0.069% of the Shared Pool]
....... Overhead = 65628 bytes
........... Hash Table = 32K bytes (4K buckets)
........... Chunk Ptrs = 12K bytes (3K slots)
........... Chunk Maps = 12K bytes
........... Miscellaneous = 8284 bytes
....... Cache Memory = 32K bytes (32 blocks)
........... Unused Memory = 30 blocks
........... Used Memory = 2 blocks
............... Dependencies = 1 blocks (1 count)
............... Results = 1 blocks

How does setting the result cache mode to FORCE affect the current contents of the SQL Query Results
Cache? As the code shown in Listing 1.2 illustrates:

 I first activated FORCE mode for the results cache, and I then sized the results cache relatively large
at 20MB and allowed the maximum size for any individual result cache to one-half of that value
(10MB).
 Next, I issued a simple SQL query to capture the names of all Vendors from table AP.VENDORS in
the Accounts Payable (AP) test data I originally generated in my previous article series on Database
Capture and Replay. Since this query doesn’t include the +NO_RESULT_CACHE optimizer directive, the
result set will be cached immediately.

 I then issued a SQL query to capture a more complex, summary-level presentation of Accounts
Payable (AP) test data. Since the resulting row set incorporates the +NO_RESULT_CACHE optimizer
directive, the result set will not be cached at all.

 My final step is to issue an EXPLAIN PLAN against these two queries to see the impact on any future
result set that might be likewise generated. I also recreated the detailed report on the result
cache’s memory to see if anything has changed there:

Result Cache Memory Report


[Parameters]
Block Size = 1K bytes
Maximum Cache Size = 20M bytes (20K blocks)
Maximum Result Size = 10M bytes (10K blocks)
[Memory]
Total Memory = 103528 bytes [0.073% of the Shared Pool]
... Fixed Memory = 5132 bytes [0.004% of the Shared Pool]
....... Cache Mgr = 108 bytes
....... Memory Mgr = 124 bytes
....... Bloom Fltr = 2K bytes
....... State Objs = 2852 bytes
... Dynamic Memory = 98396 bytes [0.069% of the Shared Pool]
....... Overhead = 65628 bytes
........... Hash Table = 32K bytes (4K buckets)
........... Chunk Ptrs = 12K bytes (3K slots)
........... Chunk Maps = 12K bytes
........... Miscellaneous = 8284 bytes
....... Cache Memory = 32K bytes (32 blocks)
........... Unused Memory = 24 blocks
........... Used Memory = 8 blocks
............... Dependencies = 2 blocks (2 count)
............... Results = 6 blocks
................... SQL = 6 blocks (2 count)
EXPLAIN PLAN FOR
SELECT /*SQRC_1.2*/
vendor_id
,name
FROM ap.vendors
;
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE',NULL));

----------------------------------------------------------------------------------------------------------------------
------
Plan hash value: 2620802014
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 164 | 3772 | 3 (0)| 00:00:01 |
| 1 | RESULT CACHE | 89gqh0j9248q8d0w79w0fcwhw2 | | | | |
| 2 | TABLE ACCESS FULL| VENDORS | 164 | 3772 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Result Cache Information (identified by operation id):
------------------------------------------------------
1 - column-count=2; dependencies=(AP.VENDORS); name="SELECT /*SQRC_1.2*/
vendor_id
,name
FROM ap.vendors
SQL> EXPLAIN PLAN FOR
SELECT /*+NO_RESULT_CACHE SQRC_1.3*/
I.customer_id
,C.cust_last_name || ', ' || C.cust_first_name AS customer_fullname
,SUM(ID.extended_amt) total_detail
FROM
ap.vendors V
,ap.invoices I
,ap.invoice_items ID
,oe.customers C
,oe.product_information P
WHERE ID.invoice_id = I.invoice_id
AND I.vendor_id = V.vendor_id
AND I.customer_id = C.customer_id
AND ID.product_id = P.product_id
AND I.active_ind = 'Y'
GROUP BY
I.customer_id
,C.cust_last_name || ', ' || C.cust_first_name
ORDER BY
I.customer_id
,C.cust_last_name || ', ' || C.cust_first_name
;
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE',NULL));
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Explained.
SQL> 2
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------
------
Plan hash value: 500053926
---------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
---------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 956 | 34416 | 11 (19)| 00:00:01
|
| 1 | SORT GROUP BY | | 956 | 34416 | 11 (19)| 00:00:01
|
|* 2 | HASH JOIN | | 956 | 34416 | 10 (10)| 00:00:01
|
| 3 | NESTED LOOPS | | | | |
|
| 4 | NESTED LOOPS | | 25 | 700 | 6 (0)| 00:00:01
|
| 5 | TABLE ACCESS FULL | CUSTOMERS | 319 | 6061 | 5 (0)| 00:00:01
|
|* 6 | INDEX RANGE SCAN | INVOICES_CUST_IDX | 25 | | 0 (0)| 00:00:01
|
|* 7 | TABLE ACCESS BY INDEX ROWID| INVOICES | 1 | 9 | 1 (0)| 00:00:01
|
| 8 | TABLE ACCESS FULL | INVOICE_ITEMS | 975 | 7800 | 3 (0)| 00:00:01
|
---------------------------------------------------------------------------------------------------
-
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"."INVOICE_ID"="I"."INVOICE_ID")
6 - access("I"."CUSTOMER_ID"="C"."CUSTOMER_ID")
filter("I"."CUSTOMER_ID">0)
7 - filter("I"."ACTIVE_IND"='Y')

Controlling SQL Query Result Set Caching With DBMS_RESULT_CACHE


Oracle 11g also supplies the DBMS_RESULT_CACHE package to interrogate the status of and precisely
control the contents of the SQL result cache. Here’s a brief summary of its capabilities:

Table 1-1. DBMS_RESULT_CACHE Functions and Procedures


Function / Procedure Description
STATUS Returns the current status of the Result Cache. Values
include:

ENABLED: The result cache is enabled.

DISABLED: The result cache has been disabled.

BYPASSED: The result cache is temporarily


unavailable.

SYNC: The result cache is available, but is currently


being resynchronized with other RAC nodes.
MEMORY_REPORT Lists either a summary (by default) or detailed report
of Result Cache memory usage
FLUSH Flushes the entire contents of the Result Cache
INVALIDATE Invalidates a cached result for a specific object in the
Result Cache
INVALIDATE_OBJECT Invalidates a specific Result Cache based on its Cache
ID

Listing 1.3 shows some additional examples of how to use these packaged procedures and functions.

Results Cache Metadata

Four dynamic views provide information about existing Results Cache contents, memory usage, and the
database objects on which Result Caches depend:

Table 1-2. SQL Result Cache Metadata


View Description
V$RESULT_CACHE_STATISTICS Lists the various cache settings and memory
usage statistics
V$RESULT_CACHE_MEMORY Lists all memory blocks and corresponding
statistics
V$RESULT_CACHE_OBJECTS Lists all the objects (cached results and
dependencies) along with their attributes
V$RESULT_CACHE_DEPENDENCY Lists the dependency details between the
cached results and dependencies

See Listing 1.4 for several queries I’ve created against the single-instance (V$) views for this article;
it’s a relatively simple task to expand these queries to the global resource view (GV$) for Real Application
Clusters databases. In Listing 1.5 I’ve also reproduced the results from the query against
the V$RESULT_CACHE_OBJECTS view to demonstrate what metadata it contains for cached result sets.

Вам также может понравиться