Comprendre la cartographie mémoire du « Buffer Cache » (1ère partie)

Le cache de données (Buffer cache) mémoire est utilisé pour stocker en mémoire les blocs de données de la base et de ce fait réduire le nombre d’accès disque relativement couteux en termes de performance. C’est l’intermédiaire entre les demandes des utilisateurs et les fichiers de données stockés sur les baies de disques. Beaucoup d’articles ont été réalisés sur ce sujet et nous allons tenter de fournir les éléments permettant de visualiser le contenu de ce cache et d’en faire une analyse sous forme graphique (lors de la 2ème partie).

Les données utilisées par les processus dédiés d’Oracle sont placées dans le cache de données par ces derniers. Pour une gestion optimale du cache, un algorithme « offline » de remplacement garantit le meilleur ratio « Cache Hit ».
Le « Cache Hit Ratio » est normalement calculé en utilisant la formule suivante :

Cache Hit Ratio = 100 * (1 - physical reads/logical reads)

 
La figure ci-dessous montre les mouvements de blocs entre le disque et le cache de données à travers des processus utilisateur et le processus d’arrière-plan « database Writer (DBWR) ». Les processus utilisateur (DEDICATED) recherchent les blocs de données déjà présents en mémoire cache et s’ils ne sont pas présents réalisent une ou plusieurs lectures à partir de la baie de stockage. Le processus DBWR est responsable de l’écriture des blocs de données modifiés (DIRTY BLOCKS) à partir du cache de données sur les disques.
Image1L’algorithme sur le cache de données utilise les principes suivants :

  • Tous les blocs de la base de données ne peuvent pas être stockés dans la mémoire cache en même temps. De ce fait, pour stocker les nouvelles demandes de blocs, il est nécessaire de prendre la place des blocs déjà présents.
  • Les blocs les plus demandés ne doivent pas être retirés de la mémoire afin d’optimiser les I/Os. Dans la même logique de performance, il est nécessaire de se protéger contre les « Table Access Full » ou bien les « Fast Full Scan » d’index qui sont de véritables « cache killer », car tous les blocs de table ou d’index sont également placés dans la mémoire tampon.
  • LRU (Least Recently Used) : Lorsque le cache de données est plein, celui-ci remplace les blocs qui sont les moins récemment utilisés pour faire place à de nouvelles données. Cette action est réalisée à partir d’une liste appelée « Main LRU List », constituée de pointeurs sur tous les blocs en mémoire.
  • MRU (Most Recently Used) : Au lieu de remplacer le bloc le moins récemment utilisé comme LRU, MRU replace le bloc le plus récemment utilisé dans la liste LRU. Il a été initialement conçu pour l’optimisation des accès aux données, c’est-à-dire les lectures à faible nombre de blocs. Ces lectures « sequential reads » sont opposées aux lectures par balayages complet d’une table « scattered reads ».
  • L’algorithme place les lectures de blocs de tables ou d’index complets à l’extrémité de la liste LRU et permet de stocker un nombre limité de ces blocs dans le cache à la fois. Cette protection empêche de faire des actions de type « FLUSH BUFFER_CACHE » et les opérations d’I/Os associées.
  • Pour que le fonctionnement du cache soit souple et efficace, un algorithme très rapide doit être mis en œuvre afin d’offrir aux blocs les plus utilisés la possibilité de rester longtemps dans ce cache. Le mécanisme de liste LRU est de ce fait complété par la comptabilisation du nombre d’accès utilisateurs aux blocs de données (Touch Count) et la création d’un compteur individuel. Lorsque le bloc est déplacé dans la MRU, le compteur est réinitialisé à 0. En revanche, s’il est déplacé vers la LRU, son compteur est positionné à 1.

