PL/SQL Function Result Cache
When a PL/SQL function has the RESULT_CACHE
option, its results are cached so sessions can reuse these results when available.
Oracle Database automatically detects all data sources (tables and views) that are queried while a result-cached function is running. If changes to any of these data sources are committed, the cached result becomes invalid across all instances. The best candidates for result-caching are functions that are invoked frequently but depend on information that changes infrequently or never.
A result object is the result of a query or result cached function execution. A temp object is the result of a query or result-cached function execution that exceeds the limit set by the multiplication of the RESULT_CACHE_MAX_SIZE
by RESULT_CACHE_MAX_RESULT
parameters. Temp objects are temporary segments stored in the temporary tablespace defined for the SYS user.
You can view the result and temp objects together by joining the V$RESULT_CACHE_OBJECTS
using the type Temp for temp object and type Result for result objects.
SELECT rc1.NAME, rc2.STATUS, rc3.STATUS, rc2.BLOCK_COUNT FROM V$RESULT_CACHE_OBJECTS rc1, V$RESULT_CACHE_OBJECTS rc2 WHERE rc1.TYPE = 'Result' AND rc2.TYPE = 'Temp' AND rc1.CACHE_KEY = rc2.CACHE_KEY;
The RESULT_CACHE_MAX_TEMP_SIZE
parameter sets the maximum amount of temporary tablespace that the result cache can consume in a PDB.
The result cache usage is optimized for best performance based on changes in the application workload.
Before fetching a cached result from a remote instance, the database uses heuristics to determine if it is more cost efficient to recompute the result on the local instance.
Oracle Database tracks recently used result-cached functions. Using this history, the database only caches a result-cached function and arguments pair if it has seen it x times in recent history, where x is set by the initialization parameter RESULT_CACHE_EXECUTION_THRESHOLD
. Assuming the default value of 2, the result is cached on the second execution and reused on the third execution.
You can assess the health of your result cache by running the following query. It shows the distribution of the reuse rate of cached functions. If you notice a majority of these results have a scan count of 0, consider increasing the value of the RESULT_CACHE_EXECUTION_THRESHOLD
by 1 or 2.
SELECT SCAN_COUNT, COUNT(CACHE_KEY) FROM V$RESULT_CACHE_OBJECTS WHERE NAMESPACE = 'PLSQL' GROUP BY SCAN_COUNT;
Topics
Enabling Result-Caching for a Function
To make a function result-cached, include the RESULT_CACHE
clause in the function declaration and definition. For syntax details, see "Function Declaration and Definition".
Note:
For more information about configuring and managing the database server result cache, see Oracle Database Reference and Oracle Database Performance Tuning Guide.
In Example 9-39, the package department_pkg
declares and then defines a result-cached function, get_dept_info
, which returns a record of information about a given department. The function depends on the database tables DEPARTMENTS
and EMPLOYEES
.
You invoke the function get_dept_info
as you invoke any function. For example, this invocation returns a record of information about department number 10:
department_pkg.get_dept_info(10);
This invocation returns only the name of department number 10:
department_pkg.get_dept_info(10).dept_name;
If the result for get_dept_info(10)
is in the result cache, the result is returned from the cache; otherwise, the result is computed and added to the cache. Because get_dept_info
depends on the DEPARTMENTS
and EMPLOYEES
tables, any committed change to DEPARTMENTS
or EMPLOYEES
invalidates all cached results for get_dept_info
, relieving you of programming cache invalidation logic everywhere that DEPARTMENTS
or EMPLOYEES
might change.
Example 9-39 Declaring and Defining Result-Cached Function
CREATE OR REPLACE PACKAGE department_pkg AUTHID DEFINER IS TYPE dept_info_record IS RECORD ( dept_name departments.department_name%TYPE, mgr_name employees.last_name%TYPE, dept_size PLS_INTEGER ); -- Function declaration FUNCTION get_dept_info (dept_id NUMBER) RETURN dept_info_record RESULT_CACHE; END department_pkg; / CREATE OR REPLACE PACKAGE BODY department_pkg IS -- Function definition FUNCTION get_dept_info (dept_id NUMBER) RETURN dept_info_record RESULT_CACHE IS rec dept_info_record; BEGIN SELECT department_name INTO rec.dept_name FROM departments WHERE department_id = dept_id; SELECT e.last_name INTO rec.mgr_name FROM departments d, employees e WHERE d.department_id = dept_id AND d.manager_id = e.employee_id; SELECT COUNT(*) INTO rec.dept_size FROM EMPLOYEES WHERE department_id = dept_id; RETURN rec; END get_dept_info; END department_pkg; /
Developing Applications with Result-Cached Functions
When developing an application that uses a result-cached function, make no assumptions about the number of times the body of the function will run for a given set of parameter values.
Some situations in which the body of a result-cached function runs are:
-
The first time a session on this database instance invokes the function with these parameter values is run
Note:
RESULT_CACHE_EXECUTION_THRESHOLD
specifies the number of times a function and a particular set of arguments must be seen until it is cached. The default value for that parameter is 2 and can be configured at the system level. -
When the cached result for these parameter values is invalid
When a change to any data source on which the function depends is committed, the cached result becomes invalid
-
When the cached results for these parameter values have aged out
If the system needs memory, it might discard the oldest or rarely used cached values based on PL/SQL function history tracking
- When the
DBMS_RESULT_CACHE
block list procedure is invoked to explicitly block some result caching related objects from being cached on a local instance or globally - After the
DBMS_RESULT_CACHE.FLUSH
has run and flushed all the cached results for SQL queries and all the cached results for PL/SQL functions -
When the function bypasses the cache (see "Result Cache Bypass")
Requirements for Result-Cached Functions
A result-cached PL/SQL function is safe if it always produces the same output for any input that it would produce were it not marked with RESULT_CACHE
. This safety is only guaranteed if these conditions are met:
-
When the function is run, it has no side effects.
For information about side effects, see "Subprogram Side Effects".
-
All tables that the function accesses are ordinary, non-
SYS
-owned permanent tables in the same database as the function. -
The function’s result must be determined only by the vector of input actuals together with the committed content, at the current
SCN
, of the tables that it references.
It is recommended that a result-cached function also meet these criteria:
-
It does not depend on session-specific settings.
For more information, see "Making Result-Cached Functions Handle Session-Specific Settings".
-
It does not depend on session-specific application contexts.
For more information, see "Making Result-Cached Functions Handle Session-Specific Application Contexts".
For more information, see Oracle Database Performance Tuning Guide.
Examples of Result-Cached Functions
The best candidates for result-caching are functions that are invoked frequently but depend on information that changes infrequently (as might be the case in the first example). Result-caching avoids redundant computations in recursive functions.
Examples:
Result-Cached Application Configuration Parameters
Consider an application that has configuration parameters that can be set at either the global level, the application level, or the role level. The application stores the configuration information in these tables:
-- Global Configuration Settings
DROP TABLE global_config_params;
CREATE TABLE global_config_params
(name VARCHAR2(20), -- parameter NAME
val VARCHAR2(20), -- parameter VALUE
PRIMARY KEY (name)
);
-- Application-Level Configuration Settings
CREATE TABLE app_level_config_params
(app_id VARCHAR2(20), -- application ID
name VARCHAR2(20), -- parameter NAME
val VARCHAR2(20), -- parameter VALUE
PRIMARY KEY (app_id, name)
);
-- Role-Level Configuration Settings
CREATE TABLE role_level_config_params
(role_id VARCHAR2(20), -- application (role) ID
name VARCHAR2(20), -- parameter NAME
val VARCHAR2(20), -- parameter VALUE
PRIMARY KEY (role_id, name)
);
For each configuration parameter, the role-level setting overrides the application-level setting, which overrides the global setting. To determine which setting applies to a parameter, the application defines the PL/SQL function get_value
. Given a parameter name, application ID, and role ID, get_value
returns the setting that applies to the parameter.
The function get_value
is a good candidate for result-caching if it is invoked frequently and if the configuration information changes infrequently.
Example 9-40 shows a possible definition for get_value
. Suppose that for one set of parameter values, the global setting determines the result of get_value
. While get_value
is running, the database detects that three tables are queried—role_level_config_params
, app_level_config_params
, and global_config_params
. If a change to any of these three tables is committed, the cached result for this set of parameter values is invalidated and must be recomputed.
Now suppose that, for a second set of parameter values, the role-level setting determines the result of get_value
. While get_value
is running, the database detects that only the role_level_config_params
table is queried. If a change to role_level_config_params
is committed, the cached result for the second set of parameter values is invalidated; however, committed changes to app_level_config_params
or global_config_params
do not affect the cached result.
Example 9-40 Result-Cached Function Returns Configuration Parameter Setting
CREATE OR REPLACE FUNCTION get_value
(p_param VARCHAR2,
p_app_id NUMBER,
p_role_id NUMBER
)
RETURN VARCHAR2
RESULT_CACHE
AUTHID DEFINER
IS
answer VARCHAR2(20);
BEGIN
-- Is parameter set at role level?
BEGIN
SELECT val INTO answer
FROM role_level_config_params
WHERE role_id = p_role_id
AND name = p_param;
RETURN answer; -- Found
EXCEPTION
WHEN no_data_found THEN
NULL; -- Fall through to following code
END;
-- Is parameter set at application level?
BEGIN
SELECT val INTO answer
FROM app_level_config_params
WHERE app_id = p_app_id
AND name = p_param;
RETURN answer; -- Found
EXCEPTION
WHEN no_data_found THEN
NULL; -- Fall through to following code
END;
-- Is parameter set at global level?
SELECT val INTO answer
FROM global_config_params
WHERE name = p_param;
RETURN answer;
END;
/
Result-Cached Recursive Function
A recursive function for finding the nth term of a Fibonacci series that mirrors the mathematical definition of the series might do many redundant computations. For example, to evaluate fibonacci(7)
, the function must compute fibonacci(6)
and fibonacci(5)
. To compute fibonacci(6)
, the function must compute fibonacci(5)
and fibonacci(4)
. Therefore, fibonacci(5)
and several other terms are computed redundantly. Result-caching avoids these redundant computations.
Note:
The maximum number of recursive invocations cached is 128.
CREATE OR REPLACE FUNCTION fibonacci (n NUMBER)
RETURN NUMBER
RESULT_CACHE
AUTHID DEFINER
IS
BEGIN
IF (n =0) OR (n =1) THEN
RETURN 1;
ELSE
RETURN fibonacci(n - 1) + fibonacci(n - 2);
END IF;
END;
/
Advanced Result-Cached Function Topics
Topics
Rules for a Cache Hit
Each time a result-cached function is invoked with different parameter values, those parameters and their result are stored in the cache. Subsequently, when the same function is invoked with the same parameter values (that is, when there is a cache hit), the result is retrieved from the cache, instead of being recomputed.
The rules for parameter comparison for a cache hit differ from the rules for the PL/SQL "equal to" (=) operator, as follows:
Category | Cache Hit Rules | "Equal To" Operator Rules |
---|---|---|
NULL comparison |
|
|
Non-null scalar comparison |
Non-null scalars are the same if and only if their values are identical; that is, if and only if their values have identical bit patterns on the given platform. For example, |
Non-null scalars can be equal even if their values do not have identical bit patterns on the given platform; for example, |
Result Cache Bypass
In some situations, the cache is bypassed. When the cache is bypassed:
-
The function computes the result instead of retrieving it from the cache.
-
The result that the function computes is not added to the cache.
Some examples of situations in which the cache is bypassed are:
-
The cache is unavailable to all sessions.
For example, the database administrator has disabled the use of the result cache during application patching (as in "Hot-Patching PL/SQL Units on Which Result-Cached Functions Depend").
-
A session is performing a DML statement on a table or view on which a result-cached function depends.
The session bypasses the result cache for that function until the DML statement is completed—either committed or rolled back. If the statement is rolled back, the session resumes using the cache for that function.
Cache bypass ensures that:
-
The user of each session sees their own uncommitted changes.
-
The PL/SQL function result cache has only committed changes that are visible to all sessions, so that uncommitted changes in one session are not visible to other sessions.
-
Making Result-Cached Functions Handle Session-Specific Settings
If a function depends on settings that might vary from session to session (such as NLS_DATE_FORMAT
and TIME ZONE
), make the function result-cached only if you can modify it to handle the various settings.
The function, get_hire_date
, in Example 8–39 uses the TO_CHAR
function to convert a DATE
item to a VARCHAR
item. The function get_hire_date
does not specify a format mask, so the format mask defaults to the one that NLS_DATE_FORMAT
specifies. If sessions that invoke get_hire_date
have different NLS_DATE_FORMAT
settings, cached results can have different formats. If a cached result computed by one session ages out, and another session recomputes it, the format might vary even for the same parameter value. If a session gets a cached result whose format differs from its own format, that result is probably incorrect.
Some possible solutions to this problem are:
-
Change the return type of
get_hire_date
toDATE
and have each session invoke theTO_CHAR
function. -
If a common format is acceptable to all sessions, specify a format mask, removing the dependency on
NLS_DATE_FORMAT
. For example:TO_CHAR(date_hired, 'mm/dd/yy');
-
Add a format mask parameter to
get_hire_date
. For example:CREATE OR REPLACE FUNCTION get_hire_date (emp_id NUMBER, fmt VARCHAR) RETURN VARCHAR RESULT_CACHE AUTHID DEFINER IS date_hired DATE; BEGIN SELECT hire_date INTO date_hired FROM HR.EMPLOYEES WHERE EMPLOYEE_ID = emp_id; RETURN TO_CHAR(date_hired, fmt); END; /
Example 9-41 Result-Cached Function Handles Session-Specific Settings
CREATE OR REPLACE FUNCTION get_hire_date (emp_id NUMBER) RETURN VARCHAR RESULT_CACHE AUTHID DEFINER IS date_hired DATE; BEGIN SELECT hire_date INTO date_hired FROM HR.EMPLOYEES WHERE EMPLOYEE_ID = emp_id; RETURN TO_CHAR(date_hired); END; /
Making Result-Cached Functions Handle Session-Specific Application Contexts
An application context, which can be either global or session-specific, is a set of attributes and their values. A PL/SQL function depends on session-specific application contexts if it does one or more of the following:
-
Directly invokes the SQL function
SYS_CONTEXT
, which returns the value of a specified attribute in a specified context -
Indirectly invokes
SYS_CONTEXT
by using Virtual Private Database (VPD) mechanisms for fine-grained security(For information about VPD, see Oracle Database Security Guide.)
The PL/SQL function result-caching feature does not automatically handle dependence on session-specific application contexts. If you must cache the results of a function that depends on session-specific application contexts, you must pass the application context to the function as a parameter. You can give the parameter a default value, so that not every user must specify it.
In Example 9-42, assume that a table, config_tab
, has a VPD policy that translates this query:
SELECT value FROM config_tab WHERE name = param_name;
To this query:
SELECT value FROM config_tab
WHERE name = param_name
AND app_id = SYS_CONTEXT('Config', 'App_ID');
Example 9-42 Result-Cached Function Handles Session-Specific Application Context
CREATE OR REPLACE FUNCTION get_param_value ( param_name VARCHAR, appctx VARCHAR DEFAULT SYS_CONTEXT('Config', 'App_ID') ) RETURN VARCHAR RESULT_CACHE AUTHID DEFINER IS rec VARCHAR(2000); BEGIN SELECT val INTO rec FROM config_tab WHERE name = param_name; RETURN rec; END; /
Choosing Result-Caching Granularity
PL/SQL provides the function result cache, but you choose the caching granularity. To understand the concept of granularity, consider the Product_Descriptions
table in the Order Entry (OE
) sample schema:
NAME NULL? TYPE ---------------------- -------- --------------- PRODUCT_ID NOT NULL NUMBER(6) LANGUAGE_ID NOT NULL VARCHAR2(3) TRANSLATED_NAME NOT NULL NVARCHAR2(50) TRANSLATED_DESCRIPTION NOT NULL NVARCHAR2(2000)
The table has the name and description of each product in several languages. The unique key for each row is PRODUCT_ID,LANGUAGE_ID
.
Suppose that you must define a function that takes a PRODUCT_ID
and a LANGUAGE_ID
and returns the associated TRANSLATED_NAME
. You also want to cache the translated names. Some of the granularity choices for caching the names are:
-
One name at a time (finer granularity)
-
One language at a time (coarser granularity)
Table 9-4 Finer and Coarser Caching Granularity
Granularity | Benefits |
---|---|
Finer |
Each function result corresponds to one logical result. Stores only data that is needed at least once. Each data item ages out individually. Does not allow bulk loading optimizations. |
Coarser |
Each function result contains many logical subresults. Might store data that is never used. One aged-out data item ages out the whole set. Allows bulk loading optimizations. |
In Example 9-43 and Example 9-44, the function productName
takes a PRODUCT_ID
and a LANGUAGE_ID
and returns the associated TRANSLATED_NAME
. Each version of productName
caches translated names, but at a different granularity.
In Example 9-43, get_product_name_1
is a result-cached function. Whenever get_product_name_1
is invoked with a different PRODUCT_ID
and LANGUAGE_ID
, it caches the associated TRANSLATED_NAME
. Each invocation of get_product_name_1
adds at most one TRANSLATED_NAME
to the cache.
In Example 9-44, get_product_name_2
defines a result-cached function, all_product_names
. Whenever get_product_name_2
invokes all_product_names
with a different LANGUAGE_ID
, all_product_names
caches every TRANSLATED_NAME
associated with that LANGUAGE_ID
. Each invocation of all_product_names
adds every TRANSLATED_NAME
of at most one LANGUAGE_ID
to the cache.
Example 9-43 Caching One Name at a Time (Finer Granularity)
CREATE OR REPLACE FUNCTION get_product_name_1 (
prod_id NUMBER,
lang_id VARCHAR2
)
RETURN NVARCHAR2
RESULT_CACHE
AUTHID DEFINER
IS
result_ VARCHAR2(50);
BEGIN
SELECT translated_name INTO result_
FROM OE.Product_Descriptions
WHERE PRODUCT_ID = prod_id
AND LANGUAGE_ID = lang_id;
RETURN result_;
END;
/
Example 9-44 Caching Translated Names One Language at a Time (Coarser Granularity)
CREATE OR REPLACE FUNCTION get_product_name_2 (
prod_id NUMBER,
lang_id VARCHAR2
)
RETURN NVARCHAR2
AUTHID DEFINER
IS
TYPE product_names IS TABLE OF NVARCHAR2(50) INDEX BY PLS_INTEGER;
FUNCTION all_product_names (lang_id VARCHAR2)
RETURN product_names
RESULT_CACHE
IS
all_names product_names;
BEGIN
FOR c IN (SELECT * FROM OE.Product_Descriptions
WHERE LANGUAGE_ID = lang_id) LOOP
all_names(c.PRODUCT_ID) := c.TRANSLATED_NAME;
END LOOP;
RETURN all_names;
END;
BEGIN
RETURN all_product_names(lang_id)(prod_id);
END;
/
Result Caches in Oracle RAC Environment
Cached results are stored in the system global area (SGA). In an Oracle RAC environment, each database instance manages its own local function result cache. However, the contents of the local result cache are accessible to sessions attached to other Oracle RAC instances. If a required result is missing from the result cache of the local instance, the result might be retrieved from the local cache of another instance, instead of being locally computed. The access pattern and workload of an instance determine the set of results in its local cache; therefore, the local caches of different instances can have different sets of results.
Before fetching a cached result from a remote instance, the database uses heuristics to determine if it is more cost efficient to recompute the result on the local instance. You can monitor the use of this functionality by querying the V$RESULT_CACHE_OBJECTS
and V$RESULT_CACHE_STATISTICS
views. The V$RESULT_CACHE_OBJECTS
has a value ’Yes’ in the GLOBAL
column if the object has been fetched from the result cache of another instance. A value of ’No’ means that the result was locally recomputed, either because it was not available remotely, or because the system has decided it is more efficient to do so instead of fetching it remotely. The statistics ’Global Prune Count’
in the V$RESULT_CACHE_STATISTICS
view shows the number of times the decision was made not to fetch from a remote instance. ’Global Prune By Self Count’
shows the number of times an instance asked to provide a local result and has decided it is more efficient for the requesting instance to compute the result locally. Finally, ’Global Load Rate’
shows the computed rate - in bytes per 10 milliseconds - of fetching results from result cache of other instances. All these statistics only apply to global result caches in a RAC environment.
Although each database instance might have its own set of cached results, the mechanisms for handling invalid results are Oracle RAC environment-wide. For example, consider a result cache of item prices that are computed from data in database tables. If any of these database tables is updated in a way that affects the price of an item, the cached price of that item is invalidated in every database instance in the Oracle RAC environment.
See Also:
Real Application Clusters
Administration and Deployment Guide for more information about
setting RESULT_CACHE_MAX_SIZE
parameter and other initialization
parameters in an Oracle RAC database
Result Cache Management
The PL/SQL function result cache shares its administrative and manageability infrastructure with the Result Cache.
You can administer the shared pool area part that is used by the SQL result cache and the PL/SQL function result cache using the DBMS_RESULT_CACHE
subprograms. Using the DBMS_RESULT_CACHE.BLACKLIST_ADD
procedure, you can add a query or a PL/SQL function to a blocklist to stop caching the results. No matter the bind variables or arguments used, there will be no objects generated for it. The result cache row source may still appear in the explain plan, but at runtime it will be a no-op. You can solve a result cache issue if you diagnose by looking for a case when ten of thousands set of cached results unique arguments is run for a function. Depending on the workload, the overhead of managing these cached results might offset the benefits of caching the results. The performance views gives you insight on this special cases.
You can run a query to identify problematic queries or functions. The cache_id
is the result cache identifier of a SQL cursor or PL/SQL function. This query counts how many unique result cache objects were made for each cache id. A unique object is created for every run of a query or function with unique bind variables or arguments.
SELECT cache_id, COUNT(cache_key) AS uniq_args
FROM GV$RESULT_CACHE_OBJECTS
WHERE type = 'Result'
GROUP BY cache_id
ORDER BY uniq_args DESC;
When a dependent object is frequently updated by a workload, it can adversely impact the performance benefits of using result cache. For example, when a large transaction is committed and is affecting already cached results, messages are sent to invalidate these cached results to prevent wrong results. The first hint that this bottleneck is happening is the observation of high waits with CHANNEL = 'Result Cache: Channel'
in the GV$CHANNEL_WAITS
view. You can run a query to check the culprit and take appropriate action such as adding the object to the blocklist. An object with an extremely high number of invalidations can be diagnosed using this query.
SELECT object_no, SUM(invalidations) AS num_invals
FROM GV$RESULT_CACHE_OBJECTS
WHERE type = 'Dependency'
GROUP BY object_no
ORDER BY num_invals DESC;
See Also:
-
Oracle Database PL/SQL Packages and Types Reference for more information about the
DBMS_RESULT_CACHE
package
Dynamic performance views provide information to monitor the server and client result caches.
See Also:
-
Oracle Database Performance Tuning Guide for more information about configuring the result cache
-
Oracle Database Reference for more information about
V$RESULT_CACHE_STATISTICS
-
Oracle Database Reference for more information about
V$RESULT_CACHE_MEMORY
-
Oracle Database Reference for more information about
V$RESULT_CACHE_OBJECTS
-
Oracle Database Reference for more information about
V$RESULT_CACHE_DEPENDENCY
The database administrator manages the server result cache by specifying the result cache initialization parameters.
See Also:
- Oracle Database Concepts for more information about the Server Result Cache Infrastructure
Hot-Patching PL/SQL Units on Which Result-Cached Functions Depend
When you hot-patch a PL/SQL unit on which a result-cached function depends (directly or indirectly), the cached results associated with the result-cached function might not be automatically flushed in all cases.
For example, suppose that the result-cached function P1
.foo()
depends on the package subprogram P2
.bar()
. If a new version of the body of package P2
is loaded, the cached results associated with P1
.foo()
are not automatically flushed.
Therefore, this is the recommended procedure for hot-patching a PL/SQL unit:
Note:
To follow these steps, you must have the EXECUTE
privilege on the package DBMS_RESULT_CACHE
.