SELECT / INSERT Deadlock

10

Cette instance héberge les bases de données SharePoint 2007 (SP). Nous avons rencontré de nombreux blocages SELECT / INSERT sur une table fortement utilisée dans la base de données de contenu SP. J'ai réduit les ressources impliquées, les deux processus nécessitent des verrous sur l'index non cluster.
L'INSERT a besoin d'un verrou IX sur la ressource SELECT et le SELECT a besoin d'un verrou S sur la ressource INSERT. Le graphique de blocage représente et trois ressources, 1.) deux du SELECT (threads parallèles producteur / consommateur), et 2.) l'INSERT.
J'ai joint le graphique de blocage pour votre examen. Étant donné qu'il s'agit de structures de code et de table Microsoft, nous ne pouvons apporter aucune modification.
Cependant, j'ai lu, sur le site MSFT SP, qu'ils recommandent de définir l'option de configuration de niveau d'instance MAXDOP sur 1. Puisque cette instance est partagée entre de nombreuses autres bases de données / applications, ce paramètre ne peut pas être désactivé.


Par conséquent, j'ai décidé d'essayer d'empêcher ces instructions SELECT de se mettre en parallèle. Je sais que ce n'est pas une solution mais plutôt une modification temporaire pour aider au dépannage. Par conséquent, j'ai augmenté le «seuil de coût pour le parallélisme» de notre standard 25 à 40 en procédant ainsi, même si la charge de travail n'a pas changé (SELECT / INSERT se produisant fréquemment), les blocages ont disparu. Ma question est pourquoi?

SPID 356 INSERT a un verrou IX sur une page appartenant à l'index non clusterisé
SPID 690 SELECT ID d'exécution 0 a un verrou S sur une page appartenant au même index non clusterisé

Maintenant

Le SPID 356 souhaite un verrou IX sur la ressource SPID 690 mais ne peut pas le contenir car le SPID 356 est bloqué par l'ID d'exécution SPID 690 0 Verrou S
L'ID d'exécution SPID 690 1 veut un verrou S sur la ressource SPID 356 mais ne peut pas l'obtenir car l'ID d'exécution SPID 690 1 est bloqué par le SPID 356 et nous avons maintenant notre blocage.

Le plan d'exécution se trouve sur mon SkyDrive

Les détails complets de l'impasse peuvent être trouvés ici

Si quelqu'un peut m'aider à comprendre pourquoi je l'apprécierais vraiment.

Table EventReceivers.
Id uniqueidentifier no 16
Nom nvarchar no 512
SiteId uniqueidentifier no 16
WebId uniqueidentifier no 16
HostId uniqueidentifier no 16
HostType int no 4
ItemId int no 4
DirName nvarchar no 512
LeafName nvarchar no 256
Type int no 4
SequenceNumber int no 4
Assembly nvarchar no 512
Class nvarchar non 512
Data nvarchar non 512
Filter nvarchar non 512
SourceId tContentTypeId non 512
SourceType int non 4
Credential int non 4
ContextType varbinary non 16
ContextEventType varbinary no 16
ContextId varbinary no 16
ContextObjectId varbinary no 16
ContextCollectionId varbinary no 16

index_name index_description index_keys
EventReceivers_ByContextCollectionId nonclustered situé sur PRIMAIRES SiteId, ContextCollectionId
EventReceivers_ByContextObjectId NONCLUSTERED situé sur PRIMAIRES SiteId, ContextObjectId
EventReceivers_ById NONCLUSTERED, unique situé sur PRIMAIRES SiteId, Id
EventReceivers_ByTarget en clusters, situé unique sur PRIMAIRES SiteId, webid, HostId, HostType, Type, ContextCollectionId, ContextObjectId, ContextId, ContextType, ContextEventType, SequenceNumber, Assembly, Class
EventReceivers_IdUnique nonclustered, unique, clé unique située sur l'ID PRIMARY

