nettoyage de blocs (commit cleanout) et blocs sales (dirty block), par Jonathan Lewis

Cet article est la traduction d’un article de Jonathan Lewis publié sur son blog. L’article original en anglais se trouve ici.
En expliquant les significations de ‘clean block’, il détaille le fonctionnement d’Oracle lorsqu’on modifie un bloc, puis lorsque ces modifications sont commitées.

Il y a une certaine confusion à propos du terme ‘clean‘ autour d’Oracle, donc j’ai pensé que je devrais écrire une note pour expliquer les différentes notions que ce mot peut couvrir lorsqu’il est appliqué aux blocs Oracle. Il y a 5 termes:

  1. clean
  2. commit cleanout
  3. block cleanout
  4. delayed block cleanout
  5. delayed logging block cleanout


Clean:

Un bloc (ou plus précisément la copie d’un bloc dans un buffer en mémoire) est ‘propre’ (‘clean‘), non-modifié, si la copie en mémoire est identique à la version sur disque. Au contraire, si la version en mémoire est différente (probablement parce que quelque chose a été modifié en mémoire) alors le buffer est dit ‘sale’ (‘dirty‘), modifié.

On doit être prudent lorsqu’on vérifie si un block est ‘dirty‘. La structure du ‘buffer header’ (x$bh) a une colonne ‘flag’ qui utilise une série de bits pour indiquer l’état du buffer. Le bit zéro est le bit ‘buffer_dirty‘ avec lequel onpeut vérifier facilement si un buffer est modifié. Mais il y a un piège. Oracle ne doit écrire sur disque que les blocs qui sont en version courante (CUR, contrairement aux versions re-crées pour la lecture consistente, CR) mais ce bit n’est pas remis à zéro lorsque le bloc est cloné pour faire une version lecture consistente (CR). Donc si vous voulez compte le nombre de ‘dirty buffers‘, n’oubliez pas de restreindre le select aux états XCUR et SCUR (x$bh.state à 1 ou 2)

Il y a souvent une confusion à propos de ce qu’il se passe lorsque DBWR (le processus database writer) écrit un bloc sur le disque. J’ai souvent entendu dire que le bloc est ‘vidé’ (‘flushed‘) du cache lorsque l’écriture a lieu, ce qui impliquerait que le bloc disparaît du cache. La pluspart du temps, ce n’est pas vrai. Il y a deux raisons pour que DBWR écrive sur disque, et une seule est suivie d’un appel pour vider le buffer qui vient d’être écrit.

La première raison, c’est pour garder les fichiers de donnée (‘datafiles‘) à jour. Dans ce cas, la copie du bloc en mémoire, dans le buffer, reste en cache et passe simplement de ‘dirty‘ à ‘clean‘.

L’autre raison, c’est quand une session a cherché un buffer libre (‘free buffer‘) et n’en a pas trouvé assez rapidement. Il a alors appelé DBWR pour libérer un peu d’espace dans le buffer cache. Et c’est le cas où DBWR vide le bloc de la mémoire (ou plus précisément marque le buffer comme libre ‘free‘) après l’avoir copié sur disque. Il y a aussi quelques autres cas, comme truncate table, tablespace offline, etc.

Commit Cleanout:

Lorsque vous modifiez des données, vous marquez des blocs comme ‘dirty‘. Il est possible que DBWR écrive ces blocs sur disque (et donc marque ces blocs comme ‘clean‘) avant que vous ne fassiez un commit. Lorsque vous faites le commit, votre session va mettre à jour l’entrée de la transaction dans la table des transactions (transaction table slot). Cette entrée est dans le bloc d’entête du segment undo (undo segment header) et cette modification génère un peu de redo pour décrire cette action, puis le commit appelle LGWR (log writer) pour qu’il flushe le log buffer et l’écrive sur disque (fichiers redo log)