La figure suivante présente les règles d’entrée des blocs de données en mémoire et de déplacement en fonction de l’utilisation des blocs :XBH1 - Cartographie
Oracle utilise les « Touch Count » pour le déplacement des blocs dans les listes LRU/MRU :XBH2 - Cartographie

  • Le cache de données est divisé en zones nommées « pools ». La zone « KEEP » est conçue pour les petits objets qui, pour des questions de temps de réponse de l’application, doivent toujours être présent en cache. La zone « RECYCLE » est conçue pour des objets plus grands qui peuvent perturber l’utilisation du cache en faisant sortir de celui-ci les blocs plus utiles. Enfin, le pool par défaut « DEFAULT » est utilisé pour tout le reste. A noter que les zones KEEP et RECYCLE ne disposent d’aucun mécanisme particulier agissant sur la performance. Seul le placement des données dans ces zones et le suivi par le DBA agissent sur les performances. J’attire votre attention sur le risque de sous-dimensionner les zones KEEP et RECYCLE et il est nécessaire de suivre les ratios « Hit Cache » de ces zones.

Les requêtes suivantes, vont nous permettre d’analyser le contenu du cache mémoire pour les blocs de données :
Taille actuelles des zones HOT/COLD par pool :

SELECT bh.inst_id INSTANCE_ID,
         pd.bp_name BUFFER_POOL,
         DECODE (bh.LRU_FLAG,
                 8, DECODE (TCH,  0, 'HOT',  1, 'HOT',  '+HOT'),
                 DECODE (bh.TCH,  0, 'COLD',  1, 'COLD',  2, '+COLD',  '-HOT'))
            LRU,
         ROUND ( (bh.blsiz * (COUNT (*))) / 1048576, 0) SIZE_MB
    FROM x$bh bh, x$kcbwds ds, x$kcbwbpd pd
   WHERE     bh.inst_id = USERENV ('INSTANCE')
         AND 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 bh.inst_id,
         pd.bp_name,
         DECODE (
            bh.LRU_FLAG,
            8, DECODE (TCH,  0, 'HOT',  1, 'HOT',  '+HOT'),
            DECODE (bh.TCH,  0, 'COLD',  1, 'COLD',  2, '+COLD',  '-HOT')),
         bh.blsiz
ORDER BY bh.inst_id, pd.bp_name;

Le résultat de la requête précédente donne les valeurs suivantes par instance, nom de zone du buffer cache (pool) et type de bloc (HOT/COLD) :

INSTANCE_ID BUFFER_POOL          LRU      SIZE_MB
----------- -------------------- ----- ----------
          1 DEFAULT              +COLD         24
          1 DEFAULT              -HOT          29
          1 DEFAULT              COLD         234


Taille des objets par pool :

SELECT bh.inst_id INSTANCE_ID,
         pd.bp_name BUFFER_POOL,
         DECODE (ob.name, NULL, TO_CHAR (bh.obj), us.name || '.' || ob.name)
            OBJECT_NAME,
         DECODE (ob.type#,
                 1, 'INDEX',
                 2, 'TABLE',
                 19, 'TAB PART',
                 20, 'IDX PART',
                 21, 'LOB',
                 24, 'QUEUE',
                 42, 'MVIEW',
                 'Others')
            TYPE_OBJ,
         ROUND ( (bh.blsiz * (COUNT (*))) / 1048576, 0) SIZE_MB
    FROM x$bh bh,
         sys.obj$ ob,
         sys.user$ us,
         x$kcbwds ds,
         x$kcbwbpd pd
   WHERE     bh.inst_id = USERENV ('INSTANCE')
         AND bh.obj = ob.dataobj#(+)
         AND ob.owner# = us.user#(+)
         AND ob.owner# > 5
         AND 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 bh.inst_id,
         pd.bp_name,
         bh.blsiz,
         DECODE (ob.name, NULL, TO_CHAR (bh.obj), us.name || '.' || ob.name),
         DECODE (ob.type#,
                 1, 'INDEX',
                 2, 'TABLE',
                 19, 'TAB PART',
                 20, 'IDX PART',
                 21, 'LOB',
                 24, 'QUEUE',
                 42, 'MVIEW',
                 'Others')
  HAVING ROUND ( (bh.blsiz * (COUNT (*))) / 1048576, 0) > 10