SQLJarHead
la source
2
Que faire proc_InsertEventReceiveret proc_InsertContextEventReceiverque nous ne pouvons pas voir dans le XDL? Aussi pour réduire le parallélisme, pourquoi ne pas avoir un impact direct sur ces instructions (en utilisant MAXDOP 1) au lieu de jouer avec les paramètres à l'échelle du serveur?
Aaron Bertrand
1
Je suis curieux de savoir quel est le paramètre MAXDOP de votre serveur et combien de processeurs (logiques) vous avez. SharePoint fonctionne vraiment mieux et préfère être sur un serveur avec un serveur MAXDOP large de 1 .. Je ne l'aime pas, mais c'est ainsi qu'ils l'ont développé. Pouvez-vous publier les plans d'exécution réels? Tout ce que je vois sur ce lien est le .xdl (graphique de blocage)
Mike Walsh
Bonjour Messieurs, j'apprécie vraiment que vous ayez pris le temps, en dehors de votre emploi du temps chargé, de répondre. Je publierai les procédures et les plans d'exécution pour votre examen sur le site SkyDrive. J'avais pensé à modifier le code pour inclure l'option de requête MAXDOP (1), cependant, cela annulera notre support avec Microsoft. Le serveur physique est un paramètre ProLiant DL580 G4 MAXDOP est 4 avec un total de 8 processeurs physiques et H / T est désactivé.
SQLJarHead
Bonjour messieurs, j'ai créé un package zip avec tous les détails sur le SkyDrive. J'ai modifié le corps du message d'origine pour inclure l'URL du package. S'il vous plaît, ne me dites pas quel est le problème, donnez-moi simplement des conseils et faites-moi travailler pour cela. REMARQUE: je ne suis pas en mesure d'apporter des modifications de code ou des modifications DDL au schéma sous-jacent.
SQLJarHead
1
Donc, vous ne pouvez pas changer le code et vous ne pouvez pas changer le schéma, quelles autres solutions attendez-vous de nous? Si vous craignez d'annuler le support de Microsoft, cela implique que vous avez un support de Microsoft, auquel cas - compte tenu de toutes les restrictions que vous nous avez dites que vous ne pouvez pas faire - avez-vous envisagé d'ouvrir un ticket de support avec Microsoft?
Aaron Bertrand

Réponses:

14

À première vue, cela ressemble à une impasse de recherche classique . Les ingrédients essentiels de ce modèle de blocage sont:

  • une SELECTrequête qui utilise un index non cluster non couvrant avec une recherche de clé
  • une INSERTrequête qui modifie l'index cluster, puis l'index non cluster

Le SELECTaccède d'abord à l'index non cluster, puis à l'index cluster. L' INSERTaccès l'index ordonné en clusters, puis l'index non cluster. Accéder aux mêmes ressources dans un ordre différent en acquérant des verrous incompatibles est un excellent moyen de «réaliser» un blocage, bien sûr.

Dans ce cas, la SELECTrequête est:

SELECT requête

... et la INSERTrequête est:

INSÉRER la requête

Remarquez la maintenance des index non cluster en surbrillance verte.

Nous aurions besoin de voir la version série du SELECTplan au cas où elle serait très différente de la version parallèle, mais comme Jonathan Kehayias le note dans son guide sur la gestion des blocages , ce modèle de blocage particulier est très sensible au timing et aux détails d'implémentation de l'exécution des requêtes internes. Ce type d'interblocage va et vient souvent sans raison extérieure évidente.

Compte tenu de l'accès au système concerné et des autorisations appropriées, je suis certain que nous pourrions éventuellement déterminer exactement pourquoi le blocage se produit avec le plan parallèle mais pas le sériel (en supposant la même forme générale). Lignes potentielles d'enquête comprennent la vérification des boucles imbriquées optimisées et / ou préchargement - qui en interne peuvent tous deux grimper le niveau d'isolement à REPEATABLE READla durée de l'instruction. Il est également possible que certaines caractéristiques de l'attribution de plage de recherche d'index parallèle contribuent au problème. Si le plan de série devient disponible, je pourrais passer un peu de temps à approfondir les détails, car il est potentiellement intéressant.

La solution habituelle pour ce type de blocage est de rendre l'index couvrant, bien que le nombre de colonnes dans ce cas puisse rendre cela impossible (et d'ailleurs, nous ne sommes pas censés gâcher de telles choses sur SharePoint, me dit-on). En fin de compte, la recommandation de plans de série uniquement lors de l'utilisation de SharePoint est là pour une raison (mais pas nécessairement une bonne, en fin de compte). Si le changement de seuil de coût pour le parallélisme résout le problème pour le moment, c'est bien. À plus long terme, je chercherais probablement à séparer les charges de travail, peut-être en utilisant le gouverneur de ressources afin que les requêtes internes SharePoint obtiennent le MAXDOP 1comportement souhaité et que l'autre application puisse utiliser le parallélisme.

La question des échanges apparaissant dans la trace de l'impasse me semble un sujet rouge; simplement une conséquence des threads indépendants possédant des ressources qui doivent techniquement apparaître dans l'arborescence. Je ne vois rien suggérer que les échanges eux-mêmes contribuent directement au problème de l'impasse.

