|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Vous êtes ici : Programmer > Firebird pour les experts - Episode 5 - Verrouillage et versions des enregistrements | |
Forum
|
Firebird pour les experts - Episode 5 - Verrouillage et versions des enregistrements par Ann Harrison publié chez IBPhoenix Research vendredi 22 juillet 2005, par Sébastien Blanchard Le contrôle de l’accès concurrentiel est le mécanisme qui permet aux utilisateurs de lire et d’écrire simultanément des données si chaque utilisateur a un accès complet à la base de données. Cet état est appelé "capacité de sérialisation". L’état de la base de données après qu’un groupe de transactions concurrentes soit terminé est le même que si chacune de ces transactions avaient été exécutées dans un ordre quelconque. Peu, voire aucun, système de base de données offre des transactions sérialisables en tant que mode par défaut. Encore récemment, le mécanisme d’accès concurrentiel le plus courant était le verrouillage. Naturellement, puisque les transactions sont des choses électroniques imaginaires, elles ne mettent pas réellement les verrous en laiton sur les clusters du disque. Cependant, le système de base de données impose une discipline concernant l’accès aux données ; chaque enregistrement d’une transaction est enregistré en mémoire et aucune transaction ne peut entrer en conflit avec un autre niveau d’utilisation. Les transactions posent des verrous lorsqu’elles accèdent aux données et n’enlèvent jamais de verrou tant qu’elles ne sont pas validées soit par un "COMMIT" soit par un "ROLLBACK". La stratégie qui consiste à verrouiller de manière incrémentale les enregistrements et à les déverrouiller simultanément lorsque la transaction se termine est appelée "verrouillage en deux phases".
Le verrouillageLes verrous d’écriture permettent d’éviter les écritures dites "sales". Dans un système basé sur le verrouillage pour gérer l’accès concurrentiel aux données, lorsqu’une transaction modifie, insert ou supprime un enregistrement, elle commence par le verrouiller. Les verrous d’écriture sont exclusifs et seulement une transaction peut poser un verrou d’écriture à un instant T. Ce seul verrou permet d’éviter à deux transactions de modifier un même enregistrement en même temps et il satisfait généralement le plus bas niveau de concurrence. Une écriture "sale" pourrait apparaître comme suit :
Si les deux mises à jour de l’enregistrement sont effectuées simultanément, le résultat sans verrou d’écriture serait que l’employé bénéficiera soit d’une augmentation de salaire, soit d’une promotion mais pas les deux. Les verrous de lecture Dans une base de données verrouillée, lorsqu’une transaction lit un enregistrement, elle pose un verrou de lecture sur cet enregistrement. Plusieurs verrous de lecture peuvent cohabiter ensemble mais ne peuvent cohabiter avec des verrous d’écriture. Les verrous de lecture permettent d’éviter les lectures dites "sales". Une lecture "sale" permet à une transaction de voir les résultats non validés ("uncommited") d’une transaction concurrente.
Lecture cohérente Une transaction active dans une base de données voit toujours le même état de la base de données, à l’exception des modifications qu’elle a pu apporter elle-même. Cet état est appelé "lecture cohérente" si, lorsqu’une transaction lit deux fois le même enregistrement, elle voit les mêmes données à l’exception de celles qu’elle a modifiées elle-même. Si une transaction lit l’ensemble des enregistrements d’une table à un instant T, elle verra exactement le même nombre d’enregistrements avec les mêmes contenus lors d’une relecture à un instant T+1, à l’exception des changements qu’elle a effectués elle-même. Les verrous de lecture et d’écriture seuls ne produisent pas de lectures cohérentes. Considérons l’exemple suivant :
Afin de s’assurer que ces lectures puissent être répétées, la transaction A doit verrouiller plus que les enregistrements existants, quelque chose de plus abstrait. Ces verrous abstraits sont appelés "verrous prédicats" ou "verrous d’existence" ; ce sont des verrous qui permettent d’ajouter quelque chose de nouveau à un ensemble de résultats, soit en insérant un nouvel enregistrement, soit en modifiant un enregistrement existant. Il répond ainsi au critère de cohérence d’un ensemble de données. Les verrous prédicats peuvent être implémentés comme des verrous sur les chemins d’accès aux enregistrements. Si le département est un champ indexé, la transaction A pourra poser un verrou de lecture sur la partie de cette index qui pointe sur les enregistrements associés au département Z. Ensuite, lorsque la transaction B voudra créer une nouvelle entrée possédant cet index, elle rencontrera un conflit de verrouillage et attendra que la transaction A se termine et enlève ses verrous. Si le département n’est pas un champ indexé, la transaction A posera un verrou sur toute la table des employés, empêchant ainsi d’ajouter des enregistrements à cette table. Aucun employé ne pourra être ni ajouté, ni modifié, ni supprimé tant que la transaction A ne sera pas terminée. Sérialisation des transactions Les verrous de lecture, d’écritures et de prédicats produisent des transactions sérialisables. Cependant, cela implique un certain nombre de tables verrouillées ainsi que certaines impasses ou controverses. Taille d’un table verrouillée Même si les verrous sont de petites choses temporaires, la lecture de plusieurs millions d’enregistrements implique la création de très nombreux verrous. C’est pour cette raison que de nombreux systèmes qui utilisent les verrous de lecture emploient des stratégies appelées "verrouillage par rétrogradation ou promotion". Controverse et impasses Une transaction importante qui utilise des verrous de lecture en deux phases peut facilement bloquer tous ceux qui souhaitent écrire dans la base de données. Inversement, ceux qui souhaitent écrire peuvent également poser des verrous qui bloquent des pages, impliquant des impasses. La conséquence est que les performances sont souvent plus mauvaises lorsque les transactions entrent en concurrence et utilisent le verrouillage à deux phases que lorsque les transactions étaient réellement exécutées une par une.
Contrôle de l’accès concurrentiel multi-versionsFirebird utilise comme stratégie les versions des enregistrements au lieu d’écrire des verrous et les logs des transactions. L’utilisation des versions des enregistrements dans les transactions est décrite ici. Verrous d’écriture - Ecriture "sales" Chaque version d’enregistrement est marquée avec la version de la transaction qui l’a créé. Toutes les transactions connaissent celles qui sont actives. Aucune transaction ne peut modifier ou supprimer un enregistrement dont la version de la transaction la plus récente n’est pas une transaction validée. Les écritures sales sont donc impossibles. Verrous de lecture - Lectures "sales" Etant donné que les enregistrements sont marqués avec leur version et que chaque transaction connaît les versions des transactions actives, aucune transaction ne peut lire une version d’un enregistrement créé par une transaction active. Les lectures "sales" sont donc impossibles. Lecture répétées Ci-dessous les différents modes de transaction. Firebird supporte trois modes orthogonaux :
Cet article décrit la seule transaction 100% Firebird : concurrency, wait, snapshot. Les transactions cohérentes verrouillent les tables et sont particulièrement ennuyeuses à décrire. Le mode "read commited" empêche les lectures répétées parce que dès que les données ont été validées, elles deviennent immédiatement accessibles aux transactions actives. Avec l’option "no wait", les transactions déclenchent une erreur dès qu’elles rencontrent le moindre conflit. Les transactions "no snapshot" lisent seulement la dernière version validée d’un enregistrement et ne sont utiles qu’avec le mode "read commited" (ndt : correspond à l’option "rec_version"). Une transaction en mode "concurrency", "wait", "snapshot" permet des lectures répétées. Lorsque la transaction démarre, elle crée une liste de toutes les transactions validées, et quand elle accède à un enregistrement, elle remonte dans l’historique de ses versions jusqu’à en trouver une dont le marqueur de transaction appartient à la liste des transactions validées. Les modifications faites par les transactions concurrentes sont ignorées. Sérialisation des transactions Contrairement aux systèmes basés sur des verrous, un système de contrôle des accès concurrentiels multi-générationnel permet des lectures répétées sans être complètement sérialisable. Voici deux anomalies qui affectent Firebird : * Echanges Un échange survient quand deux transactions utilisent des données provenant d’enregistrements différents et applique leurs modifications dans l’ordre inverse. Un exemple sera plus parlant : Le problème est de s’assurer que tous les employés ayant le même statut ont le même salaire, indépendamment de leur sexe. Une solution consiste à lire les enregistrements concernant les hommes pour chaque statut et de modifier les enregistrements concernant les femmes en utilisant les salaires des hommes. Une autre solution, plus économique pour l’entreprise consiste à modifier le salaire des hommes à partir de celui des femmes.
Le résultat est que la différence de salaires est inversée mais existe toujours. Ce problème n’arriverait pas si les deux transactions étaient actives séparément. Les transactions n’entrent pas en conflit parce que chaque enregistrement n’est modifié qu’une seule fois. Les modifications effectuées par la transaction A ne sont pas visibles parce que lorsque la transaction B essaie de lire un enregistrement qui a été modifié par A, elle lit automatiquement la dernière version validée, et inversement... La solution, quand on est au courant que cette erreur peut arriver, est de s’arranger pour bien choisir l’ordre dans lequel on copie les données d’un enregistrement vers l’autre. * Problèmes à l’insertion A l’insertion, des problèmes peuvent aussi apparaître quand deux transactions concurrentes modifient des données. Exemple : Chaque transaction n’a vu que ses propres modifications donc chaque comptage a ignoré les enregistrements insérés par l’autre transaction. Si les transactions avaient été exécutées en série, les résultats auraient été : La solution consiste à utiliser un index unique sur chaque colonne qui peut stocker le nombre (ou le maximum) des valeurs de la table. Les index uniques sont correctement exploités même lorsque les transactions impliquées ne peuvent pas voir les enregistrements des autres transactions. L’article original en langue anglaise se trouve à : http://ibphoenix.com/main.nfs ?a=ibphoenix&s=1122013419:87484&page=ibp_expert5 |
Dans la même rubrique :
Sur le web :
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Proposer un article | Nous contacter | Plan du site | Admin | Accueil |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||