• Aucun résultat trouvé

Le contrôle de concurrence est la partie de l’activité d’un SGBD qui consiste à ordonner l’exécution des transactions de manière à éviter les anomalies présentées précédemment.

Il existe deux types de méthodes pour contrôler la concurrence :

1. Contrôle continu : au vérifie au fur et à mesure de l’exécution des opérations que le critère de sérialisabilité est bien respecté. Ces méthodes sont dites pessimistes : elles reposent sur l’idée que les conflits sont fréquents et qu’il faut les traiter le plus tôt possible.

2. Contrôle par certification : cette fois on se contente de vérifier la sérialisabilité quand la transaction s’achève. Il s’agit d’une approche dite optimiste : on considère que les conflits sont rares et que l’on peut accepter de réexécuter les quelques transactions qui posent problème.

La première approche est la plus fréquente. Le mécanisme adopté est celui du verrouillage. L’idée est simple : on bloque l’accès à une donnée dès qu’elle est lue ou écrite par une transaction (“pose de verrou”) et on libère cet accès quand la transaction termine par commit ou rollback (“libération du verrou”). Reprenons l’exemple 11.1, et supposons que tout accès en lecture ou en écriture pose un verrou bloquant les autres transactions. Clairement les transactionsãžæ etã

Ì

s’exécuteront en série et les anomalies disparaîtront. Le bloquage systématique des transactions est cependant une contrainte trop forte, comme le montre l’exemple 11.3. L’exécution est correcte, mais le verrouillage total bloquerait pourtant sans nécessité la transactionã

Ì

.

On doit donc trouver une solution plus souple. On peut en fait considérer deux critères : le degré de restriction, et la granularité du verrouillage (i.e. le niveau de la ressource à laquelle il s’applique : tuple, page, table, etc). Il existe essentiellement deux degrés de restriction :

1. Le verrou partagé est typiquement utilisé pour permettre à plusieurs transactions concurrentes de

lire la même ressource.

2. Le verrou exclusif réserve la ressource en écriture à la transaction qui a posé le verrou.

Ces verrous sont posés de manière automatique par le SGBD en fonction des opérations effectuées par les transactions/utilisateurs. Mais il est également possible de demander explicitement le verrouillage de certaines ressources.

Il est important d’être conscient d’une part de la politique de verrouillage pratiquée par un SGBD, d’autre part de l’impact du verrouillage explicite d’une ressource. Le verrouillage influence considérable- ment les performances d’une BD soumises à un haut régime transactionnel.

11.2.1

Verrouillage à deux phases

Le verrouillage à deux phases est le protocole le plus répandu pour assurer des exécutions concurrentes correctes. On utilise des verrous en lecture qui seront notés rl (comme read lock) dans ce qui suit, et des verrous en écritures notés wl (comme write lock). Donc»?Éxï|°ð¥´ indique par exemple que la transaction º a posé un verrou en lecture sur la resourceð . On notera de même»<Ç etéÇ le relâchement des verrous (read

unlock et write unlock).

Le principe de base est de surveiller les conflits entre deux transactions sur la même ressource.

Definition 11.1 Deux verrousÆ6Éï °ðP´ etÊ?É ñ1°ò_´ sont en conflit siðZ½ƒò ,ºSó½‰ô etÆ6É ouÊ?É est un verrou en

Le respect du protocole est assuré par un module dit scheduler qui reçoit les opérations émises par les transactions et les traite selon l’algorithme suivant :

1. Le scheduler reçoitÆaï°ðP´ et consulte le verrou déjà posé surð ,Ê?Éñ °ðP´, s’il existe. – siÆ6É

ï

°ðP´ est en conflit avecÊ?É ñ1°ðP´,Æ

ï

°ð¥´ est retardée et la transactionã

ï est mise en attente. – sinon,ã ï obtient le verrouÆ6Éï °ðP´ et l’opérationÆ ï °ðP´ est exécutée.

2. Un verrou pourÆ ï °ð¥´ n’est jamais relâché avant la confirmation de l’exécution par un autre module, le gestionnaire de données GD.

