Oracle Datenbank-Tuning - Der Buffer Cache

08.02.2007 von Lutz Fröhlich, Carsten Czarski und Klaus Maier
Der Buffer Cache von Oracle speichert Tabellen, Indizes und andere relevante Daten zwischen. Je besser er an die Gegebenheiten angepasst ist, desto mehr Leistung holen Sie aus Ihrer Datenbank.

Im vorigen zweiten Teil unserer Serie zum Datenbank-Tuning haben wir Ihnen den Shared Pool vorgestellte, der als Cache für eine Reihe von Informationen dient. Unter anderem haben Sie erfahren, welche Statistik-Views Ihnen zur Verfügung stehen und wie Sie die Speichergröße optimal nutzen.

In diesem Teil stellen wir Ihnen den Buffer Cache vor, in dem Oracle Tabellen, Indizies und andere relevante Daten zwischenspeichert. Wir erklären, wie der Cache arbeitet, wie Sie seine Performance messen und wie Sie mehr Leistung aus dem System herauspressen.

Unsere neue Serie zu Oracle 10g basiert auf Kapitel 18 des Standardwerks „Oracle 10g“ von Fröhlich, Czarski und Maier aus dem Verlag Markt + Technik. Sie können dieses über 870 Seiten starke Buch auch in unserem Buchshop bestellen oder als eBook herunterladen.
Serie: Oracle Datenbank-Tuning

Teil 1: Grundlagen und grundsätzliche Überlegungen

Teil 2: Shared Pool konfigurieren

Teil 3: Buffer Cache optimieren

Teil 4: Der Redo-Logbuffer

Teil 5: I/O optimieren

Teil 6: Tuning automatisieren

Optimierung des Buffer Cache

Im Buffer Cache werden Datenblöcke von Segmenten gespeichert. Der Buffer Cache wird nach dem LRU-Prinzip verwaltet. Jeder Buffer hat die Größe eines Blocks. Datenblöcke können zu den folgenden Objekten gehören:

Der Buffer Cache wird gemeinsam von der LRU-Liste, einer Dirty-Liste, dem Server-Prozess des Benutzers sowie dem Database Writer (DBWn) verwaltet.

Die LRU-Liste

Auch der Buffer Cache wird nach dem LRU-Algorithmus verwaltet. Ist der Buffer Cache voll, werden Datenblöcke entfernt, die am Ende der LRU-Liste stehen. Mit diesem Prinzip bleiben die Blöcke, die zuletzt benutzt wurden, im Buffer Cache.

Wenn Daten angefordert werden, liest der Server-Prozess die Blöcke von der Festplatte und platziert sie an den Anfang der LRU-Liste. Die Buffer bleiben in der LRU-Liste, werden aber mit dem Lesen neuer Blöcke nach hinten verschoben.

Erfolgt ein Zugriff auf einen Buffer, während er in der LRU-Liste steht, dann wird dieser an den Anfang der LRU-Liste gestellt.

Der LRU-Algorithmus wird anders gehandhabt, wenn ein Full Table Scan (FTS) ausgeführt wird. Die Buffer werden sofort am Ende der LRU-Liste platziert. Damit wird verhindert, dass alle Buffer aus dem Cache entfernt werden.

Die Dirty-Liste

Es gibt die folgenden drei Buffer-Typen im Buffer Cache:

Der Server-Prozess kann Free Buffer einfach überschreiben, wenn Blöcke von der Festplatte gelesen werden. Dirty Buffer können nicht überschrieben werden, bevor sie auf die Festplatte geschrieben wurden.

Dieser Mechanismus wird Dirty-Liste genannt. Die Liste registriert alle Blöcke, die verändert und nicht auf die Festplatte geschrieben wurden. Das Schreiben der Buffer auf die Festplatte übernimmt der Hintergrundprozess Database Writer (DBWn).

Server-Prozesse der Benutzer

Wenn der Server-Prozess eines Benutzers die Daten im Buffer Cache nicht vorfindet, dann liest er die Datenblöcke von der Festplatte und schreibt sie in den Buffer Cache.

Bevor die Blöcke von der Platte gelesen werden können, müssen freie Blöcke im Buffer Cache gefunden werden. Der Server-Prozess sucht einen freien Block unter Benutzung der LRU-Liste und der Dirty-Liste in der folgenden Art und Weise:

Wenn ein Server-Prozess einen Buffer im Cache findet, kann dieser Daten enthalten, die nicht durch ein COMMIT abgeschlossen wurden. Oracle gestattet jedoch nicht, dass ein anderer Benutzer diese Daten sieht. Die Dateninhalte vor der Änderung befinden sich im Rollback- oder Undo-Segment. Diese Segmente werden auch im Buffer Cache gespeichert.

Der Database Writer (DBWn)

Es gibt eine Reihe von Ereignissen, die den Database Writer zwingen, Buffer auf die Festplatte zu schreiben. Die Liste der Ereignisse finden Sie in folgender Tabelle:

Ereignisse, die den Database Writer anstoßen

Ereignis

Aktion des DBWn

Die Dirty-Liste erreicht ihren Schwellenwert.

DBWn schreibt Buffer aus der Dirty-Liste weg.

Die LRU-Liste wurde zu lange durchsucht, ohne einen freien Buffer zu finden.

DBWn schreibt Buffer direkt aus der LRUListe weg.

Alle drei Sekunden

DBWn schreibt Dirty Buffer von der LRUListe in die Dirty-Liste. Wird der Schwellenwert erreicht, dann werden diese Buffer auf die Festplatte geschrieben.

Checkpoint

DBWn verschiebt alle Dirty Buffer von der LRU-Liste in die Dirty-Liste und schreibt sie weg.

Tablespace im Backup-Modus

DBWn verschiebt alle Dirty Buffer der Tablespace von der LRU-Liste in die Dirty-Liste und schreibt sie weg.

Tablespace Offline Temporary

DBWn verschiebt Dirty Buffer dieser Tablespace von der LRU-Liste in die Dirty-Liste und schreibt sie weg.

Drop Segment

DBWn schreibt die Dirty Buffer dieses Segments vorher auf die Festplatte.

Die Performance des Buffer Cache messen

Der wichtigste Indikator für die Performance des Buffer Cache ist die Hit Ratio. Eine große Hit Ratio bedeutet, dass die Benutzerprozesse Daten überwiegend im Buffer Cache finden und nur selten auf die Festplatte zugreifen.

Ein Cache Miss tritt dann auf, wenn der Server-Prozess eines Benutzers die benötigten Buffer nicht im Buffer Cache findet und diese von der Festplatte lesen muss. Das View V$SYSSTAT speichert drei Statistiken, die zur Bestimmung der Hit Ratio herangezogen werden können:

Aus diesen Werten kann die Hit Ratio bestimmt werden. Die Abfrage in folgender Tabelle ermittelt das Verhältnis zwischen Zugriffen auf den Buffer Cache und Zugriffen auf die Festplatte.

SQL> SELECT 1-(a.value / (b.value + c.value))
2 "Hit Ratio"
3 FROM v$sysstat a, v$sysstat b, v$sysstat c
4 WHERE a.name = 'physical reads'
5 AND b.name = 'db block gets'
6 AND c.name = 'consistent gets';
Hit Ratio
----------
,990704986

Eine gut optimierte OTLP-Datenbank sollte eine Buffer Cache Hit Ratio von mehr als 90 % aufweisen.

Hit Ratio des Buffer Cache

Für den Optimierungsprozess ist es wichtig, die Hit Ratio des Buffer Cache für individuelle Benutzer zu kennen. Es ist durchaus möglich, dass die allgemeine Performance der Datenbank in Ordnung ist, aber einzelne Benutzer Probleme berichten. Die Abfrage in folgendem Listing liefert die Hit Ratio pro Benutzer.

SQL> SELECT b.username, b.osuser,
2 (1-a.physical_reads / (a.block_gets+a.consistent_gets)) "Hit Ratio"
3 FROM v$sess_io a, v$session b
4 WHERE a.sid = b.sid
5 AND (a.block_gets + a.consistent_gets) != 0
6 AND b.username IS NOT NULL;
USERNAME OSUSER Hit Ratio
---------- ----------- ----------
DBSNMP oracle ,986853637
SYS oracle ,983943803
SYS Administrator 1
DBSNMP oracle ,998837278
SYS oracle ,953183521