Paul White 9
la source
6

S'il s'agissait d'un blocage de recherche classique , la liste des ressources inclura à la fois l'index cluster et l'index non cluster. Typiquement, le SELECT tiendra un verrou PARTAGÉ sur l'index NC et attendra un verrou PARTAGÉ sur le CI, tandis que l'INSERT acquerra un verrou EXCLUSIF sur le CI et attendra un verrou EXCLUSIF sur le NC. La liste des ressources dans le xml de blocage listera ces deux objets dans ce cas.

Étant donné que le graphique de blocage n'implique que l'index NC, nous pouvons exclure cette option.

De plus, s'il s'agissait d'un verrou mort en raison de la jointure en boucle imbriquée avec UNORDERED PREFETCH , le plan d'exécution nous dira si l'algorithme UNORDERED PREFETCH est utilisé, ce qui n'est pas encore le cas ici (voir la mise à jour ci-dessous).

Cela nous laisse supposer qu'il s'agit d'une impasse due au Plan parallèle.

Le graphique de blocage n'est pas rendu correctement, mais si vous regardez le XML de blocage, vous pouvez voir que deux threads de l'instruction SELECT (SPID 690) sont impliqués dans le blocage. Le thread consommateur détient un verrou PARTAGÉ sur la PAGE 1219645 et attend le producteur sur le port801f8ed0 (e_waitPipeGetRow). Le thread producteur attend un verrou partagé sur la PAGE 1155940.

L'instruction INSERT contient un verrou IX sur la PAGE 1155940 et attend un verrou IX sur la PAGE 1219645, entraînant un blocage.

Je crois qu'un blocage sera évité lors de l'utilisation d'un plan de série pour l'instruction SELECT, car à aucun moment il ne faudra un verrou PARTAGÉ sur plus d'une page. Je pense aussi que le plan série sera presque le même que le plan parallèle (sans l'opérateur de parallélisme).

[MISE À JOUR basée sur le commentaire de Paul]

Apparemment, le plan utilise un algorithme de boucle imbriquée OPTIMISÉ

Cela explique pourquoi les verrous PARTAGÉS sont maintenus jusqu'à la fin de la déclaration. REPEATABLE READ combiné avec un plan parallèle est plus vulnérable aux interblocages qu'un plan série car le plan parallèle peut acquérir et conserver des verrous de différentes plages d'un index alors que le plan série acquiert des verrous de manière plus séquentielle.

Roji P Thomas
la source
D'accord. Si ce blocage était lié à une RECHERCHE réelle, la ressource d'attente, pour SELECT, aurait référencé l'index cluster. J'ai pu exclure cela en affichant l'en-tête de page pour chaque ressource d'attente (SPID 690 Wait Resource = PAGE: 1155940 | SPID 356 Wait Resource = PAGE 1219645) via DBCC PAGE et les deux étaient sur l'ID d'index 5 (IndexID 5 = EventReceivers_ByContextObjectId) qui pointe vers l'index NC sur la table spécifiée (EventReceivers).
SQLJarHead
Messieurs, merci encore d'avoir pris le temps d'aider à analyser cette question intéressante. Quelques questions: 1.) Roji souligne que le SPID parallèle demande plus d'une page. Je ne vois cela dans aucun des plans d'exécution. En regardant le nombre de lignes, pour l'opérateur INDEX SEEK, un seul thread sur les deux producteurs traite des lignes. Comment avez-vous déterminé qu'il demandait plus d'une page? (1/2)
SQLJarHead
2.) Un algorithme de boucle imbriquée OPTIMISÉ définira-t-il toujours le niveau d'isolement sur REAPTABLE READ? J'ai vérifié la sortie XML des plans d'exécution et je ne vois que la lecture validée pour la connexion SPID. Je fais l'hypothèse que cela n'est invoqué qu'au niveau de l'opérateur de plan uniquement. (2/2)
SQLJarHead
Le comportement de verrouillage des boucles imbriquées OPTIMIZED est comparable à REPEATABLE READ (conserve les verrous jusqu'à la fin de l' instruction ), mais il ne définit pas explicitement le niveau d'isolement de la transaction sur REPEATABLE READ. Je pense que cela répond aussi à votre question. Ce n'est pas que les threads parallèles demandent plus d'une page à la fois, mais un thread parallèle tient un verrou sur une page et un autre thread attend un verrou sur une autre page
Roji P Thomas