Aller au contenu principal

Les transactions

Ce que ce chapitre apporte

  • Écrire une transaction avec BEGIN, COMMIT et ROLLBACK, et dire ce qui est visible entre les deux.
  • Nommer les quatre garanties d'ACID et dire contre quel accident chacune protège.
  • Reconnaître trois anomalies de concurrence, et les provoquer soi-même.
  • Choisir un niveau d'isolation en sachant ce qu'il interdit et ce qu'il coûte.
  • Expliquer pourquoi une erreur au milieu d'une transaction ne l'annule pas toute seule.
Un virement, c'est deux ordres : retirer d'un compte, ajouter à l'autre. Entre les deux, la machine peut s'éteindre, le réseau peut lâcher, et un autre client peut lire des soldes dont la somme ne tombe plus juste. Tout ce qui a été vu jusqu'ici sait interroger une base ; ce chapitre porte sur ce qui en fait une base plutôt qu'un fichier partagé : la garantie que le travail se fait entièrement ou pas du tout, et qu'il se fait sans que les autres regardent au mauvais moment.

Le problème, en deux ordres

Voici un virement écrit sans précaution. La table porte une contrainte qui interdit un solde négatif, et c'est déjà une protection, mais elle ne protège que chaque ordre pris isolément.

requete.sql
Résultat
>_ Prêt à exécuter…

Le résultat est juste. Mais il l'est par chance : entre les deux UPDATE, il existe un instant où trente euros n'appartiennent à personne. Si la machine s'arrête à cet instant précis, la somme des soldes a diminué de trente, et rien dans la base ne permet de savoir ce qui s'est passé.

Définition

Une transaction est un groupe d'ordres que la base traite comme un seul. Soit ils prennent tous effet, soit aucun ne prend effet. Il n'y a pas d'état intermédiaire durable, et aucun autre client ne verra le milieu.

Tout ou rien

Trois mots suffisent. BEGIN ouvre la transaction, COMMIT la valide, ROLLBACK l'annule. Le bloc ci-dessous fait le virement, regarde les soldes à l'intérieur de la transaction, puis annule tout et regarde de nouveau.

requete.sql
Résultat
>_ Prêt à exécuter…

Deux tableaux, deux réponses différentes à la même question. C'est le point à retenir : à l'intérieur d'une transaction, une lecture voit son propre travail, et personne d'autre ne le voit. Tant que COMMIT n'a pas été écrit, rien n'existe pour les autres.

Le même virement, validé cette fois.

requete.sql
Résultat
>_ Prêt à exécuter…

ACID, quatre garanties et quatre accidents

Le sigle se récite souvent sans qu'on dise contre quoi chaque lettre protège. Ce sont quatre accidents différents.

Définitions

L'atomicité protège de l'interruption : la transaction s'applique entièrement ou pas du tout. C'est ce que ROLLBACK rend visible.

La cohérence protège des états impossibles : une transaction fait passer la base d'un état valide à un autre état valide. Ce sont les contraintes qui la définissent, comme le CHECK (solde >= 0) ci-dessus.

L'isolation protège de la concurrence : une transaction se déroule comme si elle était seule. C'est la garantie la plus coûteuse, et la seule qui se règle par degrés. Toute la suite du chapitre porte sur elle.

La durabilité protège de la panne : une fois validée, une transaction survit à une coupure de courant. Elle repose sur l'écriture d'un journal sur disque avant que la validation ne soit annoncée.

Laquelle se règle, et laquelle ne se règle pas
Trois de ces quatre lettres sont tout ou rien : une base est atomique, cohérente et durable, ou elle ne l'est pas. L'isolation, elle, se choisit, parce qu'elle se paie en attente et en refus. C'est pour cette seule raison que les niveaux d'isolation existent.

Une erreur n'annule rien toute seule

Voici un virement dont le second ordre viole la contrainte. Le bloc s'arrête là, et c'est ce qu'il faut observer.

