FirebirdSQL logo

Introduction

Firebird utilise un cache de pages pour conserver les pages en mémoire. Il est beaucoup plus rapide de récupérer des pages de la RAM que d’aller sur le système de disque et de les lire physiquement chaque fois qu’elles sont nécessaires. La description suivante de la façon dont Firebird utilise son cache est tirée d’un message d’Ann Harrison sur la liste de diffusion Firebird Support.

Le poste était en réponse à une question demandant s’il existe un moyen de réduire le cache mémoire sans renoncer à trop de performances. Il s’agissait en soi d’un système qui mettait beaucoup de temps à sortir de l’hibernation et dont la cause était le temps nécessaire pour recharger toutes les pages de cache du disque avant que la première requête (après hibernation) ne puisse être traitée. La cause a été confirmée et le DBA responsable de la base de données a demandé, sur la liste, comment il pouvait réduire au maximum le cache de la mémoire tampon tout en ayant un système réactif.

Ann a donné son accord pour que cette publication soit documentée officiellement dans le cadre du projet de documentation Firebird.

Le cache de Firebird

Tout dans la base de données est organisé sous forme de pages de taille fixe de structure connue - il existe neuf types de pages différents. Le cache de page est l’intermédiaire entre les parties « fonctionnelles » de la base de données et le disque.

Au démarrage, Firebird lit la page d’en-tête de la base de données, puis la première page de pointeur d’une table système appelée RDB$PAGES à partir de laquelle il sait alors où trouver les pages de pointeurs, entre autres, pour les tables système et utilisateur de la base de données.

Dès que l’application accède à la base de données et commence une transaction, Firebird lit les pages de pointeur lui indiquant où trouver les pages de données des tables impliquées dans les transactions de l’application. Toutes les pages vont dans le cache et y restent jusqu’à ce que le cache soit complètement plein. Lorsqu’il n’y a pas d’endroit où placer la page suivante, Firebird publie la page la moins récemment utilisée – pas la première lue, mais celle qui a été référencée le moins récemment et qui n’a pas été modifiée.

Lorsqu’une transaction est validée - et à d’autres moments - Firebird écrit les pages modifiées par cette transaction sur le disque, mais ne les libère pas du cache - cela permet d’éviter une lecture supplémentaire si la page est à nouveau requise bientôt.

Au fil du temps et avec un peu de chance, vous vous retrouvez avec les pages les plus fréquemment modifiées et référencées résidant dans le cache - il s’agit des pages d’inventaire des transactions, des pages de pointeur pour les tables actives, de la page d’en-tête, des niveaux supérieurs des index, etc. Les données et les pages d’index de niveau inférieur sont échangées selon les besoins, mais le cache sera toujours plein.

Vous pouvez utiliser les tables MON$ (à partir de Firebird 2.1, en particulier MON$IO_STATS) pour déterminer la qualité de l’utilisation de votre cache. Ils signalent le nombre d’extractions par rapport aux lectures, c’est-à-dire le nombre de fois où les pages ont été consultées par rapport au nombre de fois où elles ont dû être lues à partir du disque. Lorsque le nombre de lectures augmente considérablement, vous avez trop réduit le cache.

Les tables MON$ vous donneront également le nombre de marques par rapport aux écritures, ce qui vous indique le nombre de fois que les pages sont modifiées par rapport au nombre de fois où les pages ont été écrites sur le disque. Lorsque vous voyez le nombre d’écritures sur le disque augmenter, vous avez probablement trop réduit le cache.

MON$IO_STATS

Comme mentionné ci-dessus, la table MON$IO_STATS peut être utilisée pour vous aider à déterminer les performances de votre cache tampon. La structure de la table est la suivante :

MONS$STAT_ID

ID statistique.

MONS$STAT_GROUP

Groupe de statistiques. Des statistiques sont recueillies pour les groupes suivants :

  • 0: The database as a whole.

  • 1: Attachments.

  • 2: Transactions.

  • 3: Statements.

  • 4: Calls.

MON$PAGE_READS

Le nombre de pages lues. Il s’agit des pages lues à partir du disque et non du cache.

MON$PAGE_WRITES

Nombre de pages réécrites sur le disque.

MON$PAGE_FETCHES

Nombre de pages lues à partir du cache plutôt que du disque.

MON$PAGE_MARKS

Le nombre de pages a changé dans le cache. Il est possible que tous n’aient pas été réécrits sur disque.

Pour inspecter les statistiques actuelles de la base de données dans son ensemble, nous utiliserions la requête suivante dans isql :

tux> isql employee
Database:  employee

SQL> SELECT MON$PAGE_READS, MON$PAGE_WRITES, MON$PAGE_FETCHES, MON$PAGE_MARKS
CON> FROM MON$IO_STATS
CON> WHERE MON$STAT_GROUP = 0;
     MON$PAGE_READS     MON$PAGE_WRITES    MON$PAGE_FETCHES      MON$PAGE_MARKS
=================== =================== =================== ===================
                134                 526               13851                 529