Éventuellement, votre session peut aussi revisiter quelques blocs qu’elle a modifié (normalement jusqu’à 10% du buffer cache) et marquer leur entrée ITL (interrested transaction list) avec le SCN du commit (‘commit SCN‘). C’est cette activité qui s’appelle ‘commit cleanout‘. Le commit cleanout ne fait pas un nettoyage complet du bloc (il laisse par exemple le flag de vérouillage ‘lock byte’). Il s’assure seulement que la prochaine transaction qui va voir ce bloc saura que la transaction a été commitée et quand elle a été commitée. Cette fonctionnalité a été introduite en version 7.3 pour réduire les échanges de blocs en Parallel Server (le précurseur de RAC).

Précisons que si un de ces blocs avait été écrit précédemment par DBWR (donc en statut ‘clean‘), il sera marqué ‘dirty‘ à nouveau lorsque votre session va faire le commit cleanout, et DBWR devra à nouveau l’écrire plus tard. Mais bien que le commit cleanout modifie les blocs, il ne génère pas de redo pour décrire le changement, et il n’incrémente pas la statistique db block gets lorsqu’il visite le bloc.

Block Cleanout et Delayed block Cleanout

Il est possible que les blocs modifiés par votre transaction aient été écrits sur disque par DBWR, et même qu’ils aient été vidés du buffer cache avant que vous ne fassiez le commit. Votre session ne va pas relire ces blocs pour effectuer le commit cleanout sur eux. En fait, si vous avez une longue transaction et que vous avez modifié un grand nombre de blocs, il est possible que votre session ne fasse même pas le commit cleanout sur tous les blocs en mémoire. Oracle ne veut pas que l’utilisateur attende sur un commit, et ne fait pas forcément un commit cleanout complet.

Plus tard, une autre session va lire ce bloc et s’apercevoir que la liste des transaction (ITL) a une entrée pour une transaction qui a été commitée, mais pour laquelle le commit cleanout n’a pas eu lieu. Cette vérification est faite en reliant l’entrée ITL avec la table des transactions (transaction table slot) de l’entête du segment undo (undo segment header).

cette session va alors lire le commit SCN de la table des transactions, nettoyer l’entrée ITL et mettre à zéro les flags de verrou (et il va faire celà pour toutes les transaction concernées par le bloc). C’est ce nettoyage du bloc qui s’appelle le ‘block cleanout‘ et comme ce nettoyage complet n’est jamais fait lors du commit, il est plus connu comme ‘delayed block cleanout‘.

Cette opération de ‘delayed block cleanout‘ modifie le bloc, donc il génère du redo. Et c’est la raison pour laquelle on peut voir un simple select générer du redo, souvent après un gros update. On peut noter aussi que qu’oracle incrémente la statistique ‘db block changes‘ mais pas ‘db block gets‘.

Delayed Logging Block Cleanout

Enfin, revenons au bloc qui a été sujet à un commit cleanout. Si vous lisez ce bloc, vous devez voir les modifications commitées. Mais comme le commit cleanout a eu lieu et a écrit le commit SCN dans l’entrée ITL, vous savez qand la transaction a été commitée et nous n’avez généralement rien d’autre à faire comme nettoyage pour lire le bloc.
Par contre, si vous voulez modifier le bloc, vous vous rendez responsable du nettoyage à finir. Vous pouvez même avoir à réutiliser cette entrée ITL et à modifier des enregistrements qui ont toujours le flag de verrou (lock byte). A ce moment, vous terminez le nettoyage du block (block cleanout), et le redo que vous générez décrit non seulement votre changement, mais aussi ceux qui ont été fait avant par le commit cleanout. Celà s’appelle delayed logging block cleanout car la génération de redo concernant le commit cleanout est reportée au block cleanout.

Il y aurait d’autres détails, comportements étranges et particularités des statistiques, que je pourrai décrire – mais je vous ai probablement assez embrouillé, et j’arrête là.

Laisser un commentaire