3. Dès queã ï relâche un verrou, elle ne peut plus en obtenir d’autre.

Le terme “verrouillage à deux phases” s’explique par le processus détaillé ci-dessus : il y a d’abord

accumulation de verrou pour une transactionã , puis libération des verrous.

Theorem 1 Toute exécution obtenue par un verrouillage à deux phases est sérialisable.

De plus on obtient une exécution stricte en ne relachant les verrous qu’au moment du commit ou du

rollback. Les transactions obtenues satisfont les propriétés ACID.

Il est assez facile de voir que ce protocole garantit que, en présence de deux transactions en conflitãæ et

ã

Ì

, la dernière arrivée sera mise en attente de la première ressource conflictuelle et sera bloquée jusqu’à ce que la première commence à relâcher ses verrous (règle 1). A ce moment là il n’y a plus de conflit possible puisqueã æ ne demandera plus de verrou (règle 3).

La règle 2 a principalement pour but de s’assurer de la synchronisation entre la demande d’exécution d’une opération, et l’exécution effective de cette opération. Rien ne garantit en effet que les exécutions vont se faire dans l’ordre dans lequel elles ont été demandées.

Pour illustrer l’intérêt de ces règles, on peut prendre l’exemple suivant :

Exemple 11.9 (Non-respect de la règle de relâchement des verrous). Soit les deux transactions suivantes :

1. Tæ: ræ [x]õ wæ [y]õ cæ 2. T Ì : w Ì [x]õ w Ì [y]õ c Ì

et l’ordre d’exécution suivant : ræ [x] w

Ì

[x] w

Ì

[y] c

Ì

wæ [y] cæ . Supposons que l’exécution avec pose et

relâchement de verrous soit la suivante : rlæ [x] ræ [x] ruæ [x] wl Ì [x] w Ì [x] wl Ì [y] w Ì [y] wu Ì [x] wu Ì [y] c Ì

wlæ [y] wæ [y] wuæ [y] cæ

On a violé la règle 3 :ãæ a relaché le verrou surð puis en a repris un surò . Un “fenêtre” s’est ouverte qui a permis aã

Ì

de poser des verrous surð etò . Conséquence : l’exécution n’est plus sérialisable carã

Ì

a écrit surãæ pourð , etãæ a écrit surã

Ì pourò (ræ [x]ö w Ì [x] et w Ì [y]ö wæ [y]). Reprenons le même exemple, avec un verrouillage à deux phases :

Exemple 11.10 (exécution correcte)

»?ɁæW°ðP´÷»<æA°ð¥´éÉ Ì °ðP´÷ã Ì ¼'¾hµ_¹¹:¼'¾À¹:¼ éɁæA°ò1´÷éæW°ò_´¬/晻<ÇÀæW°ð¥´éÇÀæA°ò_´÷éÉ Ì °ð¥´é Ì °ð¥´éÉ Ì °ò1´÷é Ì °ò_´¬ Ì éGÇ Ì °ðP´÷éGÇ Ì °ò_´ Le verrouillage à deux phases (avec des variantes) est utilisé dans tous les SGBD. Il permet en effet une certaine imbrication des opérations. Notons cependant qu’il est un peu trop strict dans certains cas : voici l’exemple d’une exécution sérialisable

Exemple 11.11 Une exécution sérialisable impossible par verrouillage

ræ [x] w

Ì

[x] c

Ì

wø [y] cø ræ [y] wæ [z] cæ

Le principal défaut du verrouillage à deux phases est d’autoriser des interblocages : deux transactions concurrentes demandent chacune un verrou sur une ressource détenue par l’autre. Voici l’exemple type :

Exemple 11.12 Les deux transactions sont les suivantes :

ãžæú»?æ?°ðP´ÀõûéGæA°ò1´Àõû¬/æ ã Ì ú» Ì °ò1´Àõüé Ì °ðP´žõû¬ Ì

Considérons maintenant queãæ etã

