Choisir le bon niveau d'isolation dans PostgreSQL est souvent un équilibre entre la cohérence des données et le débit du système. Bien que READ COMMITTED soit la valeur par défaut et offre de hautes performances, il est susceptible de lectures non reproductibles et de lectures fantômes. À l'autre extrémité du spectre, le niveau SERIALIZABLE offre la garantie la plus forte, mais il s'accompagne traditionnellement d'une pénalité de performance sévère due aux vérifications de validation de la sérialisabilité.
Cependant, PostgreSQL offre un terrain d'entrepôt puissant : l'Isolation de Snapshot Sérialisable (SSPI). Cet article de blog explore comment le SSPI vous permet de maintenir des garanties de sérialisabilité strictes tout en minimisant la surcharge associée aux vérifications sérialisables traditionnelles, ce qui le rend adapté aux environnements à haut débit.
Comprendre l'Isolation de Snapshot Sérialisable
L'isolation sérialisable traditionnelle fonctionne en verrouillant les lignes, ce qui peut entraîner une contention significative dans les scénarios à forte concurrence. Le SSPI, introduit dans PostgreSQL 9.1 et affiné dans les versions ultérieures, utilise un mécanisme différent. Il garantit que si vous exécutez des transactions en parallèle, le résultat est équivalent à un ordre d'exécution séquentielle quelconque, mais il y parvient en détectant les anomalies de sérialisation (telles que les dépendances lecture-écriture ou écriture-écriture) et en annulant les transactions conflictuelles plutôt qu'en les bloquant.
Cette approche est particulièrement bénéfique pour les applications à haut débit car elle évite la surcharge de détection des interblocages et réduit la contention des verrous. Au lieu d'attendre les verrous, les transactions peuvent échouer et nécessiter une nouvelle tentative, ce qui est souvent moins coûteux que de maintenir les verrous ouverts.
Configuration du SSPI dans PostgreSQL
Mettre en œuvre le SSPI est simple. Vous pouvez définir le niveau d'isolation des transactions au niveau de chaque transaction ou globalement. Pour la plupart des applications, le définir au niveau de la session est suffisant, mais le contrôle individuel des transactions offre une granularité plus fine.
Voici comment vous pouvez initier une transaction avec le SSPI :
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Alternativement, vous pouvez le modifier pour toute la session :
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Gestion des échecs de sérialisation
Le défi principal avec le SSPI n'est pas la surcharge de performance des vérifications elles-mêmes, mais la nécessité de gérer les nouvelles tentatives de transaction. Lorsque PostgreSQL détecte un conflit de sérialisation, il annule la transaction et renvoie un code d'erreur SQLSTATE 40001 (généralement serializable_failure). Le code de votre application doit être conçu pour capturer cette exception spécifique et relancer la transaction.
Voici un exemple pratique utilisant Python avec la bibliothèque psycopg2 pour gérer les nouvelles tentatives :
import psycopg2
from psycopg2 import errors
def execute_serializable_transaction(conn):
while True:
try:
with conn.cursor() as cur:
cur.execute("BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE")
# Effectuez les opérations de base de données ici
cur.execute("SELECT balance FROM accounts WHERE id = 1")
row = cur.fetchone()
cur.execute("UPDATE accounts SET balance = %s WHERE id = 1", (row[0] - 10,))
conn.commit()
break
except errors.SerializationFailure:
conn.rollback()
continue
except Exception as e:
conn.rollback()
raise
Ce modèle garantit que votre application gère les conflits de manière transparente. Puisque le SSPI détecte les conflits tardivement dans le cycle de vie de la transaction, le coût est principalement la nouvelle tentative elle-même, et non le verrouillage pendant la transaction.
Compromis de performance et meilleures pratiques
Bien que le SSPI soit plus rapide que l'isolation sérialisable traditionnelle, il est toujours plus lent que READ COMMITTED. Par conséquent, il doit être utilisé avec discernement. Considérez les meilleures pratiques suivantes :
- Minimisez la portée de la transaction : Gardez les transactions aussi courtes que possible pour réduire la fenêtre d'opportunité des conflits.
- Évitez les lectures de longue durée : Les requêtes de lecture longues peuvent empêcher d'autres transactions de progresser, augmentant ainsi la probabilité d'échecs de sérialisation.
- Utilisez le verrouillage optimiste pour les cas simples : Si vous avez seulement besoin d'empêcher les mises à jour perdues, envisagez d'utiliser
SELECT ... FOR UPDATEavec un versionnement au niveau de l'application, ce qui est plus léger que le SSPI complet. - Surveillez les taux de nouvelle tentative : Si votre application connaît un taux élevé d'échecs de sérialisation, vous devrez peut-être optimiser la logique de vos transactions ou reconsidérer votre modèle de données.
Conclusion
L'Isolation de Snapshot Sérialisable offre un moyen robuste d'assurer la cohérence des données dans PostgreSQL sans la pénalité de performance paralysante du verrouillage traditionnel. En comprenant les compromis et en mettant en œuvre une logique de nouvelle tentative appropriée, les développeurs peuvent exploiter le SSPI pour construire des applications hautement concurrentes et cohérentes. Que vous traitiez des transactions financières ou de systèmes d'inventaire complexes, le SSPI offre une voie évolutive vers une forte cohérence dans les environnements à haut débit.