Cet article est la traduction d’un article de Jonathan Lewis publié sur son blog. L’article original en anglais se trouve ici.
L’article explique à quoi correspondent exactement ‘db block gets‘ et ‘consistent gets‘ que l’on retrouve dans les statistiques de performance à différents endroits: autotrace, statspack, AWR, V$SESSTAT
Dans tkprof, on retrouve ‘db block gets’ dans la colonne ‘current‘ (version courante du bloc) et ‘consistent gets’ dans la colonne ‘query‘ (version consistante par rapport au début de la requête)
Dans V$SQL on retrouve leur somme, sans les différencier, dans la colonne BUFFER_GETS. Leur somme est aussi souvent appelée ‘logical I/O‘ (LIO) ou ‘Lectures en mémoire tampon‘ sous OEM et s’incrémente à chaque fois qu’oracle va lire un bloc en buffer cache en passant par le ‘cache buffer chain‘, donc en nécessitant un latch – verrou sur cette structure mémoire.
‘db block gets‘ et ‘consistent gets‘ sont deux contextes différents de ces lectures logiques: version courante ou version consistante du bloc.
Comment décrire ‘db block gets’ and ‘consistent gets’ en quelques lignes ?
Ayant posé la question sur mon blog je suppose que je dois donc donner ma version de la réponse.
Avant de donner ma définition, je voudrais insister sur le fait qu’il existe au moins 2 autre mécanismes utilisés par Oracle pour visiter les blocs de données du buffer cache:
- revisiter un buffer déjà épinglé (pinned buffer). Déjà épinglé signifie qu’il n’est pas nécessaire d’acquérir un nouveau latch pour aller le revisiter.
- et faire un nettoyage rapide (fast cleanout) au moment du commit.
Ces visites sont enregistrées respectivement dans les statistiques ‘buffer is pinned count‘ et ‘commit cleanouts successfully completed‘. (La statistique ‘commit cleanouts‘nous dit combien de fois Oracle a essayé de faire un nettoyage au commit, et il existe divers autres statistiques pour expliquer la raison des échecs).
Lorsque j’ai posé la question au départ, j’ai précisé qu’il était possible qu’Oracle modifie un bloc sans enregistrer aucun type de ‘get’ et sans générer aucun undo ou redo.
Il s’agit du cas du ‘commit cleanout‘, nettoyage rapide du bloc lors du commit où seule l’entrée ITL de l’entête de bloc est touchée.
Mais pour être précis, le nettoyage (cleanout) complet du bloc est effectué par la prochaine personne qui va mettre à jour ce bloc, et c’est cette prochaine personne qui va générer de l’undo et du redo correspondant – et comptabiliser un ‘db block get‘.
Donc techniquement, vous pouvez dire que le ‘commit cleanout‘ est la source quelque undo et de redo, même si leur génération est reportée (delayed block cleanout) à plus tard.
Plus de détail sur commit cleanout ici
Revenons donc au sujet:
‘db block get‘ est incrémenté lorsque Oracle doit voir la dernière version du bloc, avec toutes les modifications, même celles qui ne sont pas encore commitées. Cependant les ‘commit cleanouts‘ ne sont pas considérés comme des ‘db block gets‘ même si ils concernent la dernière version du bloc.
Les deux raisons les plus courantes du ‘db block get‘ sont:
- l’accès à un bloc pour en modifier les des données. On ne peut faire des modifications que qur la dernière version du bloc.
- L’accès à un bloc d’index pour vérifier l’unicité et les contraintes d’intégrité référentielles. En interne, votre session doit voir les modifications faites par les autres sessions même si elles ne sont pas commitées (bien que vous, utilisateur final, n’avez pas le droit de voir ces données non commitées). Cela est nécessaire pour qu’Oracle empêche de faire toute modification qui violerait une contrainte lorsque l’autre session fera un commit. Ce conflit est souvent la cause d’un verrou (enqueue) TX en mode 4
‘consistent get‘ est un autre type de visite de bloc protégé par un latch (on a vu que ‘pinned buffer’, qui ne nécessite pas de nouveau latch, n’est pas comptabilisé comme un consistent get) mais qui n’a pas besoin de voir la dernière version du bloc.
Il y a deux cas pour cette lecture consistante:
- l’accès à un bloc d’undo (rollback segment) pour lire les enregistrements d’undo nécessaires à la construction d’une version antérieure d’un bloc de donnée pour la lecture consistante
- l’accès à la version d’un bloc de donnée ne montrant que les changements qui ont été commités a un certain instant dans le passé (instant indiqué par le System Change Number – SCN)
Bien sûr, pour avoir un simple aperçu de ce qui se passe, les définitions traditionnelles sont correctes: un ‘db block get‘ accède à la dernière version d’un bloc, un ‘consistent read‘ utilise l’undo pour produire une précédente version du bloc.
Mais il est préférable d’être plus précis lorsqu’on on voit des chiffres inattendus dans un rapport Statspack ou AWR.
Voici par exemple une situation à laquelle on ne pense pas toujours. Une lecture consistante (‘consistent read’) peut voir un bloc tel qu’il n’a jamais existé dans le passé.
Prenons le scénario suivant:
- La session 1 fait un update de la ligne 1 dans un bloc, mais ne fait pas encore de commit
- La session 2 fait un update de la ligne 2 du même bloc, et fait un commit
- La session 3 lit ce bloc
La session 3 doit alors appliquer les enregistrements d’undo sur une copie du bloc afin de rollbacker l’update de la ligne 1. Par contre, cette lecture consistente prend en compte le changement effectué sur la ligne 2. Le bloc est donc un état qui n’a jamais existé dans le passé: ligne 2 modifiée mais ligne 1 non modifiée.
Lorsque l’on saisit la conséquence de ce scénario, on comprend pourquoi il est possible de voir des valeurs très élevées pour ‘consistent gets’ quand plusieurs processus mélangent des longues transactions avec des transactions courtes, et qui ne concernent qu’une faible portion des données. Ces processus sont constamment en train de lire les segments d’undo pour créer des versions de blocs dans lequels les modifications faites par les autres sessions ne sont pas visibles. Cela devient particulièrement sérieux lorsque le code fait un ‘select for update’ suivi un peu plus loin par un update.
Un dernier point. Si vous sur le point de modifier un bloc, vous le prenez avec un ‘db block get’ pour être sûr d’avoir la dernière version. Mais avant de le modifier, vous devez créer une version consistante pour vérifier que:
- les lignes que vous allez modifier existaient au moment où vous avez démarré l’update
- il n’y a aucune ligne ‘manquante’ qui était dans la cible lorsque vous avez démarré l’update, mais qui n’est plus dans le bloc courant car elle a été supprimée ou modifiée par une autre transaction (qui a commitée ou non) entre temps.
Lorsque on commence ç réfléchir à la lecture consistante (read-consistency) on se demande parfois comment les magiciens d’Oracle Corporation on réussi à faire fonctionner tout cela.
Merci beaucoup pour ces explications, ça m’a bcp aidé. Bon courage pour la suite. ^_^
Un grand merci à Mohamed Houri qui a fait une relecture de cet article, avec des corrections et améliorations.
Franck Pachot