Es gibt eine Reihe von Statistiken über Cash Misses. Mit deren Hilfe lässt sich analysieren, wie effektiv der LRU-Algorithmus arbeitet. Sie können die folgenden Werte abfragen:

Eine große oder ständig wachsende Anzahl von diesen Werten zeigt an, dass die Server-Prozesse zu viel Zeit verbringen, um freie Buffer im Buffer Cache zu finden. Mit Hilfe der Abfrage in folgendem Listing können Sie diese Statistikwerte abfragen.

SQL> SELECT name, value
2 FROM v$sysstat
3 WHERE name IN
4 ('free buffer inspected',
'free buffer waits','buffer busy waits');
NAME VALUE
----------------------------------- ----------
free buffer inspected 2343
buffer busy waits 1287
free buffer waits 1343

Verwenden Sie die Abfrage in folgendem Listing, um festzustellen, welche Objekte momentan wie viele Blöcke im Buffer Cache belegen.

SQL> SELECT object_name, object_type,
count(*) "Anz. Buffer"
2 FROM x$bh a, dba_objects b
3 WHERE a.obj = b.object_id
4 AND owner NOT IN ('SYS','SYSTEM')
5 GROUP BY object_name, object_type
6 ORDER BY 3 DESC;
OBJECT_NAME OBJECT_TYPE Anz. Buffer
-------------------------- ------------ -----------
SMP_VTM_DISPLAY_STATE TABLE 8
SMP_VDG_NODE_LIST TABLE 7
SMP_VDD_OPERATIONS_TABLE TABLE 7
XDB$CONFIG TABLE 6
AQ$_QS_ES_ORDERS_MQTAB_S TABLE 6
AQ$_QS_OS_ORDERS_MQTAB_S TABLE 6
AQ$_QS_CS_ORDER_STATUS_QT_S TABLE 6
AQ$_QS_OS_ORDERS_PR_MQTAB_S TABLE 6

Das ultimative Ziel zur Verbesserung der Performance lautet für den Buffer Cache, die Hit Ratio zu erhöhen. Das kann mit den folgenden Strategien erreicht werden:

Den Buffer Cache vergrößern

Der einfachste Weg zur Verbesserung der Performance ist, den Buffer Cache zu vergrößern. Je größer der Buffer Cache ist, desto geringer ist die Wahrscheinlichkeit, dass Buffer durch den LRU-Mechanismus aus dem Buffer Cache entfernt werden.

Eine Vergrößerung des Buffer Cache erhöht nicht nur die Hit Ratio, auch die Anzahl von Warte-Ereignissen wie buffer busy waits oder free buffer waits geht zurück.

Die Hit Ratio verändert sich nicht linear zur Erhöhung der Buffer-Cache-Größe. Eine Verdoppelung des Buffer Cache führt nicht zu einer Verdoppelung der Hit Ratio.

Verschiedene Buffer Pools verwenden

Von vornherein benutzen alle Segmente denselben Buffer Pool, den so genannten Default Pool. Der Default Pool besitzt die Standard-Blockgröße der Datenbank. Ein Pool mit abweichender Blockgröße wird als Subcache bezeichnet.

Ein Subcache wird nach den Mechanismen des Default Pools verwaltet. Oracle unterscheidet die folgenden drei Pool-Typen, die unterschiedlich verwaltet werden:

Als Administrator müssen Sie festlegen, welcher Pool für welche Segmente geeignet ist. Dazu sollten Sie die Anwendung und das Datenmodell kennen.

Datenbankobjekte des Buffer Pool

Nützliche Informationen liefert auch das View V$BH. Es wurde für den Parallel Server entwickelt, steht aber auch für Single Instance zur Verfügung. Die Abfrage in folgendem Listing gibt alle Datenbankobjekte zurück, die sich im Buffer Pool befinden. Die Liste ist absteigend nach der Buffer-Anzahl sortiert.

SQL> SELECT a.owner, a.object_name, a.object_type,
2 COUNT(*)
3 FROM dba_objects a, v$bh b
4 WHERE a.object_id = b.objd
5 AND owner NOT IN ('SYS','SYSTEM')
6 GROUP BY a.owner, a.object_name, a.object_type
7 HAVING COUNT(*) > 2
8 ORDER BY 4 DESC;
OWNER OBJECT_NAME OBJECT_TYPE COU
--------- --------------------- ------------- ---
DBSNMP MGMT_RESPONSE_CAPTURE TABLE 11
DBSNMP MGMT_RESPONSE_CONFIG TABLE 6
DBSNMP MGMT_RESPONSE_BASELINE TABLE 6

Nachdem Sie festgelegt haben, welche Objekte in welchem Pool gespeichert werden sollen, gilt es zu bestimmen, wie groß die einzelnen Pools sein sollen. Die aktuelle Größe einer Tabelle kann mit der folgenden Abfrage ermittelt werden.

SQL> SELECT owner, table_name, blocks
2 FROM dba_tables
3 WHERE owner = 'SH';
OWNER TABLE_NAME BLOCKS
------------ ----------------------- ------
SH SALES 1768
SH COSTS 206
SH TIMES 59
SH PRODUCTS 5
SH CHANNELS 4

Die Statistiken in den Views DBA_TABLES und DBA_INDEXES werden nur bereitgestellt, wenn die Objekte analysiert sind. Analysieren Sie deshalb die Tabellen und Indexe. Verwenden Sie für das Erstellen von Statistiken das Paket DBMS_STATS.

Tablespaces mit unterschiedlichen Blockgrößen

Seit Oracle9i ist es möglich, Tablespaces mit unterschiedlichen Blockgrößen in einer Datenbank zu verwenden. Vom Datenbankstandard abweichende Blockgrößen benötigen einen separaten Buffer Pool. In folgender Tabelle finden Sie eine Übersicht der Initialisierungsparameter zum Einstellen der Größen der Buffer Pools.

Initialisierungsparameter für Buffer Pools

Parameter

Bedeutung

DB_CACHE_SIZE

Größe des Default Buffer Pools in Byte. »K«, »M«, »G« kann verwendet werden für Kilo-, Mega- und Gigabyte.

DB_CACHE_nK_SIZE

Größe der Subcaches für andere Blockgrößen. »n« ist die Blockgröße und kann die Werte 2, 4, 8, 16, 32 annehmen. Für die Standard-Blockgröße der Datenbank kann kein Subcache eingerichtet werden, hierfür ist DB_CACHE_SIZE zu verwenden.

DB_KEEP_CACHE_SIZE

Größe des Keep Pools in Byte, Kilo-, Mega- oder Gigabyte. Der Keep Pool kann nur für die Standard-Blockgröße verwendet werden.

DB_RECYCLE_CACHE _SIZE

Größe des Recycle Pools in Byte, Kilo-, Mega- oder Gigabyte. Der Recycle Pool kann nur für die Standard-Blockgröße verwendet werden.

Die Parameter aus vorhergehender Tabelle können dynamisch geändert werden, das heißt, ein Neustart der Instanz ist nicht erforderlich. Folgendes Listing zeigt ein Beispiel.

SQL> SHOW PARAMETER CACHE;
NAME TYPE VALUE
------------------- ----------- -----
db_cache_size big integer 33554432
db_keep_cache_size big integer 16777216
db_recycle_cache_size big integer 0
db_16k_cache_size big integer 16777216
db_2k_cache_size big integer 0
db_32k_cache_size big integer 0
db_4k_cache_size big integer 0
...
SQL> ALTER SYSTEM SET db_keep_cache_size=10M;
System wurde geändert.
SQL> ALTER SYSTEM SET db_recycle_cache_size=20M;
System wurde geändert.

Objekte den Buffer Pools zuordnen

Mit einer ALTER TABLE- oder einer ALTER INDEX-Anweisung können Sie, so wie in folgendem Listing, ein Objekt einem Buffer Pool zuordnen.

SQL> ALTER TABLE dwh.household_facts
2 STORAGE (BUFFER_POOL RECYCLE);
Tabelle wurde geändert.
SQL> ALTER TABLE dwh.time_dim
2 STORAGE (BUFFER_POOL KEEP);
Tabelle wurde geändert.

Mit der Abfrage in dem nächsten Listing können Sie feststellen, welches Objekt sich in welchem Pool befindet. Sie können die Pool-Zuweisungen jederzeit dynamisch verändern.

SQL> SELECT segment_name, segment_type, buffer_pool
2 FROM dba_segments
3 WHERE owner = 'DWH';
SEGMENT_NAME SEGMENT_ BUFFER_
-------------------- -------- -------
HOUSEHOLD_FACTS TABLE RECYCLE
ACCOUNT_DIM TABLE DEFAULT
BRANCH_DIM TABLE DEFAULT
HOUSEHOLD_DIM TABLE DEFAULT
PRODUCT_DIM TABLE DEFAULT
STATUS_DIM TABLE DEFAULT
TIME_DIM TABLE KEEP
ACCOUNTS_PART TABLE PA DEFAULT

Die Buffer bleiben nach Ausführung der ALTER TABLE-Anweisung zunächst im ursprünglichen Pool. Erst wenn die Blöcke das nächste Mal von der Festplatte gelesen werden, gelangen sie in den neuen Pool.

Die Performance der Buffer Pools kann mit Hilfe des Views V$BUFFER_POOL_STATISTICS überwacht werden. Die Abfrage in folgendem Listing zeigt die Hit Ratio pro Pool.

SQL> SELECT name, block_size,
2 DECODE(db_block_gets + consistent_gets,
3 0,0,1-(physical_reads / (db_block_gets +
consistent_gets))) "Hit Ratio"
4 FROM v$buffer_pool_statistics;
NAME BLOCK_SIZE Hit Ratio
-------------------- ---------- ----------
KEEP 8192 ,958333333
RECYCLE 8192 ,571428571
DEFAULT 8192 ,904970208
DEFAULT 16384 1

Wahrscheinlich haben Sie damit gerechnet, dass der Keep Pool näher bei 100 % liegt. Beachten Sie, dass es sich um eine kumulative Statistik seit dem letzten Start der Instanz handelt und die Datenblöcke mindestens einmal von der Festplatte gelesen werden müssen. Außerdem wird auch der Keep Pool nach dem LRU-Prinzip verwaltet.

Tabellen mit Buffer Pools fest verbinden

Unabhängig davon, in welchem Pool Sie ein Objekt platzieren, wird es mit einem LRU-Mechanismus verwaltet. Normalerweise werden die Buffer an den Anfang der LRU-Liste gestellt. Im Fall eines Full Table Scan werden die Buffer jedoch ans Ende der LRU-Liste platziert.

Das kann zu Optimierungsproblemen führen. Für kleine Tabellen, wie z.B. Lookup-Tabellen, plant der Cost-Based Optimizer in der Regel einen Full Table Scan. Diese Tabellen fallen dann relativ schnell wieder aus dem Buffer Pool heraus, obwohl es sinnvoller wäre, sie möglichst lange zu behalten.

Ein Weg, diesem Problem zu begegnen, ist die Benutzung von Cache Tables. Datenblöcke dieser Tabellen werden in jedem Fall an den Anfang der LRUListe gestellt. Es gibt die folgenden Möglichkeiten, Cache Tables zu implementieren:

Fügen Sie einfach die CACHE-Option in der CREATE TABLE-Anweisung hinzu, um eine Cache Table zu erstellen.

SQL> CREATE TABLE cache_test (
2 id NUMBER) CACHE;
Tabelle wurde angelegt.

Mit dem ALTER TABLE-Befehl können Sie die CACHE-Option ein- und ausschalten.

SQL> ALTER TABLE cache_test NOCACHE;
Tabelle wurde geändert.
SQL> ALTER TABLE cache_test CACHE;
Tabelle wurde geändert.

Auch mit einem Optimizer Hint können Sie eine Tabelle als Cache Table behandeln. Allerdings wird hierbei die Tabelle nicht permanent geändert, die CACHE-Option gilt nur für die Anweisung.

SQL> SELECT /*+ CACHE */ * FROM cache_test;

Mit der folgenden Anweisung können Sie alle Cache Tables identifizieren.

SQL> SELECT owner, table_name
2 FROM dba_tables
3 WHERE LTRIM(cache) = 'Y';
OWNER TABLE_NAME
------------ ---------------
SYS CACHE_TEST

Indexe sinnvoll einsetzen

Den wohl größten Einfluss auf die Performance des Buffer Cache haben Indexe. Wenn durch Verwendung eines Index der Explain-Plan so geändert wird, dass ein Full Table Scan vermieden wird, kommt es zwangsläufig zu viel weniger Leseoperationen von der Festplatte. (mha)