Niveaux d'isolement transactionnel pour les plus petits



Aujourd'hui, je voudrais apporter une section extrêmement intéressante, mais souvent couverte de secrets pour les programmeurs mortels ordinaires de la base de données (DB) - les niveaux d'isolement des transactions. Comme le montre la pratique, de nombreuses personnes associées à l'informatique, en particulier au travail avec des bases de données, ne savent pas très bien pourquoi ces niveaux sont nécessaires et comment ils peuvent être utilisés à leur propre avantage.

Un peu de théorie


Les transactions elles-mêmes ne nécessitent pas d'explications particulières; une transaction est N (N≥1) requêtes vers la base de données qui seront exécutées avec succès ensemble ou pas du tout. L'isolement des transactions montre combien de transactions parallèles s'influencent mutuellement.
En choisissant un niveau de transaction, nous essayons de parvenir à un consensus dans le choix entre une grande cohérence des données entre les transactions et la vitesse de ces mêmes transactions.
Il convient de noter que la vitesse d'exécution la plus élevée et la cohérence la plus faible sont lues sans engagement . La vitesse d'exécution la plus faible et la cohérence la plus élevée sont sérialisables .

Préparation de l'environnement


Par exemple, le SGBD MySQL a été sélectionné. PostgreSQL pourrait également être utilisé, mais il ne prend pas en charge le niveau d'isolement non validé en lecture et utilise le niveau validé en lecture à la place . Et il s'est avéré que différents SGBD perçoivent les niveaux d'isolement différemment. Ils peuvent avoir diverses nuances pour assurer l'isolement, avoir des niveaux supplémentaires ou ne pas être bien connus.

Créez un environnement en utilisant l'image MySQL terminée avec le Docker Hub. Et remplissez la base de données.

docker-compose.yaml
version: '3.4' services: db: image: mysql:8 environment: - MYSQL_ROOT_PASSWORD=12345 command: --init-file /init.sql volumes: - data:/var/lib/mysql - ./init.sql:/init.sql expose: - "3306" ports: - "3309:3306" volumes: data: 


Remplir la base de données
 create database if not exists bank; use bank; create table if not exists accounts ( id int unsigned auto_increment primary key, login varchar(255) not null, balance bigint default 0 not null, created_at timestamp default now() ) collate=utf8mb4_unicode_ci; insert into accounts (login, balance) values ('petya', 1000); insert into accounts (login, balance) values ('vasya', 2000); insert into accounts (login, balance) values ('mark', 500); 


Voyons comment fonctionnent les niveaux et leurs fonctionnalités.
Nous allons exécuter des exemples sur 2 transactions exécutées simultanément. Conditionnellement, une transaction dans la fenêtre de gauche sera appelée transaction 1 (T1), dans la fenêtre de droite - transaction 2 (T2).

Lire sans engagement


Le niveau avec la pire cohérence des données, mais la vitesse de transaction la plus élevée. Le nom du niveau parle de lui-même - chaque transaction voit des changements non validés dans une autre transaction (le phénomène de la lecture sale ). Voyons comment ces transactions s’affectent mutuellement.

Étape 1. Nous commençons 2 transactions parallèles.



Étape 2. Nous examinons les informations dont nous disposons au début.



Étape 3. Maintenant, nous effectuons les opérations INSERT, DELETE, UPDATE dans T1 et voyons ce qu'une autre transaction voit maintenant.



T2 voit les données d'une autre transaction qui n'a pas encore été validée.

Étape 4. Et T2 peut obtenir des données.



Étape 5. Lorsque vous annulez les modifications sur T1, les données reçues par T2 seront erronées.



À ce niveau, il est impossible d'utiliser des données sur la base desquelles des conclusions et des décisions critiques importantes pour l'application sont prises car ces conclusions peuvent être loin de la réalité.
Ce niveau peut être utilisé, par exemple, pour des calculs approximatifs de quelque chose. Le résultat COUNT (*) ou MAX (*) peut être utilisé dans certains rapports non stricts.
Un autre exemple est le mode débogage. Lors d'une transaction, vous souhaitez voir ce qui arrive à la base de données.

Lire engagé


Pour ce niveau, les transactions exécutées simultanément ne voient que les modifications validées des autres transactions. Ainsi, ce niveau offre une protection contre les lectures sales .

Les étapes 1 et 2 sont similaires à l'exemple précédent.

Étape 3. Nous effectuons également 3 opérations simples avec la table des comptes (T1) et faisons une sélection complète de ces tables dans les deux transactions.



Et nous verrons que le phénomène de lecture sale est absent en T2.

Étape 4. Nous corrigeons les changements dans T1 et vérifions ce que T2 voit maintenant.



Maintenant, T2 voit tout ce que T1 a fait. Il s'agit du phénomène dit de lecture non répétitive lorsque nous voyons des lignes mises à jour et supprimées (UPDATE, DELETE) et du phénomène de lecture de fantômes lorsque nous voyons des enregistrements ajoutés (INSERT).

Lecture répétable


Un niveau pour éviter le phénomène de lecture non répétitive . C'est-à-dire nous ne voyons pas les enregistrements modifiés et supprimés dans une autre transaction avec une autre transaction. Mais nous voyons toujours les enregistrements insérés d'une autre transaction. La lecture des fantômes ne va nulle part.

Répétez à nouveau les étapes 1 et 2.

Étape 3. Dans T1, nous exécutons les requêtes INSERT, UPDATE et DELETE. Après, dans T2, nous essayons de mettre à jour la même ligne que celle mise à jour dans T1.



Et nous obtenons le verrouillage: T2 attendra jusqu'Ă  ce que T1 valide les modifications ou annule.

Étape 4. Corrigez les modifications apportées par T1. Et relisez les données du tableau des comptes en T2.



Comme vous pouvez le constater, les phénomènes de lecture non répétitive et de lecture de fantômes ne sont pas observés. Comment se fait-il que, par défaut, la lecture répétable nous permette de ne prévenir que le phénomène de lecture non répétitive ?

En fait, MySQL n'a pas l'effet de lire des fantômes pour le niveau de lecture répétable . Et dans PostgreSQL, ils s'en sont également débarrassés pour ce niveau. Bien que dans la représentation classique de ce niveau, nous devons observer cet effet.

Un petit exemple abstrait est le service de génération de chèques-cadeaux (codes) et leur utilisation. Par exemple, un attaquant a généré un code de certificat pour lui-même et essaie de l'activer, essayant d'envoyer plusieurs demandes de suite pour activer un coupon. Dans ce cas, nous commencerons plusieurs transactions exécutées simultanément en travaillant avec le même coupon. Et dans certaines situations, une double ou même triple activation du coupon peut se produire (l'utilisateur recevra des bonus 2x / 3x). Avec une lecture répétable, dans ce cas, un verrouillage se produira et l'activation aura lieu une fois, et dans les 2 niveaux précédents, une activation multiple est possible. Un problème similaire peut également être résolu en utilisant la requête SELECT FOR UPDATE , qui bloquera également l'enregistrement mis à jour (coupon).

SĂ©rialisable


Le niveau auquel les transactions se comportent comme si rien d'autre n'existe, il n'y a aucune influence les unes sur les autres. Dans la représentation classique, ce niveau élimine l'effet de la lecture des fantômes .

Étape 1. Démarrez la transaction.

Étape 2. T2 nous lisons le tableau des comptes, puis T1 nous essayons de mettre à jour les données lues par T2.



Nous obtenons un verrou: nous ne pouvons pas modifier les données d'une transaction lues dans une autre.

Étape 3. INSERT et DELETE nous conduisent tous les deux au verrou en T1.



Tant que T2 n'aura pas terminé ses travaux, nous ne pourrons pas travailler avec les données qu'il a lues. Nous obtenons une cohérence maximale des données, aucune donnée supplémentaire ne sera enregistrée. Le prix à payer est une vitesse de transaction lente en raison de verrous fréquents, donc avec une architecture d'application médiocre, cela peut vous jouer un tour.

Conclusions


Dans la plupart des applications, le niveau d'isolement change rarement et la valeur par défaut est utilisée (par exemple, dans MySQL, il est reproductible en lecture , dans PostgreSQL, il est lu en lecture ).

Mais périodiquement, il est difficile de trouver un meilleur équilibre entre la cohérence élevée des données ou la vitesse de transaction pour résoudre certains problèmes d'application.

Source: https://habr.com/ru/post/fr469415/


All Articles