News
DBA-Tipp: Buffer Caches

Die Information ist ein schnelllebiges Gut. Jeden Tag werden wir mit hunderten Informationen zugemüllt. Deshalb sind wir bestrebt uns auf das Wesentliche zu konzentrieren und nur substantiell nachhaltige Informationen bereitzustellen.

Icon Unternehmen

Der Buffer Cache der Oracle Datenbank speichert alle Datenblöcke, die von der Datenbank gelesen oder verarbeitet werden. Er spielt eine zentrale Rolle im Performancetuning, da Blöcke, die sich im Buffer Cache befinden, nicht von Platte gelesen werden müssen und somit deutlich schneller verfügbar sind. Da nur in den seltensten Fällen der Buffer Cache groß genug ist, die gesamte Oracle Datenbank aufzunehmen, findet in diesem Cache eine Verdrängung nach einem LRU (Least Recently Used) Verfahren statt. Sobald Platz im Buffer Cache für neu zu lesende Blöcke benötigt wird, werden die am längsten nicht verwendeten Blöcke aus dem Cache verdrängt und müssen bei erneutem Zugriff wieder von Platte gelesen werden.

Grundsätzlich ist dieser Algorithmus praxiskonform, solange man eine reine Online Transaction Processing (OLTP) oder reine Data Warehouse (DSS) Konfiguration vorliegen hat. Problematisch verhält es sich hingegen mit Mischformen. Während der OLTP-Teil für eine gute Performance auf gecachte Blöcke angewiesen ist, schleust die DSS-Komponente u.U. große Datenmengen durch den Buffer Cache (sofern sie keine direct path Operationen nutzen kann) und profitiert zudem eher selten von gecachten Daten.

Ein bereits sehr altes Feature, Datenblöcke möglichst von der Verdrängung aus dem Buffer Cache zu bewahren, ist die CACHE Klausel. Die Blöcke CACHE-markierter Tabellen und Indizes werden an der Spitze der LRU-Liste abgelegt und nur verdrängt, wenn bereits sämtliche anderen Blöcke aus dem Cache verdrängt wurden. Dieses Feature hat allerdings zwei signifikante Nachteile:

  • CACHE-markierte Blöcke werden nach wie vor verdrängt, wenn Operationen Datenmengen bearbeiten, die die Größe des NOCACHE-Bereiches überschreiten.
  • Die Cache-Klausel empfiehlt sich nur für kleine, häufig zugegriffene Objekte. Je größer die Anzahl der CACHE-markierten Blöcke tatsächlich ist, desto unflexibler arbeitet zwangsläufig das LRU-Verfahren und desto wahrscheinlicher werden auch CACHE-Blöcke verdrängt.

Eine wesentlich bessere Lösung stellt hier das „Multiple Buffer Pools Feature“ der Oracle Datenbank ab Version 8 dar. Zusätzlich zum default buffer cache können zwei weitere Caches abgegrenzt werden – der „keep“- und der „recycle“-Pool. Auch diese Pools unterliegen dem LRU-Algorithmus, haben allerdings ihre eigene LRU-Liste und sind somit gegeneinander und gegenüber dem default buffer cache isoliert. D.h., Blockverdrängung findet zwar weiterhin statt, wirkt sich aber nur noch auf Blöcke innerhalb des jeweiligen Pools aus. Damit könnte also z.B. eine große DSS-Tabelle über den recycle-Pool geladen werden, ohne gleichzeitig „wertvolle“ OLTP-Blöcke, die sich im default pool befinden, aus dem Cache zu verdrängen. Auf der anderen Seite würde man Objekte, die klein sind und sehr häufig benötigt werden, dem keep-Pool zuordnen. Die Namen keep und recycle sind aus technischer Sicht übrigens als rein willkürlich zu betrachten. Alle drei Pools folgen den selben Regeln.

Keep- und Recycle-Pool werden mit dem Setzen der entsprechenden init-Parameter DB_KEEP_CACHE_SIZE und DB_RECYCLE_CACHE_SIZE eingerichtet. Der zugewiesene Platz geht zu Lasten des Default Buffer Cache (DB_CACHE_SIZE). Sowohl keep- als auch recycle-Pool sind nicht für abweichende Blockgrößen (DB_nK_CACHE_SIZE) verfügbar. Damit ein Objekt den keep- oder recycle-Pool verwendet, muss dieser explizit über den „buffer_pool“ Parameter der storage-Klausel im CREATE- oder ALTER-Befehl zugewiesen werden.

Die aktuelle Belegung der einzelnen Buffer Pools lässt sich mit folgender Abfrage ermitteln:

select subcache,object_name,sum(blocks) blocks from (

 select decode(pd.bp_id,1,'KEEP',2,'RECYCLE',3,'DEFAULT',
                4,'2K SUBCACHE',5,'4K SUBCACHE',6,'8K SUBCACHE',
                7,'16K SUBCACHE',8,'32K SUBCACHE','UNKNOWN') subcache,
         bh.object_name,
         bh.blocks
  from   x$kcbwds ds,
         x$kcbwbpd pd,
         (select /*+ use_hash(x) */ set_ds,
                 o.name object_name,
                 count(*) BLOCKS
          from   obj$ o,
                 x$bh x
          where  o.dataobj# = x.obj
          and    x.state !=0 and o.owner# !=0
          group by set_ds,o.name) bh
  where  ds.set_id >= pd.bp_lo_sid
  and    ds.set_id <= pd.bp_hi_sid
  and    pd.bp_size != 0
  and    ds.addr=bh.set_ds)
group by subcache,object_name
order by 3 desc;