requete.sql
Résultat
>_ Prêt à exécuter…
L'ordre fautif échoue, la transaction reste ouverte
Le message d'erreur signale que la contrainte a refusé le second ordre. Mais la transaction, elle, n'a pas été annulée : le premier retrait de trente euros est toujours en attente, et le COMMIT de la ligne suivante n'a jamais été atteint parce que le bloc s'est interrompu.
Une erreur de contrainte annule l'ordre, pas la transaction. C'est au programme qui pilote la base d'attraper l'erreur et d'écrire ROLLBACK. Un programme qui ne le fait pas laisse une transaction ouverte, qui garde ses verrous et bloque les autres.

Quand deux transactions se croisent

Le reste du chapitre porte sur l'isolation, et il faut d'abord voir pourquoi elle est nécessaire.

Une anomalie de concurrence a une propriété désagréable pour qui veut l'enseigner : elle ne se produit qu'à un entrelacement précis, une fois sur mille, au hasard de l'ordonnanceur. On ne peut ni la montrer ni la reproduire sur une vraie base.

Dans les figures qui suivent, c'est le lecteur qui décide quelle transaction avance. L'anomalie cesse d'être une histoire qu'on raconte, et le niveau d'isolation se change en haut de la figure pour rejouer exactement le même ordre.

Deux guichets retirent chacun de l'argent du même compte. Chacun lit le solde, calcule, puis écrit.

niveau d'isolation
T1en attente
  1. début
  2. lire Ada
  3. écrire Ada = 70
  4. valider
T2en attente
  1. début
  2. lire Ada
  3. écrire Ada = 80
  4. valider
objetvaleur validéeen attente dans T1en attente dans T2
Ada100··
Rien ne s'est encore passé. Avancer les transactions dans l'ordre de son choix.
Avancer en alternance stricte, T1 puis T2, jusqu'au bout. Ada avait 100, deux retraits ont eu lieu, et il faut relever ce qu'il reste.

Quatre-vingts. Le premier retrait a disparu sans laisser de trace, parce que les deux transactions ont lu cent avant que l'une d'elles n'écrive. Chacune a calculé juste, à partir d'une valeur qui n'était déjà plus la bonne.

Il faut maintenant faire l'autre essai : reprendre la figure, avancer T1 entièrement avant de commencer T2. Le résultat tombe à cinquante, ce qui est le bon compte pour deux retraits. C'est la leçon centrale du chapitre : l'ordre en série est toujours juste, et c'est l'entrelacement qui abîme.

Définition

Une mise à jour perdue se produit quand deux transactions lisent la même donnée, la modifient chacune de leur côté, et valident : la seconde écrase la première, dont le travail disparaît.

Deux autres façons de se tromper

Lire ce qui n'existera jamais

Ici, T1 écrit puis se ravise et annule. Le niveau est réglé au plus bas.

niveau d'isolation
T1en attente
  1. début
  2. écrire Ada = 70
  3. annuler
T2en attente
  1. début
  2. lire Ada
  3. valider
objetvaleur validéeen attente dans T1en attente dans T2
Ada100··
Rien ne s'est encore passé. Avancer les transactions dans l'ordre de son choix.
Avancer en alternance stricte. T2 lit une valeur que T1 finit par annuler : cette valeur n'a jamais existé.

T2 a lu soixante-dix, une valeur qui n'a jamais été validée et qui n'existera jamais. Si T2 avait imprimé un relevé, ce relevé serait faux pour toujours.

Sur cette figure, il faut maintenant passer le niveau à « lecture validée » et rejouer le même ordre. L'anomalie disparaît, parce qu'à ce niveau une lecture ne voit que ce qui a été validé.

Lire deux fois et obtenir deux réponses

niveau d'isolation
T1en attente
  1. début
  2. écrire Ada = 70
  3. valider
T2en attente
  1. début
  2. lire Ada
  3. lire Ada
  4. valider
objetvaleur validéeen attente dans T1en attente dans T2
Ada100··
Rien ne s'est encore passé. Avancer les transactions dans l'ordre de son choix.
Avancer en alternance stricte. T2 pose deux fois la même question, et n'obtient pas deux fois la même réponse.

