Dans l’article précédent, nous avons vu ce qu’est une transaction, pourquoi elle est essentielle et comment elle suit le modèle ACID pour garantir l’intégrité des données.
Dans ce volet, nous allons explorer un aspect critique des transactions :
👉 La gestion de la concurrence et les niveaux d’isolation
Lorsqu’un système traite plusieurs transactions simultanément, il doit gérer les conflits et assurer la cohérence des données sans sacrifier les performances.
Concurrence et Problèmes Associés
Dans une base de données avec plusieurs utilisateurs actifs, plusieurs transactions peuvent s’exécuter en même temps. Sans une bonne gestion, cela peut provoquer des incohérences et des effets indésirables.
Problèmes classiques liés à la concurrence
- Dirty Reads (Lecture sale)
Une transaction lit des données modifiées par une autre transaction non validée. Si la deuxième transaction est annulée (rollback), la première se retrouve avec des données erronées. - Non-Repeatable Reads (Lecture non reproductible)
Une transaction lit une donnée une première fois, mais une autre transaction la modifie avant que la première ne soit terminée. Si la première relit la donnée, elle obtient un résultat différent. - Phantom Reads (Lecture fantôme)
Une transaction exécute une requête, mais une autre transaction ajoute ou supprime des données avant la validation. Résultat : la première obtient des résultats incohérents si elle refait la requête.
Solution : Pour gérer ces problèmes, on utilise les niveaux d’isolation.
Les Niveaux d’Isolation des Transactions
Un niveau d’isolation définit comment une transaction voit les modifications des autres transactions en cours. Plus il est strict, moins il y a de conflits, mais plus les performances sont impactées.
Voici les 4 niveaux d’isolation définis par SQL standard :
Read Uncommitted (Lecture non validée) – Niveau le plus bas
✅ Performances élevées
❌ Problèmes : Dirty Reads, Non-Repeatable Reads, Phantom Reads
Dans ce mode, une transaction peut lire les données non validées d’une autre transaction. Cela améliore les performances mais expose aux incohérences.
🔹 Cas d’usage : Rarement utilisé sauf pour des lectures rapides sans enjeu critique.
Read Committed (Lecture validée) – Le plus utilisé
✅ Évite les Dirty Reads
❌ Non-Repeatable Reads et Phantom Reads possibles
Une transaction ne peut lire que les données validées par d’autres transactions. Cela améliore la sécurité sans trop impacter les performances.
- Cas d’usage : Transactions classiques où les lectures doivent être fiables, mais les performances restent prioritaires.
Repeatable Read (Lecture répétable) – Plus strict
✅ Évite Dirty Reads et Non-Repeatable Reads
❌ Phantom Reads possibles
Les modifications d’autres transactions ne sont pas visibles tant que la transaction en cours n’est pas terminée.
- Cas d’usage : Systèmes bancaires où la cohérence des lectures est primordiale.
Serializable – Niveau le plus strict
✅ Évite tous les problèmes (Dirty Reads, Non-Repeatable Reads, Phantom Reads)
❌ Impact fort sur les performances
Les transactions s’exécutent comme si elles étaient séquentielles, sans chevauchement. Cela évite toute incohérence mais ralentit énormément le système.
- Cas d’usage : Opérations critiques nécessitant une cohérence absolue, comme la comptabilité.
Comparatif des Niveaux d’Isolation
Niveau d’isolation | Dirty Reads ❌ | Non-Repeatable Reads ❌ | Phantom Reads ❌ | Performances |
---|---|---|---|---|
Read Uncommitted | ✅ Possible | ✅ Possible | ✅ Possible | 🔥🔥🔥🔥 (Rapide) |
Read Committed | ❌ Évité | ✅ Possible | ✅ Possible | 🔥🔥🔥 (Équilibré) |
Repeatable Read | ❌ Évité | ❌ Évité | ✅ Possible | 🔥🔥 (Stable) |
Serializable | ❌ Évité | ❌ Évité | ❌ Évité | 🔥 (Lent) |
Implémentation en SQL
Voici comment définir un niveau d’isolation dans MySQL et PostgreSQL :
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;START TRANSACTION;-- Opérations ici...COMMIT;
En SQL Server
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;BEGIN TRANSACTION;-- Opérations...COMMIT TRANSACTION;
En Laravel (Eloquent + Transactions) :
DB::transaction(function () { DB::statement('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'); // Opérations ici...});
Bonnes Pratiques pour Gérer la Concurrence
✅ Choisir le bon niveau d’isolation : Plus il est strict, plus l’impact sur les performances est grand. Read Committed est souvent un bon compromis.
✅ Utiliser les verrous uniquement si nécessaire : Les verrous pessimistes (locking) empêchent d’autres transactions d’accéder aux données, mais ralentissent les traitements.
✅ Limiter la durée des transactions : Plus une transaction dure, plus elle bloque d’autres utilisateurs. Faites des transactions courtes et précises.
✅ Gérer les erreurs et les deadlocks : Toujours prévoir une gestion des erreurs et conflits dans le code pour éviter les blocages et les annulations involontaires.
Conclusion
La gestion des transactions ne se limite pas au commit et au rollback. Il faut aussi prendre en compte la concurrence et choisir le bon niveau d’isolation pour éviter les incohérences tout en gardant de bonnes performances.
Ce que nous avons appris :
- Les problèmes classiques de concurrence (Dirty Reads, Phantom Reads...)
- Les 4 niveaux d’isolation et leur impact
- Les bonnes pratiques pour une gestion efficace des transactions