Latches & Enqueues /*+ 3. Global Enqueues */

Ce post termine la description des latches, enqueues et global enqueues sans entrer dans le détail de l’utilité ni du fonctionnement particulier de chacun d’entre eux . Si vous voulez plus de détails sur un latch ou un enqueue, ajoutez un commentaire avec le nom du latch ou de l’enqueue que vous aimeriez voir traité… mais avant d’adresser ces questions, répondons d’abord à : Qu’est-ce qu’un Global Enqueue ?

Pourquoi enqueues ne suffisent-il pas ?
Si les enqueues répondent parfaitement à l’ensemble des besoins complexes de gestion d’accès à des ressources des instances Oracle, ils ne permettent pas de gérer des accès concurrents entre plusieurs instances d’un environnement RAC. Les global enqueues sont donc simplement les enqueues généralisés pour plusieurs instances d’un RAC

Global Enqueue, comment ca marche ?
Les global enqueues sont des structures très semblables à celles des enqueues décrites dans le post précédent. Elles sont également située dans la Shared Pool. Toutefois :

  • Les structures des global enqueues sont distribuées entre plusieurs instances de manière a assurer une redondance suffisante des informations pour continuer quelque soit le nombre de serveurs qui « crash ».
  • Alors que les process mettent directement à jour les enqueues qu’ils utilisent, dans le cas des global enqueues, si les process manipulent les structures des global enqueues de l’instance sur laquelle ils s’exécutent, ils doivent faire appel à un mecanisme plus évolué pour manipuler les global enqueues des autres instances quand c’est nécessaire. Les process d’instances specifiques de RAC LMON (GES Monitor) et LMD (GES Daemon) permettent de mettre a jour les global enqueues des autres instances. L’ensemble de ces process est appelé Global Enqueue Services ou GES et doit faire l’objet d’une attention particulière dans le cas de RAC et notamment lorsque vous examinez les performances d’un système.

Vous voyez ci-dessous comment un global enqueue est constitue dans le cas d’un enqueue TM. Cela serait identique quelque soit le type d’enqueue.

  • Il existe un agorithme assez évolué en 10g qui permet de définir que pour chaque ressource, une instance qui sera dite le « Resource Master », conservera une copie complète de toutes les infomations de Global Enqueue. Cet algorithme tient compte des instances ouvertes, d’un algorithme de HASH pour répartir les « Resource Master » de manière équitable entre les noeuds. Cet algorithme tient également compte de l’activité précédente des instances (cf ce Post sur le blog de Fairlie Rego). Je n’irai pas plus loin dans cette description mais dans l’exemple ci-dessous l’instance 2 est le « Resource Master » de l’enqueue TM sur la table GARK.
  • Si un utilisateur fait une opération qui nécessite qu’un Global Enqueue TM soit posé sur cette table, alors une structure est associée dans la Shared Pool de l’instance à partir de laquelle l’enqueue est posé et (via le GES) sur l’instance « Resource Master »
  • Dans une seconde phase, imaginons qu’un utilisateur sur une autre instance réclame le même verrou exclusif sur la table GARK. Il crée alors une structure dans la shared pool qui contiendra l’image locale du Global enqueue et via le GES et l’instance « Resource Master », la ressource sera reclamée a l’instance qui est désormais propriétaire de la ressource du fait de son précédent lock sur la table. A vrai dire, je ne sais pas si les informations sont également conservée par le « Resource Master » mais, à condition d’avoir un RAC 3 instances ou plus, ce doit être assez facile de le déterminer avec les requêtes de la section suivantes.

Maintenant que vous avez vu la logique de fonctionnement, vous pouvez vous assurer que quelque soit la ou les instances qui seraient perdues, l’ensemble des informations de global enqueue des instances restantes sont toujours disponibles ! Redistribuer les global enqueues et les reconstruire constituent le remastering des ressources en cas de crash d’une ou plusieurs instances… Il est complété par un recover des instances ayant échouée. Avant de se quitter, pourquoi pas illustrer ce qui est présenté ci-dessus sur un cas réel ?

Un exemple concret

Apres la théorie, la pratique, voici une première requête qui montre les différentes zones de mémoire de la SGA, i.e. la Shared Pool, associées aux GES sur l’instance locale. Vous voyez en particulier l’espace occupé par les global enqueues surligné en rouge

select pool, name, bytes
from v$sgastat
where name like 'ges%';

