Documentation PostgreSQL 9.2.24 > Langage SQL > Contrôle d'accès simultané > Vérification de cohérence des données au niveau de l'application | |
Verrouillage explicite | Avertissements |
Il est très difficile d'implémenter des règles de gestion sur l'intégrité des données en utilisant des transactions Read Committed parce que la vue des données est changeante avec chaque ordre, met même un seul ordre peut ne pas se cantonner à son propre instantané si un conflit en écriture se produit.
Bien qu'une transaction Repeatable Read ait une vue stable des données dans toute la durée de son exécution, il y a un problème subtil quand on utilise les instantanés MVCC pour vérifier la cohérence des données, impliquant quelque chose connu sous le nom de conflits lecture/écriture. Si une transaction écrit des données et qu'une transaction concurrent essaye de lire la même donnée (que ce soit avant ou après l'écriture), elle ne peut pas voir le travail de l'autre transaction. Le lecteur donne donc l'impression de s'être exécuté le premier quel que soit celui qui a commencé le premier ou qui a validé le premier. Si on s'en tient là, ce n'est pas un problème, mais si le lecteur écrit aussi des données qui sont lues par une transaction concurrente il y a maintenant une transaction qui semble s'être exécutée avant les transactions précédemment mentionnées. Si la transaction qui semble s'être exécutée en dernier valide en premier, il est très facile qu'un cycle apparaisse dans l'ordre d'exécution des transactions. Quand un cycle de ce genre apparaît, les contrôles d'intégrité ne fonctionneront pas correctement sans aide.
Comme mentionné dans Section 13.2.3, « Niveau d'Isolation Serializable », les transactions Serializable ne sont que des transactions Repeatable Read qui ajoutent une supervision no-bloquante de formes dangereuses de conflits lecture/écriture. Quand une de ces formes est détectée qui pourrait entraîner un cycle dans l'ordre apparent d'exécution, une des transactions impliquées est annulée pour casser le cycle.
Si le niveau d'isolation de transactions Serializable est utilisé pour toutes les écritures et toutes les lectures qui ont besoin d'une vue cohérente des données, aucun autre effort n'est requis pour garantir la cohérence. Un logiciel d'un autre environnement écrit pour utiliser des transactions serializable pour garantir la cohérence devrait « fonctionner sans modification » de ce point de vue dans PostgreSQL™.
L'utilisation de cette technique évitera de créer une charge de travail inutile aux développeurs d'applications si le logiciel utilise un framework qui réessaye le s transactions annulées pour échec de sérialisation automatiquement. Cela pourrait être une bonne idée de positionner default_transaction_isolation à serializable. Il serait sage, par ailleurs, de vous assurer qu'aucun autre niveau d'isolation n'est utilisé, soit par inadvertance, soit pour contourner les contrôles d'intégrité, en vérifiant les niveaux d'isolations dans les triggers.
Voyez Section 13.2.3, « Niveau d'Isolation Serializable » pour des suggestions sur les performances.
Ce niveau de protection de protection de l'intégrité en utilisant des transactions Serializable ne s'étend pour le moment pas jusqu'au mode standby (Section 25.5, « Hot Standby »). Pour cette raison, les utilisateurs du hot standby voudront peut-être utiliser Repeatable Read et un verrouillage explicite sur le maître.
Quand des écritures non-sérialisables sont possibles, pour garantir la validité courante d'un enregistrement et le protéger contre des mises à jour concurrentes, on doit utiliser SELECT FOR UPDATE, SELECT FOR SHARE, ou un ordre LOCK TABLE approprié. (SELECT FOR UPDATE et SELECT FOR SELECT ne verrouillent que les lignes retournées contre les mises à jour concurrentes, tandis que LOCK TABLE verrouille toute la table.) Cela doit être pris en considération quand vous portez des applications PostgreSQL™ à partir d'autres environnements.
Il est aussi important de noter pour ceux qui convertissent à partir d'autres environnements le fait que SELECT FOR UPDATE ne garantit pas qu'une transaction concurrente ne mettra pas à jour ou n'effacera pas l'enregistrement selectionné. Pour faire cela dans PostgreSQL™ vous devez réellement modifier l'enregistrement, même si vous n'avez pas besoin de modifier une valeur. SELECT FOR UPDATE empêche temporairement les autres transactions d'acquérir le même verrou ou d'exécuter un UPDATE ou DELETE qui modifierait l'enregistrement verrouillé, mais une fois que la transaction possédant ce verrou valide ou annule, une transaction bloquée pourra continuer avec son opération en conflit sauf si un réel UPDATE de l'enregistement a été effectué pendant que le verrou était possédé.
Les verrifications globales de validité demandent davantage de réflexion sous un MVCC non sérialisable. Par exemple, une application bancaire pourrait vouloir vérifier que la somme de tous les crédits d'une table est égale à la somme de tous les débits d'une autre, alors que les deux tables sont en cours de mise à jour. La comparaison des résultas de deux SELECT sum(...) successifs ne fonctionnera pas correctement en mode Read Committed, puisque la seconde requête incluera probablement les résultats de transactions pas prises en compte dans la première. Effectuer les deux sommes dans une seule transaction repeatable read donnera une image précise des effets d'uniquement les transactions qui ont validé avant le début de la transaction repeatable read $mdash; mais on pourrait légitimement se demander si la réponse est toujours valide au moment où elle est fournie. Si la transaction repeatable read a elle même effectué des modifications avant d'effectuer le test de cohérence, l'utilité de la vérification devient encore plus sujette à caution, puisque maintenant elle inclut des modifications depuis le début de la transaction, mais pas toutes. Dans ce genre de cas, une personne prudente pourra vouloir verrouiller toutes les tables nécessaires à la vérification, afin d'avoir une vision incontestable de la réalité courante. Un mode SHARE (ou plus élevé) garantit qu'il n'y a pas de changements non validés dans la table verrouillée, autres que ceux de la transaction courante.
Notez aussi que si on se fie au verrouillage explicite pour empêcher les mises à jour concurrentes, on devrait soit utiliser Read Committed, soit utiliser Repeatable Read et faire attenion à obtenir les verrous avant d'effectuer les requêtes. Un verrou obtenu par une transaction repeatable read guarantit qu'aucune autre transaction modifiant la table n'est en cours d'exécution, mais si l'instantané vu par la transaction est antérieur à l'obtention du verrou, il pourrait aussi précéder des modifications maintenant validées dans la table. Un instantané de transaction repeatable read est en fait figé à l'exécution de sa première requête ou commande de modification de données (SELECT, INSERT, UPDATE, ou DELETE), il est donc possible d'obtenir les verrous explicitement avant que l'instantané ne soit figé.