Ì

s’exécutent en concurrence dans le cadre d’un verrouillage à deux phases : »?ɁæA°ð¥´»?æW°ðP´÷»?É Ì °ò_´» Ì °ò_´éɁæW°ò_´÷ãæý¼/¾Oµ_¹¹:¼'¾À¹:¼ éÉ Ì °ð¥´ã Ì ¼'¾Tµ1¹¹:¼'¾À¹:¼ ãæ etã Ì

sont en attente l’une de l’autre : il y a interblocage (deadlock en anglais).

Cette situation ne peut pas être évitée et doit donc être gérée par le SGBD : en général ce dernier maintient un graphe d’attente des transactions et teste l’existence de cycles dans ce graphe. Si c’est le cas, c’est qu’il y a interblocage et une des transactions doit être annulée et ré-éxécutée. Autre solution : tester le temps d’attente et annuler les transactions qui dépassent le temps limite.

Notons que le problème vient d’un accès aux mêmes ressources, mais dans un ordre différent : il est donc bon, au moment où l’on écrit des programmes, d’essayer de normaliser l’ordre d’accès aux données.

A titre d’exercice, on peut reprendre le programme de réservation donné initialement, mais dans une version légèrement différente :

ProgrammeRESERVATION2

Entrée : Une séance×

Le nombre de places souhaitéØÙ¶zÚɁµ8¬™¼?× Le client¬

debut

Lire la séance×

si (nombre de places libresۀØܶzÚÉ[µ_¬–¼<× )

Lire le compte du spectateur¬

Débiter le compte du client

SoustraireØÙ¶zÚɁµ8¬™¼?× au nombre de places vides

Ecrire le compte du client¬

Ecrire la séance×

finsi fin

Exercice : donner une exécution concurrente de RESERVATION et de RESERVATION2 qui aboutisse à un interblocage.

Dès que 2 transactions lisent la même donnée avec pour objectif d’effectuer une mise-à-jour ulté- rieurement, il y a potentiellement interblocage. D’où l’intérêt de pouvoir demander dès la lecture un ver- rouillage exclusif (écriture). C’est la commandeSELECT ... FOR UPDATEque l’on trouve dans cer- tains SGBD.

Autre solution pour fluidifier les transactions : ne pas poser de verrou en lecture (modeREAD COMMITTED en SQL2). Cette solution affecte l’isolation entre deux transactions.

Exemple 11.13 Toujours avec le programmeÝßÞáàJÞSÝâÍãä8åßØ . Chaque programme veut réserver des

places dans la même séance, pour deux clients distincts¬'æ et¬

Ì . »?æ?®[×<¯:»<æ1®¬/斯» Ì ®è×'¯:» Ì ®¬ Ì ¯éæA®[×<¯é Ì ®[×<¯é Ì ®¬ Ì ¯éæA®¬/æ/¯î^æ/î Ì

11.2.2

Contrôle par estampillage

C’est une méthode beaucoup plus simple qui consiste à fixer, a priori, l’ordre de sérialisabilité des transactions soumises au scheduler. Pour cela, on affecte à chaque transaction une estampille. Chaque valeur d’estampille est unique et les valeurs sont croissantes : on garantit ainsi un ordre total entre les transactions.

Quand un conflit survient entre‡Æ6ï etÂ‡Æ ñ , on vérifie simplement que l’ordre du conflit est compatible avec l’ordre des transactions (i.e.ºCöZôƒþ.ÿ ‡Æaï õû‡Æ

ñ ). Sinon on rejette la transaction qui veut effectuer l’opération conflictuelle.

Sur l’exemple suivant :

»<æA®[×<¯:é

Ì

°ðP´÷»/ø_°ð¥´»

Ì

°ðP´÷éæA°ðP´

en admettant que l’estampille est le numéro de la transaction, on rejette la transation ãæ au moment de éæW°ðP´.

Méthode peu utilisée car elle entraîne des abandons inutiles de transactions, et le risque de privation : une transaction est toujours rejettée.