POOL NAME BYTES
------------ -------------------------- ----------------------
shared pool ges enqueue cur. usage pe 12
shared pool ges deadlock xid hash tab 11036
shared pool ges recovery domain table 108
shared pool ges process hash table 8640
shared pool ges lmd process descripto 2684
shared pool ges process array 256000
shared pool ges enqueue max. usage pe 12
shared pool ges reserved msg buffers 1242532
shared pool ges scan queue array 108
shared pool ges resource hash seq tab 8192
shared pool ges resource pools 456
shared pool ges deadlock xid freelist 7040
shared pool ges regular msg buffers 441004
shared pool ges enqueues 1635568
shared pool ges res mastership bucket 3072
shared pool ges resource hash table 221184
shared pool ges ipc instance maps 384
shared pool ges big msg buffers 3979396
shared pool ges resource 1626072
shared pool ges enqueue multiple free 300
shared pool ges lms process descripto 2684
shared pool ges shared global area 22724

select RESOURCE_NAME,
CURRENT_UTILIZATION,
MAX_UTILIZATION
from v$resource_limit
where resource_name like 'g_s_%' escape '';

Pour ce qui suit, il serait beaucoup plus intéressant d’avoir 3 instances… malheureusement je n’ai pour l’instant pour mes tests qu’une configuration à deux noeuds… Désolé donc mais contentons nous de ça pour montrer quelques point interéssants. Pour commencer pour qu’aucune ressource ne soit en mémoire, arrêtez et redémarrer l’ensemble de vos instances après avoir créé la table GARK (Voir mon exemple précédent à propos d
es enqueues pour créer cette table). On verifiera d’abord qu’il n’y a aucun Global Enqueue associé a notre table GARK…

1- La requête suivante permet de trouver la valeur object_id de la table :

select object_id
from dba_objects
where object_name='GARK'
and OWNER='SYS'
and OBJECT_TYPE='TABLE';

OBJECT_ID
----------------------
11739

2- Remplacez la valeur de object_id avec votre valeur pour verifier qu’il n’y a aucun global enqueue en rêquetant la table GV$GES_ENQUEUE comme ci-dessous :

select inst_id,
substr(resource_name2,instr(resource_name2,',',1,2)+1, 2) CODE,
substr(resource_name2,1, instr(resource_name2,',',1,1)-1) ID1,
substr(resource_name2,instr(resource_name2,',',1,1)+1,
instr(resource_name2,',',1,2)
-instr(resource_name2,',',1,1)-1) ID2,
GRANT_LEVEL,
REQUEST_LEVEL,
STATE,
OWNER_NODE,
BLOCKED,
BLOCKER
from gv$ges_enqueue
where substr(resource_name2,instr(resource_name2,',',1,2)+1, 2)='TM'
and substr(resource_name2,1, instr(resource_name2,',',1,1)-1)
= to_char(11739);

INST_ID CODE ID1 ID2 GRANT_LEVEL REQUEST_LEVEL STATE OWNER_NODE
-------- ---- --------- ---- ----------- ------------- ------ ----------

0 rows selected

Ensuite, effectuez un simple select for update sur tous les noeuds dont vous disposez, vous constaterez que le nombre de global enqueue diffère selon que vous êtes sur le noeud master de la ressource ou non. Utilisez le script ci-dessous sur chacune des instances

select * from sys.gark
where id=1 for update;

select inst_id,
substr(resource_name2,instr(resource_name2,',',1,2)+1, 2) CODE,
substr(resource_name2,1, instr(resource_name2,',',1,1)-1) ID1,
substr(resource_name2,instr(resource_name2,',',1,1)+1,
instr(resource_name2,',',1,2)
-instr(resource_name2,',',1,1)-1) ID2,
GRANT_LEVEL,
REQUEST_LEVEL,
STATE,
OWNER_NODE,
BLOCKED,
BLOCKER
from gv$ges_enqueue
where substr(resource_name2,instr(resource_name2,',',1,2)+1, 2)='TM'
and substr(resource_name2,1, instr(resource_name2,',',1,1)-1)
= to_char(11739);

Entre parenthèses, si vous cherchez la signification des GRANTED_LEVEL, la voici ci-dessous :

KJUSERNL : Null
KJUSERCR : Row-S (SS)
KJUSERCW : Row-X (SX)
KJUSERPR : Share
KJUSERPW : S/Row-X (SSX)
KJUSEREX : Exclusive

Et voilà, vous pouvez également requêter GV$ENQUEUE pour vous persuader que si les Global Enqueues sont distribués, ce n’est pas le cas des enqueues… C’est presque tout ! Ah si : une dernière remarque, contrairement aux autres enqueues « les enqueues transaction (TX) n’ont vraiment de sens que si l’instance sur laquelle la transaction s’effectue est en vie. Pour cette raison, les enqueues TX n’ont pas de global enqueue correspondant »

Conclusion
Je souhaite que cette série vous ait apportee une vision plus claire du fonctionnement de ces mécanismes internes a RAC et aux bases de données Single instance. Si ça continue à vous intéresser, n’hésitez pas, encore une fois à ouvrir la discussion sur un latch ou un enqueue particulier : J’attends vos commentaires !

-GarK!