Der Result Cache ist ein - leider auf die Enterprise Edition beschränktes - Feature, das mit Version 11g eingeführt wurde. Er ist sowohl in SQL für Select-Befehle als auch in PL/SQL für Funktionsaufrufe nutzbar. Die Idee dahinter ist in beiden Fällen, dass das Ergebnis bei der ersten Ausführung gespeichert wird und bei weiteren Aufrufen der Befehl selber nicht mehr ausgeführt, sondern das Ergebnis aus dem Cache abgerufen wird. Um Tom Kyte zu zitieren: "Everyone knows the fastest way to do something is – to not do it".
Der dafür genutzte Speicherbereich - eben der Result Cache - ist Bestandteil der SGA. Um ihn nutzen zu können, muss nur seine Größe (RESULT_CACHE_MAX_SIZE ) auf einen Wert > 0 eingestellt sein.
Parameter (serverseitig)
RESULT_CACHE_MAX_SIZE
Größe des Result Cache in Bytes.
Der Default hängt von den Einstellungen für SHARED_POOL_SIZE, SGA_TARGET und MEMORY_TARGET ab
RESULT_CACHE_MAX_RESULT
Anteil am Result Cache in Prozent, der maximal für einen einzigen Befehl genutzt werden darf.
Default: 5
In einem RAC-Umfeld muss darauf geachtet werden, dass entweder auf ALLEN Instanzen RESULT_CACHE_MAX_SIZE auf einem Wert > 0 steht, oder auf keiner; sonst kann es laut Doku zu falschen Ergebnissen kommen.
SQL
In SQL gibt es zwei Wege, den Result Cache zu nutzen, entweder über den (auch auf Session-Ebene einstellbaren) Parameter RESULT_CACHE_MODE oder über den Hint RESULT_CACHE:
Steht RESULT_CACHE_MODE auf MANUAL (dem Default), dann muss die Verwendung des Result Cache explizit im Select über den Hint RESULT_CACHE angefordert werden. Steht RESULT_CACHE_MODE dagegen auf FORCE, so wird bei JEDEM Select versucht, den Result Cache zu nutzen, es sei denn, das wird explizt durch den Hint NO_RESULT_CACHE unterbunden. Zumindest eine systemweite Einstellung von RESULT_CACHE_MODE=FORCE dürfte eher nicht sinnvoll sein, da der Platz ja begrenzt ist und somit ein ständiger Turnover stattfinden würde.
Die Verwendung des Result Cache ist auch im Ausführungsplan sichtbar.
PL/SQL
Interessant ist der Result Cache vor allem im PL/SQL-Umfeld. Es kommt immer wieder vor, dass man kleine Lookup-Tabellen hat, aus denen häufig ein einzelner Wert - gekapselt in eine Funktion - ausgelesen wird. Oder man verwendet ständig die ein oder andere kleinere Hilfsfunktion (z.B. zur Formatierung oder Berechnung von Werten) für immer wieder die gleichen Werte. Musste man vor Version 11g die Performance steigern, so blieben nur Hilfskonstrukte, wie beispielsweise das Einlesen von Werten in globale Package-Variablen oder -Arrays. Der Nachteil dieses Vorgehens liegt auf der Hand: Package-Variablen behalten ihren Wert nur innerhalb der Session, und spätestens bei Web-Applikationen ist dieser Ansatz daher obsolet.
In Version 11g kann nun das Ergebnis einer Funktion im Result Cache abgelegt und wieder abgerufen werden, egal, ob es sich um eine Package-Funktion oder um eine Standalone Funktion handelt. Es muss nur in der Funktionsdeklaration angegeben werden (bei Packages sowohl im Header als auch im Body). Und da der Result Cache Bestandteil der SGA ist, nicht der PGA, ist er auch Session-übergreifend nutzbar!
Beispiel:
SET SERVEROUTPUT ON
SET ECHO ON
CREATE FUNCTION cache_it (
p_empno IN NUMBER
)
RETURN VARCHAR2
RESULT_CACHE
-- RELIES_ON(SCOTT.emp) -- nur in Version 11.1 nötig
IS
v_name SCOTT.EMP.ename
%TYPE;BEGIN
SELECT ename
INTO v_name
FROM SCOTT.emp
WHERE empno = p_empno;
RETURN v_name;
END;
/
DECLARE
v_begin NUMBER;
v_end NUMBER;
v_ret VARCHAR2 (100);
TYPE t IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
v_arr t;
BEGIN
SELECT empno
BULK COLLECT INTO v_arr
FROM scott.emp;
v_begin := DBMS_UTILITY.get_cpu_time;
FOR i IN 1 .. 1000
LOOP
FOR j IN 1 .. v_arr.COUNT
LOOP
v_ret := cache_it (v_arr (j));
END LOOP;
END LOOP;
v_end := DBMS_UTILITY.get_cpu_time;
DBMS_OUTPUT.put_line ('Zeit: ' || (v_end - v_begin));
END;
/
-- Verwendung des Result Cache (systemweit) deaktivieren
EXEC DBMS_RESULT_CACHE.bypass(TRUE)
/
-- Verwendung des Result Cache (systemweit) wieder aktivieren
EXEC DBMS_RESULT_CACHE.bypass(FALSE)
/
DROP FUNCTION cache_it;
Ausgabe:
Zeit: 5 -- mit Result Cache
Zeit: 54 -- ohne Result Cache
Zeit: 5 -- mit Result Cache
Das Package DBMS_RESULT_CACHE bietet einige Verwaltungsmöglichkeiten an, wie z.B. Statusabfrage, vorübergehendes Ausschalten (s.o.), explizite Invalidierung oder, wie unten gezeigt, Erzeugung eines Reports zum Speicherverbrauch:
SET SERVEROUTPUT ON
EXEC DBMS_RESULT_CACHE.MEMORY_REPORT(detailed => TRUE)
INVALIDIERUNGEN
Im obigen Beispiel wird innerhalb der Funktion auf eine Tabelle zugegriffen. Was passiert, wenn diese Tabelle nun von einer anderen Session geändert wird? In diesem Fall werden beim Commit alle davon abhängigen Ergebnisse automatisch invalidiert und bei Bedarf neu ermittelt, so dass die Funktion nie falsche Ergebnisse liefert. Wird die Tabelle innerhalb der EIGENEN Session geändert, so wird für abhängige Funktionen der Result Cache grundsätzlich so lange nicht mehr verwendet, bis die Transaktion beendet ist. Die Lesekonsistenz ist also immer gewährleistet.
In Version 11.1 musste man mit der Klausel RELIES_ON noch explizit angeben, von welchen Tabellen der Rückgabewert der Funktion abhängig ist. Hat man die Klausel vergessen, so führte das nicht zu einem Kompilierungsfehler, sondern ggf. zu falschen Resultaten. In Version 11.2 werden solche Abhängigkeiten automatisch erkannt.
EINSCHRÄNKUNGEN
Natürlich gibt es auch ein paar Einschränkungen bei der Verwendung, die aber normalerweise schon der Compiler nicht zulässt. Dazu gehören
- OUT-Parameter sind nicht erlaubt (gespeichert werden die Werte für IN-Parameter und Ergebnisse)
- Zulässig als IN-Parameter und Returnwert sind im wesentlichen nur skalare Datentpyen
- Nicht zulässig bei Invoker Rights
Beispiel für Fehlermeldungen:
PLS-00999: Implementierungseinschränkung (kann temporär sein) RESULT_CACHE is disallowed on subprograms in Invoker-Rights modules
Ab Version 12c fällt die letztgenannte Einschränkung weg; dann kann der Result Cache auch bei Invoker Rights genutzt werden.
Eine genaue Auflistung aller Einschränkungen finden Sie in der PL/SQL Language Reference.
V$-VIEWS ZUM RESULT CACHE:
- V$RESULT_CACHE_OBJECTS
- V$RESULT_CACHE_MEMORY
- V$RESULT_CACHE_DEPENDENCY
Viele weitere interessante Informationen hierzu erfahren Sie in unseren Schulungen.