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.
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.
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é.
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.
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.
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.
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.
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.
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.
- ▸ début
- lire Ada
- écrire Ada = 70
- valider
- ▸ début
- lire Ada
- écrire Ada = 80
- valider
| objet | valeur validée | en attente dans T1 | en attente dans T2 |
|---|---|---|---|
| Ada | 100 | · | · |
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.
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.
- ▸ début
- écrire Ada = 70
- annuler
- ▸ début
- lire Ada
- valider
| objet | valeur validée | en attente dans T1 | en attente dans T2 |
|---|---|---|---|
| Ada | 100 | · | · |
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
- ▸ début
- écrire Ada = 70
- valider
- ▸ début
- lire Ada
- lire Ada
- valider
| objet | valeur validée | en attente dans T1 | en attente dans T2 |
|---|---|---|---|
| Ada | 100 | · | · |
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.
| niveau | lecture sale | lecture non répétable | mise à jour perdue |
|---|---|---|---|
| lecture non validée | possible | possible | possible |
| lecture validée | impossible | possible | possible |
| lecture répétable | impossible | impossible | possible |
| sérialisable | impossible | impossible | impossible |
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.
- ▸ début
- lire Ada
- écrire Ada = 70
- valider
- ▸ début
- lire Ada
- écrire Ada = 80
- valider
| objet | valeur validée | en attente dans T1 | en attente dans T2 |
|---|---|---|---|
| Ada | 100 | · | · |
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.
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.
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.
- Un rapport additionne les ventes de janvier, puis celles de l'année, et le total annuel est inférieur à celui de janvier.
- Un client voit s'afficher une commande qui disparaît quand il rafraîchit la page.
- 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
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.
- 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.
- Entourer d'un
BEGINet d'unCOMMITtout ce qui doit tenir ensemble, et rien de plus : une transaction longue garde ses verrous longtemps. - Prévoir le
ROLLBACK. Une erreur n'annule pas la transaction toute seule. Le programme doit l'attraper. - 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.
- 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.
BEGINouvre,COMMITvalide,ROLLBACKannule. 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
ROLLBACKexplicite, 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.