Les résultats de ce qui précède montrent que :

  • Jusqu’à présent, 134 pages ont dû être lues physiquement à partir du disque dans le cache.

  • 13 851 pages, en revanche, ont été lues directement à partir du cache.

  • 529 pages, dans le cache, ont été modifiées d’une manière ou d’une autre.

  • 526 pages modifiées ont été copiées du cache sur le disque.

Nous pouvons donc supposer que, bien qu’un petit nombre de pages aient été lues dans le cache, il n’y a rien que nous puissions faire pour éviter cela. Lorsque la base de données est démarrée, le cache est vide, lorsque les applications se connectent et accèdent à la base de données, diverses pages doivent être lues et le cache doit être rempli, de sorte que les lectures physiques seront une nécessité. Dans cet exemple, il semble qu’une fois que les pages sont dans le cache, elles sont consultées assez fréquemment, étant donné qu’il y a eu environ 103 lectures de cache pour chaque lecture physique.

Sur les 529 pages mises à jour - et il s’agit de pages système et de pages utilisateur - 526 ont été réécrites sur les disques physiques, mais trois restent encore en cache, non encore écrites.

Les résultats présentés ci-dessus montrent les performances du cache sur la durée de vie de la base de données jusqu’à présent. Nous pouvons réduire cela à nos pièces jointes actuelles en modifiant la requête pour sélectionner les lignes où le MON$STAT_GROUP est égal à 1.

SQL> SELECT MON$PAGE_READS, MON$PAGE_WRITES, MON$PAGE_FETCHES, MON$PAGE_MARKS
CON> FROM MON$IO_STATS
CON> WHERE MON$STAT_GROUP = 1;
     MON$PAGE_READS     MON$PAGE_WRITES    MON$PAGE_FETCHES      MON$PAGE_MARKS
=================== =================== =================== ===================
                  0                   4                  87                   5
                134                 520               13619                 522

L’interprétation des statistiques ci-dessus est exactement la même que pour l’ensemble de la base de données.

Nous pouvons ensuite diagnostiquer les statistiques par transactions individuelles, comme suit :

SQL> SELECT MON$PAGE_READS, MON$PAGE_WRITES, MON$PAGE_FETCHES, MON$PAGE_MARKS
CON> FROM MON$IO_STATS
CON> WHERE MON$STAT_GROUP = 2;
     MON$PAGE_READS     MON$PAGE_WRITES    MON$PAGE_FETCHES      MON$PAGE_MARKS
=================== =================== =================== ===================
                  0                   0                  60                   0
                  0                   0                   1                   0
                  0                   0                   1                   0
                  0                   0                  69                   0
                  0                   0                  93                   0
                  0                   0                  85                   0
                  0                   0                   1                   0
                  0                   0                   1                   0

Et, par des états individuelles :

SQL> SELECT MON$PAGE_READS, MON$PAGE_WRITES, MON$PAGE_FETCHES, MON$PAGE_MARKS
CON> FROM MON$IO_STATS
CON> WHERE MON$STAT_GROUP = 3;
     MON$PAGE_READS     MON$PAGE_WRITES    MON$PAGE_FETCHES      MON$PAGE_MARKS
=================== =================== =================== ===================
                  0                   0                   1                   0
                  0                   0                  38                   0
                  0                   0                   4                   0
                  0                   0                  18                   0
                  0                   0                 158                   0
                  0                   0                   1                   0
                  0                   0                   1                   0
                  0                   0                   1                   0
                  0                   0                   1                   0
                  0                   0                   1                   0
                  0                   0                   0                   0
                  0                   0                   1                   0
                  1                   0                  12                   0
                  0                   0                   2                   0
                  3                   0                1436                   0
                  0                   0                 101                   0
                  7                   0                 613                   0

Enfin, il est possible - et probablement le plus utile - de déterminer les statistiques de votre propre session. Vous pouvez trouver votre ID de pièce jointe à partir de CURRENT_CONNECTION et l’utiliser dans une requête qui se joint à MON$IO_STATS à l’aide de la colonne MON$STAT_ID.

SQL> SET LIST;

SQL> SELECT T.MON$ATTACHMENT_ID, T.MON$TRANSACTION_ID,
CON> IO.MON$PAGE_READS, IO.MON$PAGE_WRITES,
CON> IO.MON$PAGE_FETCHES, IO.MON$PAGE_MARKS
CON> FROM MON$TRANSACTIONS AS T
CON> JOIN MON$IO_STATS as IO
CON> ON (IO.MON$STAT_ID = T.MON$STAT_ID)
CON> WHERE T.MON$ATTACHMENT_ID = CURRENT_CONNECTION;
MON$ATTACHMENT_ID               12
MON$TRANSACTION_ID              218
MON$PAGE_READS                  5
MON$PAGE_WRITES                 0
MON$PAGE_FETCHES                66
MON$PAGE_MARKS                  0

MON$ATTACHMENT_ID               12
MON$TRANSACTION_ID              217
MON$PAGE_READS                  0
MON$PAGE_WRITES                 0
MON$PAGE_FETCHES                1
MON$PAGE_MARKS                  0

Appendices