ORDER BY ROUND ( (bh.blsiz * (COUNT (*))) / 1048576, 0) DESC;

Dans cette sélection, nous ne prenons que les objets dont la taille en mémoire est supérieure à 10MB. Le résultat nous donne deux objets de type « TABLE ».

INSTANCE_ID BUFFER_POOL          OBJECT_NAME                    TYPE_OBJ    SIZE_MB
----------- -------------------- ------------------------------ -------- ----------
          1 DEFAULT              XAS.PERF_ADM                   TABLE            84
          1 DEFAULT              XAS.MES_COMMANDE               TABLE            22


Nombre d’utilisation des objets par pool dans le cache de données :

SELECT bh.inst_id INSTANCE_ID,
         pd.bp_name BUFFER_POOL,
         DECODE (ob.name, NULL, TO_CHAR (bh.obj), us.name || '.' || ob.name)
            OBJECT_NAME,
         DECODE (ob.type#,
                 1, 'INDEX',
                 2, 'TABLE',
                 19, 'TAB PART',
                 20, 'IDX PART',
                 21, 'LOB',
                 24, 'QUEUE',
                 42, 'MVIEW',
                 'Others')
            TYPE_OBJ,
         SUM (tch) TOTAL_TCH,
         ROUND (AVG (tch), 3) AVG_TCH,
         MAX (tch) MAX_TCH,
         MIN (tch) MIN_TCH
    FROM x$bh bh,
         sys.obj$ ob,
         sys.user$ us,
         x$kcbwds ds,
         x$kcbwbpd pd
   WHERE     bh.inst_id = USERENV ('INSTANCE')
         AND bh.obj = ob.dataobj#(+)
         AND ob.owner# = us.user#(+)
         AND ob.owner# > 5
         AND 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 bh.inst_id,
         pd.bp_name,
         bh.blsiz,
         DECODE (ob.name, NULL, TO_CHAR (bh.obj), us.name || '.' || ob.name),
         DECODE (ob.type#,
                 1, 'INDEX',
                 2, 'TABLE',
                 19, 'TAB PART',
                 20, 'IDX PART',
                 21, 'LOB',
                 24, 'QUEUE',
                 42, 'MVIEW',
                 'Others')
  HAVING SUM (tch) > 2000
ORDER BY MAX (tch) DESC ;

Cette requête nous donne la liste des objets en mémoire ayant une utilisation supérieure à 2000 accès (« Touch count »).

INSTANCE_ID BUFFER_POOL OBJECT_NAME        TYPE_OBJ  TOTAL_TCH AVG_TCH MAX_TCH MIN_TCH
----------- ----------- ------------------ -------- ---------- ------- ------- -------
          1 DEFAULT     XAS.IDX_FK_0025    INDEX          2266   8.181     119       0
          1 DEFAULT     XAS.IDX_FK_FACTURE INDEX          2401   7.696      96       0
          1 DEFAULT     XAS.IDX_DO         INDEX          3113   5.021      95       0
          1 DEFAULT     XAS.IDX_UNITE      INDEX          2697    7.25      95       0
          1 DEFAULT     XAS.IDX_DOMAINE    INDEX          3673   6.727      95       0
          1 DEFAULT     XAS.IDX_ST_ADM     INDEX          2654    7.04      95       0
          1 DEFAULT     XAS.IDX_STADM      INDEX          3039   6.115      94       0
          1 DEFAULT     XAS.IDX_FK_TVA     INDEX          2820   7.103      94       0

 
Nous verrons dans la seconde partie de cet article comment réaliser une analyse graphique fine du contenu du cache à l’aide de la fonctionnalité PowerPivot d’Excel, restez en ligne avec nous.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *