Ceci est une traduction de d’un ancien post de Jonathan Lewis sur forums.oracle.com, référencé récemment sur son blog. Il décrit le fonctionnement de la journalisation en mémoire (IMU – In Memory Undo), une optimisation introduite en 10g qui utilise des structures en mémoire pour diminuer la contention sur les blocs d’undo et le redo log buffer.
Le contenu des blocs d’undo et des fichiers de redo log sont quasiment les même que l’on utilise in-memory undo (et les private redo threads) ou que l’on utilise la journalisation ‘normale’.
La principale différence se trouve dans l’ordre où sont faites les choses.
Il y a aussi, avec in-memory undo, une diminution du nombre de redo records même si le nombre de change vectors reste le même.
Voici le séquencement d’une transaction courte avec gestion normale de la journalisation.
- Vous modifiez un bloc de table ou d’index. Un vecteur de changement (redo change vector) est généré pour cette modification.
- En même temps, vous devez enregistrer l’information nécessaire pour défaire (rollback) de cette modification. C’est un enregistrement d’annulation (undo record) qui est généré pour décrire ce qui a été altéré.
- Mais comme cet undo record est stocké dans un bloc d’undo (rollback segment), alors un vecteur de changement redo change vector est généré pour décrire cette modification du bloc d’undo
- Oracle combine ces deux redo change vector (vecteurs de changement du bloc de donnée et du bloc d’undo) dans en un enregistrement de redo (redo record), ce qui incrémente la statistique de session ‘redo entries’.
- Donc pour cette modification, Oracle doit acquérir de l’espace dans le tampon journalisation redo log buffer avec le latch ‘redo allocation’ et y copier l’enregistrement de redo avec le latch ‘redo copy’
Si l’on insère 10 lignes, une par une, dans une table qui a 4 indexes, alors on va générer 50 redo records et 50 undo records, et faire appel 50 fois au latches de redo: 5 redo record par ligne (un pour la table et un pour chaque index) pour 10 lignes.
Lorsque la fonctionnalité de journalisation en mémoire (in-memory undo) est activée, et parce que dans cet exemple il s’agit d’une petite transaction, voici ce qu’il se passe:
- A moment où on modifie la première ligne de la table, Oracle alloue dans la shared pool son propre buffer de redo privé (appelé redo strand) et son propre buffer de « undo ». En fait, ce buffer de « undo » contient du redo: c’est le redo qui décrit ce qui doit être modifié dans les bloc d’undo.
- Lors de la mise à jour de la table et des index, chaque change vector qui décrit la modification est écrit dans le buffer de redo privé.
- En même temps, les change vector qui décrivent le undo record correspondant sont écrits dans le buffer de « undo » privé.
- Le nombre total de change vectors, et leur contenu sont exactement les mêmes que pour les change vectors traditionnels.
- Au commit, oracle concatène ces 2 buffers pour faire un seul redo record et l’écrit dans le tampon de journalisation normal (redo log buffer)
- En même temps, ces 100 change vectors sont appliqués: 10 sur la table, 10 sur chaque index, et 50 sur les blocs d’undo. Et en dehors de cela, tout ce qui doit se faire lors d’un commit s’applique aussi.
- Le nombre de modification de blocs (« db block changes ») reste le même dans tous les cas
- La différence la plus significative dans le volume de redo généré vient de l’entête du redo record qui fait 12 octets. Avec la gestion ‘in-memory’ de l’undo il n’y qu’un seul redo record, donc un header de 12 octets, alors que la méthode traditionnelle en génère 50, donc 50*12=600 octets.
Il y a de nombreux détails et variations autour de ce qui se passe là . Par exemple au début et à la fin de la transaction, ou lorsque un des deux buffers est plein (puisqu’ils ne font que 64Ko ou 128Ko) mais la description faite ci-dessus couvre les différences essentielles.
Question: Supposons que je démarre l’instance et effectue quelques mises à jour. J’ai donc un buffer privé de redo et un buffer privé de undo, créés en shared pool. Immédiatement après le système se plante et rien n’est encore écrit dans les fichiers de redo ni dans les blocs d’undo. Dans cette situation comment fait Oracle pour récupérer les données d’undo ?
Il y a deux chose que vous devez prendre en compte dans ma description:
- la précision: ‘Il y a de nombreux détails et variations’
- la partie qui montre que les modifications faites dans les blocs tables et index est tout à la fin.
Si la session a fait un commit, elle a écrit le redo privé dans le redo thread public, qui doit être écrit sur disque avant que le commit ne soit terminé. Donc il n’y a rien de différent au niveau du recovery.
Maintenant, si la session n’a pas encore fait de commit, alors du point de vue des autres utilisateurs, rien ne s’est encore passé (ils ne sont censés voir que les effets des transactions commitées). Du coup, cela n’a pas d’importance que les redo et undo privés aient disparu.
Mais voici où ca devient plus complexe: Comment les autres sessions voient que vous êtes en train de modifier les mêmes blocs qu’elles, si vous ne les mettez à jour que lorsque vous faites le commit de votre transaction ? Comment Oracle fait pour minimiser le temps que prennent toutes les modifications de blocs qui doivent être faites lors du commit ? J’ai quelques réponses à ces questions, mais elles ne sont ni exactes, ni complètes, alors je ne préfère pas les publier.
Cependant, un point clé de ce mécanisme, c’est le fait qu’il ne s’applique qu’à des petites transactions. Les zones privées ne font que 64Ko ou 128Ko suivant qu’on est en 32 ou 64 bits, et dès que la transaction devient trop grande, Oracle les écrit dans les redo buffer et poursuit avec le mécanisme normal.
C’est bien le point qui est soulevé par Jonathan Lewis.
Contrairement au mécanisme classique, avec le mécanisme de journalisation en mémoire, pour des transactions courtes, c’est au moment du commit, aprés avoir écrit les redo records dans le redo log ‘normal’, que les change vectors sont appliqués sur les blocs.
Hmmm, ce point me pose problème :
– change vector et undo change vector (redo) : re-jouer les modifications (RMAN, flashback, dataguard, …)
– undo record : lecture consistente et ROLLBACK
Quand tu une transaction modifie des données, elle écrit directement dans les blocs même avant le commit (avec les ITL, l’undo et tout), et le COMMIT n’est pas sensé appliquer des change vectors… (c’est juste sensé faire un flush de la redo log).
Enfin, il me semble…
Salut,
Oui, ce n’est que du redo: redo des blocs d’undo dans le ‘buffer de undo’ et redo des blocs de données dans le ‘buffer de redo’
Donc ce mécanisme concerne aussi les blocs de données. Et la modification des blocs de données (tables et index) se fait tout à la fin aussi, cf l’étape:
Le problème soulevé est que avec cette description, une session concurrente ne verra pas que notre session a vérouillé un enregistrement, puisque le verrou est écrit dans le bloc. Et pourtant il faut que les sessions concurrentes le voient si elles cherchent à modifier le même enregistrement. Elles peuvent ignorer les modifications non commitées, mais elles doivent voir le lock flag et l’entrée ITL correspondante.
Merci pour l’encouragement. Je n’ai pas eu trop le temps de rajouter d’autres traduction ces derniers temps
Franck.
Salut Franck,
Sympa de traduire les articles des gros experts
Je suis pas un expert, mais si j’ai bien compris, la buffer privé ne concerne que le REDO. C’est pour ça qu’il y a la précision :
=> Seul les redo vectors décrivant les modifications des blocs d’undo sont « invisibles »
=> Les undo records quant à eux, sont bien écrits dans l’undo tablespace, et rentrent donc dans la mécanique générale de la lecture consistente.
Voilà , voilà , j’espère ne pas dire de conneries…
Continue comme ça, c’est cool ce que tu fais ! (et puis ça doit te permettre de consolider pleins de connaissances super pointues)
Pacmann