T2 n'a rien fait de mal : elle a lu deux fois le même compte, et la base lui a donné deux valeurs différentes. Pour un rapport qui additionne des colonnes en plusieurs passes, cela suffit à produire un total qui ne tombe pas juste.

Là encore, passer le niveau à « lecture répétable » et rejouer. T2 obtient alors deux fois cent, parce que ce niveau lui donne une photographie de la base prise à sa première lecture.

Les quatre niveaux

Chaque niveau se définit par les anomalies qu'il tolère, et non par la façon dont il s'y prend : deux bases obtiennent le même niveau par des moyens différents, verrous d'un côté, versions de l'autre.

niveaulecture salelecture non répétablemise à jour perdue
lecture non validéepossiblepossiblepossible
lecture validéeimpossiblepossiblepossible
lecture répétableimpossibleimpossiblepossible
sérialisableimpossibleimpossibleimpossible

Ce tableau se récite mal et se vérifie bien : chacune de ses douze cases se contrôle sur les figures ci-dessus, en changeant le niveau et en rejouant le même ordre.

Le dernier niveau mérite d'être vu, parce qu'il ne se contente pas d'empêcher : il refuse.

niveau d'isolation
T1en attente
  1. début
  2. lire Ada
  3. écrire Ada = 70
  4. valider
T2en attente
  1. début
  2. lire Ada
  3. écrire Ada = 80
  4. valider
objetvaleur validéeen attente dans T1en attente dans T2
Ada100··
Rien ne s'est encore passé. Avancer les transactions dans l'ordre de son choix.
Le même double retrait qu'au début, au niveau le plus strict. Avancer en alternance stricte et observer ce qui arrive à la seconde validation.

T2 ne perd pas silencieusement : sa validation est refusée, parce que la valeur sur laquelle elle avait décidé a changé entre-temps. Le travail est perdu quand même, mais le programme l'apprend, et il peut recommencer sur la valeur à jour. C'est toute la différence entre une base qui se trompe en silence et une base qui dit non.

À retenir
Le niveau sérialisable ne rend pas les transactions plus rapides ni plus intelligentes. Il transforme une erreur silencieuse en un refus visible, que le programme doit savoir traiter en réessayant.

Le lecteur fantôme, que ces figures ne montrent pas

Il existe une quatrième anomalie classique, le lecteur fantôme : une transaction compte les lignes qui remplissent une condition, une autre en insère une qui la remplit aussi, et le second comptage trouve une ligne de plus. Aucune ligne déjà lue n'a changé, et pourtant la réponse change.

Les figures de ce chapitre ne peuvent pas la produire, et c'est assumé : elles manipulent des objets nommés un par un, alors que le fantôme suppose une lecture par condition, « toutes les lignes où le solde dépasse mille ». C'est aussi ce qui le rend plus coûteux à empêcher : il faut verrouiller non pas des lignes, mais l'intervalle où une ligne pourrait apparaître.

Ce que l'isolation coûte

Un niveau élevé se paie de deux façons, et le choix se fait entre les deux.

En attente. Une transaction qui doit garantir de ne pas voir bouger ses données fait patienter celles qui voudraient les modifier. Plus le niveau est élevé, plus les transactions s'attendent.

En refus. Un moteur qui préfère ne pas faire attendre refuse les validations en conflit, comme dans la figure précédente. Le programme doit alors gérer le refus, ce qui est du travail supplémentaire à écrire.

C'est pour cela que la plupart des bases se règlent par défaut sur la lecture validée, et non sur le niveau le plus sûr : c'est le premier niveau qui interdit de lire ce qui n'existe pas, et il ne fait presque jamais attendre personne. On ne monte au-dessus que pour les traitements où une incohérence coûte plus cher que la lenteur, un arrêté comptable par exemple.

Exercices type

Écrire le virement correctement. Reprendre le premier bloc de ce chapitre et l'entourer d'une transaction, de sorte qu'aucune interruption ne puisse faire disparaître les trente euros.

