Récemment, une de nos applications ASP.NET a affiché une erreur de blocage de la base de données et il m'a été demandé de vérifier et de corriger l'erreur. J'ai réussi à trouver la cause du blocage était une procédure stockée qui mettait à jour de manière rigoureuse une table dans un curseur.
C'est la première fois que je vois cette erreur et que je ne sais pas comment la détecter et la corriger efficacement. J'ai essayé tous les moyens possibles que je connais, et j'ai finalement trouvé que la table en cours de mise à jour n'avait pas de clé primaire! Heureusement, c'était une colonne d'identité.
J'ai par la suite trouvé le développeur qui a scripté la base de données pour le déploiement en désordre. J'ai ajouté une clé primaire et le problème a été résolu.
Je me suis senti heureux et je suis revenu à mon projet et j'ai fait quelques recherches pour découvrir la raison de cette impasse ...
Apparemment, c'était une condition d'attente circulaire qui a provoqué l'impasse. Les mises à jour prennent apparemment plus de temps sans clé primaire qu'avec une clé primaire.
Je sais que ce n'est pas une conclusion bien définie, c'est pourquoi je poste ici ...
- La clé primaire manquante est-elle le problème?
- Existe-t-il d'autres conditions qui entraînent une impasse autres que (exclusion mutuelle, attente et maintien, absence de préemption et attente circulaire)?
- Comment puis-je prévenir et suivre les blocages?
la source
Réponses:
Le suivi des impasses est le plus simple des deux:
La prévention est plus difficile, vous devez essentiellement rechercher les éléments suivants:
Le bloc de code 1 verrouille la ressource A, puis la ressource B, dans cet ordre.
Le bloc de code 2 verrouille la ressource B, puis la ressource A, dans cet ordre.
C'est la condition classique dans laquelle un blocage peut survenir. Si le verrouillage des deux ressources n'est pas atomique, le bloc de code 1 peut verrouiller A et être préempté, puis le bloc de code 2 se verrouille B avant que A ne récupère le temps de traitement. Maintenant, vous avez une impasse.
Pour éviter cette condition, vous pouvez effectuer les opérations suivantes:
Bloc de code A (code psuedo)
Bloc de code B (pseudo code)
sans oublier de déverrouiller A et B quand on en a fini avec eux
cela éviterait le blocage entre le bloc de code A et le bloc de code B
Du point de vue de la base de données, je ne sais pas trop comment éviter cette situation, car les verrous sont gérés par la base de données elle-même, c'est-à-dire que les verrous de ligne / table lors de la mise à jour des données. La plupart des problèmes que j'ai rencontrés sont ceux où vous avez vu le vôtre, dans un curseur. Les curseurs sont notoirement inefficaces, évitez-les si possible.
la source
Mes articles préférés à lire et à apprendre sur les blocages sont les suivants: Conversation simple - Traquez les blocages et SQL Server Central - Utilisation de Profiler pour résoudre les blocages . Ils vous donneront des échantillons et des conseils sur la façon de gérer une situation difficile.
En bref, pour résoudre un problème actuel, j’allais raccourcir les transactions impliquées, en retirer la partie inutile, prendre en charge l’ordre dans lequel les objets sont utilisés, voir quel niveau d’isolement est réellement nécessaire, et non lire inutilement. Les données...
Mais mieux lire les articles, ils seront beaucoup plus agréables dans les conseils.
la source
Il est parfois possible de résoudre un blocage en ajoutant l'indexation, car cela permet à la base de données de verrouiller des enregistrements individuels plutôt que la table entière, ce qui permet de réduire les conflits et les risques de bourrage.
Par exemple, dans InnoDB :
Une autre solution courante consiste à désactiver la cohérence transactionnelle lorsqu'elle n'est pas nécessaire ou à modifier autrement votre niveau d'isolation , par exemple un travail de longue durée pour calculer des statistiques ... une réponse précise suffit généralement, vous n'avez pas besoin de chiffres précis. comme ils changent de sous vous. Et si cela prend 30 minutes, vous ne voulez pas que toutes les autres transactions sur ces tables soient arrêtées.
...
En ce qui concerne leur suivi, cela dépend du logiciel de base de données que vous utilisez.
la source
Juste pour développer le curseur. c'est vraiment très mauvais. Il verrouille toute la table puis traite les lignes une par une.
Il est préférable de parcourir les lignes à la manière d'un curseur à l'aide d'une boucle while
Dans la boucle while, une sélection sera effectuée pour chaque ligne de la boucle et le verrouillage ne se produira que sur une seule ligne à la fois. Le reste des données de la table est libre d'interrogation, ce qui réduit les risques de blocage.
En plus c'est plus rapide. On se demande pourquoi il y a des curseurs de toute façon.
Voici un exemple de ce type de structure:
Si votre champ d'identifiant est clairsemé, vous souhaiterez peut-être extraire une liste d'identifiants distinct et effectuer une itération:
la source
Manquer une clé primaire n'est pas le problème. Au moins tout seul. Premièrement, vous n'avez pas besoin d'un primaire pour avoir des index. Deuxièmement, même si vous effectuez des analyses de table (ce qui doit se produire si votre requête n’utilise pas d’index, un verrou de table ne provoquera pas en soi un blocage. Un processus d’écriture attendrait une lecture et un processus de lecture attendre pour une écriture, et bien sûr, les lectures ne devraient pas attendre du tout.
En ajoutant aux autres réponses, le niveau d'isolation de transaction est important, car les lectures répétables et sérialisées sont la raison pour laquelle les verrous «en lecture» sont conservés jusqu'à la fin de la transaction. Verrouiller une ressource ne provoque pas un blocage. Le garder verrouillé fait. Les opérations d'écriture gardent toujours leurs ressources verrouillées jusqu'à la fin de la transaction.
Ma stratégie de prévention de verrouillage préférée utilise les fonctionnalités de "capture instantanée". La fonctionnalité Lire les instantanés validés signifie que les lectures n'utilisent pas de verrous! Et si vous avez besoin de plus de contrôle que "Lecture validée", il existe la fonction "Niveau d'isolation de capture instantanée". Celui-ci permet à une transaction sérialisée (en utilisant les termes MS ici) de se produire sans bloquer les autres joueurs.
Enfin, une classe de blocages peut être évitée en utilisant un verrou de mise à jour. Si vous lisez et maintenez la lecture (HOLD ou utilisez la lecture répétable) et qu'un autre processus fait de même, alors les deux essaient de mettre à jour les mêmes enregistrements, vous obtiendrez une impasse. Mais si les deux demandent un verrou de mise à jour, le second processus attendra le premier, tout en permettant aux autres processus de lire les données à l'aide de verrous partagés jusqu'à ce que les données soient réellement écrites. Bien entendu, cela ne fonctionnera pas si l’un des processus demande toujours un verrou HOLD partagé.
la source
Bien que les curseurs soient lents dans SQL Server, vous pouvez éviter les blocages de curseur en extrayant les données source du curseur dans une table Temp et en l'exécutant. Cela empêche le curseur de verrouiller la table de données réelle et les seuls verrous que vous obtenez concernent les mises à jour ou les insertions effectuées à l'intérieur du curseur qui ne sont maintenues que pendant la durée de l'insertion / mise à jour et non pour la durée du curseur.
la source