Contenu
Si vous avez déjà vu un système transactionnel se figer sous une charge lourde, vous connaissez le genre de panique silencieuse qui s'installe. Les requêtes s'accumulent, l'utilisation du CPU semble normale, les journaux paraissent normaux, pourtant tout est bloqué. Les interblocages sont des problèmes sournois qui ne se manifestent pas avant de causer de réels dégâts. Ils ne corrompent pas les données mais tuent la disponibilité et ébranlent la confiance dans le système, forçant les équipes à se précipiter pour les anticiper avant que la situation ne dégénère. Un interblocage survient lorsque deux transactions ou plus attendent mutuellement les verrous des autres en boucle. Le système reconnaît ce cycle et termine une transaction pour le briser. Dans les systèmes traitant des tonnes de transactions en ligne, une transaction tuée conduit souvent à des réessais, des délais d'attente et une cascade d'échecs visibles par les utilisateurs. C’est pourquoi prévenir les interblocages n’est pas seulement un plus pour la performance ; c’est crucial pour la résilience du système.\n\nLes experts qui conçoivent des systèmes à haut débit s’accordent à dire que les interblocages ne sont généralement pas aléatoires. Martin Kleppmann souligne que des incohérences subtiles dans l’ordre d’acquisition des verrous causent la plupart des interblocages. Pat Helland soutient que les systèmes qui considèrent l’ordre et l’idempotence comme des préoccupations majeures font rarement face à des interblocages persistants. Tammy Butow ajoute que de nombreuses entreprises sous-estiment à quel point les tempêtes de réessais causées par de mauvaises stratégies de temporisation aggravent les interblocages. Leurs conseils se résument à trois choses : un ordre prévisible des verrous, des réessais limités et une compréhension claire de la manière dont les verrous sont accordés.\n\nAlors pourquoi les interblocages se produisent-ils ? Ce ne sont pas des accidents. Quatre conditions surviennent généralement ensemble : exclusion mutuelle (une seule transaction peut détenir un verrou à la fois), maintien et attente (les transactions gardent des verrous en attendant d’autres), absence de préemption (les verrous ne peuvent pas être retirés de force) et attente circulaire (chaque transaction attend un verrou détenu par une autre dans un cycle). Les bases de données relationnelles ont toutes ces conditions par conception. Les développeurs peuvent principalement contrôler les attentes circulaires. Les causes courantes incluent des ordres de verrou incohérents, des transactions longues, l’utilisation de niveaux d’isolation trop stricts et des schémas d’accès adverses lors des pics de trafic. Par exemple, si une transaction met à jour les lignes 1 puis 2, et une autre fait 2 puis 1, elles peuvent s’interbloquer si elles se chevauchent de manière inappropriée.\n\nLe vrai dommage des interblocages n’est pas la transaction unique tuée ; c’est la tempête de réessais qui suit. Les clients réessaient immédiatement sans délai ni aléa, provoquant des collisions répétées des réessais. Cela peut dégénérer en pannes durant plusieurs minutes. Un cas concernait une API de paiements où un interblocage de 80 ms a causé une panne partielle de cinq minutes à cause de collisions massives de réessais. À grande échelle, même un faible pourcentage d’interblocages peut provoquer un déluge d’échecs saturant les pools de connexions. Il est bien plus facile d’empêcher l’interblocage que de contenir la tempête après coup.\n\nPour prévenir les interblocages, commencez par appliquer un ordre strict et documenté des verrous. Chaque type de transaction doit acquérir les verrous dans le même ordre — par clé primaire, type de ressource ou hiérarchie. Ne laissez pas les entrées utilisateur ou les conditions dynamiques changer cet ordre à la volée, sinon vous invitez les cycles. Si des écritures conditionnelles sont nécessaires, faites d’abord une simple lecture de métadonnées pour choisir le chemin ordonné correct.\n\nGarder les transactions courtes aide aussi. Les transactions longues ne causent pas directement les interblocages mais augmentent la fenêtre de conflits. Supprimez les requêtes inutiles et les opérations coûteuses comme les appels API à l’intérieur des transactions. Traitez l’atomicité comme un scalpel plutôt qu’un gros marteau — ne protégez transactionnellement que ce qui en a besoin.\n\nUtilisez le bon niveau d’isolation plutôt que le plus fort. L’isolation sérialisable peut sembler la plus sûre mais signifie souvent plus de verrous et un risque accru d’interblocages. Choisissez le niveau d’isolation le plus faible qui protège encore la correction, comme l’isolation par instantané pour les lectures ou lecture validée pour les écritures avec vérifications explicites. Un niveau d’isolation plus élevé n’est pas toujours meilleur — juste plus restrictif.\n\nMême avec tout cela, certains interblocages surviendront. Ajoutez donc toujours un backoff exponentiel avec aléa aux boucles de réessai. Ne réessayez pas immédiatement après un échec. Randomisez les délais pour que les réessais ne s’agglutinent pas et ne se percutent pas à nouveau. Même un petit aléa réduit drastiquement les collisions de réessais, vous sauvant souvent d’un simple accroc à un incident majeur.\n\nEnfin, suivez la contention des verrous comme métrique clé. La plupart des équipes surveillent la latence globale des requêtes mais ignorent le temps d’attente sur les verrous. Mesurer le temps de blocage, le nombre d’interblocages et les principaux attendants de verrous aide à détecter les problèmes tôt avant qu’ils ne causent des pannes.