requete.sql
Résultat
>_ Prêt à exécuter…

Le total reste à cent cinquante, avant comme après : c'est l'invariant que la transaction protège.

Trouver l'ordre qui sauve la mise à jour. Sur la première figure de transactions, réglée sur « lecture validée », trouver un ordre d'avance qui donne cinquante plutôt que quatre-vingts. Il en existe plusieurs, et ils ont tous la même propriété.

Nommer l'anomalie. Pour chacun des trois cas suivants, dire laquelle des trois anomalies se produit, puis le vérifier sur les figures.

  1. Un rapport additionne les ventes de janvier, puis celles de l'année, et le total annuel est inférieur à celui de janvier.
  2. Un client voit s'afficher une commande qui disparaît quand il rafraîchit la page.
  3. Deux administrateurs ajoutent chacun cinq articles au stock, et le stock n'a augmenté que de cinq.

Compter les entrelacements. Deux transactions de quatre opérations chacune. Combien d'ordres d'exécution différents existe-t-il, si l'ordre interne de chaque transaction est fixé ? La réponse est le nombre de façons de choisir quatre positions parmi huit.

Vérification

Vérification rapideon peut se reprendre

1.Que voit une autre transaction pendant qu'une transaction a écrit sans valider ?

2.Une contrainte CHECK refuse un ordre au milieu d'une transaction. Que devient la transaction ?

3.Deux guichets retirent 30 d'un compte de 100, et le solde final est 70. Quel est le nom de ce défaut ?

4.Quel est le niveau d'isolation par défaut de la plupart des bases ?

5.Que fait le niveau sérialisable face à deux transactions en conflit ?

6.Laquelle des quatre lettres d'ACID se règle par degrés ?

La méthode

Devant un traitement qui touche plusieurs lignes ou plusieurs tables.

  1. Chercher l'invariant. Qu'est-ce qui doit rester vrai avant et après ? Ici, la somme des soldes. C'est ce que la transaction protège, et rien d'autre.
  2. Entourer d'un BEGIN et d'un COMMIT tout ce qui doit tenir ensemble, et rien de plus : une transaction longue garde ses verrous longtemps.
  3. Prévoir le ROLLBACK. Une erreur n'annule pas la transaction toute seule. Le programme doit l'attraper.
  4. Poser la question de la concurrence. Deux exécutions simultanées de ce traitement peuvent-elles se gêner ? Si une valeur est lue puis réécrite, la réponse est oui.
  5. Choisir le niveau en dernier, et le monter seulement si l'anomalie identifiée l'exige, en sachant qu'un niveau élevé demande de savoir réessayer.

Synthèse

  • Une transaction est un groupe d'ordres traité comme un seul : tout ou rien, et rien de visible au milieu.
  • BEGIN ouvre, COMMIT valide, ROLLBACK annule. Avant la validation, le travail n'existe que pour sa propre transaction.
  • ACID : atomicité contre l'interruption, cohérence contre les états impossibles, isolation contre la concurrence, durabilité contre la panne. Seule l'isolation se règle par degrés, parce que seule elle se paie.
  • Une erreur de contrainte annule l'ordre, pas la transaction : sans ROLLBACK explicite, elle reste ouverte et garde ses verrous.
  • Trois anomalies : la lecture sale voit ce qui sera annulé, la lecture non répétable obtient deux réponses à la même question, la mise à jour perdue écrase le travail d'une autre.
  • Le lecteur fantôme est la quatrième : il porte sur des lignes qui apparaissent, et coûte plus cher à empêcher parce qu'il faut verrouiller un intervalle.
  • Chaque niveau interdit une anomalie de plus. La lecture validée est le défaut presque partout ; le sérialisable transforme l'erreur silencieuse en refus visible, à charge pour le programme de réessayer.
  • L'exécution en série est toujours juste. C'est l'entrelacement qui abîme, et l'isolation n'est qu'un moyen de s'en rapprocher à un coût acceptable.